mirror of https://gitee.com/openkylin/qemu.git
Merge remote-tracking branch 'kwolf/for-anthony' into staging
# By Max Reitz (30) and others # Via Kevin Wolf * kwolf/for-anthony: (61 commits) qemu-iotests: Add test for inactive L2 overlap qemu-io: Let "open" pass options to block driver vmdk: Fix vmdk_parse_extents blockdev: blockdev_init() error conversion blockdev: Don't disable COR automatically with blockdev-add blockdev: Remove 'media' parameter from blockdev_init() qemu-iotests: Check autodel behaviour for device_del blockdev: Remove IF_* check for read-only blockdev_init blockdev: Move virtio-blk device creation to drive_init blockdev: Move bus/unit/index processing to drive_init blockdev: Move parsing of 'boot' option to drive_init blockdev: Moving parsing of geometry options to drive_init blockdev: Move parsing of 'if' option to drive_init blockdev: Move parsing of 'media' option to drive_init blockdev: Pass QDict to blockdev_init() blockdev: Separate ID generation from DriveInfo creation blockdev: 'blockdev-add' QMP command blockdev: Introduce DriveInfo.enable_auto_del qapi-types/visit.py: Inheritance for structs qapi-types/visit.py: Pass whole expr dict for structs ... Message-id: 1381503951-27985-1-git-send-email-kwolf@redhat.com Signed-off-by: Anthony Liguori <aliguori@amazon.com>
This commit is contained in:
commit
33c6cae44e
1
Makefile
1
Makefile
|
@ -246,7 +246,6 @@ clean:
|
|||
rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp)
|
||||
rm -rf qapi-generated
|
||||
rm -rf qga/qapi-generated
|
||||
$(MAKE) -C tests/tcg clean
|
||||
for d in $(ALL_SUBDIRS); do \
|
||||
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
||||
rm -f $$d/qemu-options.def; \
|
||||
|
|
53
block.c
53
block.c
|
@ -769,13 +769,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
|||
bs->read_only = !(open_flags & BDRV_O_RDWR);
|
||||
|
||||
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
|
||||
error_setg(errp, "Driver '%s' is not whitelisted", drv->format_name);
|
||||
error_setg(errp,
|
||||
!bs->read_only && bdrv_is_whitelisted(drv, true)
|
||||
? "Driver '%s' can only be used for read-only devices"
|
||||
: "Driver '%s' is not whitelisted",
|
||||
drv->format_name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
|
||||
if (!bs->read_only && (flags & BDRV_O_COPY_ON_READ)) {
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
if (flags & BDRV_O_COPY_ON_READ) {
|
||||
if (!bs->read_only) {
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
} else {
|
||||
error_setg(errp, "Can't use copy-on-read on read-only device");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (filename != NULL) {
|
||||
|
@ -881,7 +890,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
|||
/* Find the right block driver */
|
||||
drvname = qdict_get_try_str(options, "driver");
|
||||
if (drvname) {
|
||||
drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
|
||||
drv = bdrv_find_format(drvname);
|
||||
if (!drv) {
|
||||
error_setg(errp, "Unknown driver '%s'", drvname);
|
||||
}
|
||||
|
@ -1123,7 +1132,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||
/* Find the right image format driver */
|
||||
drvname = qdict_get_try_str(options, "driver");
|
||||
if (drvname) {
|
||||
drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
|
||||
drv = bdrv_find_format(drvname);
|
||||
qdict_del(options, "driver");
|
||||
}
|
||||
|
||||
|
@ -3147,6 +3156,12 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (ret & BDRV_BLOCK_RAW) {
|
||||
assert(ret & BDRV_BLOCK_OFFSET_VALID);
|
||||
return bdrv_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS,
|
||||
*pnum, pnum);
|
||||
}
|
||||
|
||||
if (!(ret & BDRV_BLOCK_DATA)) {
|
||||
if (bdrv_has_zero_init(bs)) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
|
@ -3322,6 +3337,15 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
|||
return drv->bdrv_get_info(bs, bdi);
|
||||
}
|
||||
|
||||
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (drv && drv->bdrv_get_specific_info) {
|
||||
return drv->bdrv_get_specific_info(bs);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
|
||||
int64_t pos, int size)
|
||||
{
|
||||
|
@ -4632,3 +4656,22 @@ int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options)
|
|||
}
|
||||
return bs->drv->bdrv_amend_options(bs, options);
|
||||
}
|
||||
|
||||
ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->drv->bdrv_check_ext_snapshot) {
|
||||
return bs->drv->bdrv_check_ext_snapshot(bs);
|
||||
}
|
||||
|
||||
if (bs->file && bs->file->drv && bs->file->drv->bdrv_check_ext_snapshot) {
|
||||
return bs->file->drv->bdrv_check_ext_snapshot(bs);
|
||||
}
|
||||
|
||||
/* external snapshots are allowed by default */
|
||||
return EXT_SNAPSHOT_ALLOWED;
|
||||
}
|
||||
|
||||
ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs)
|
||||
{
|
||||
return EXT_SNAPSHOT_FORBIDDEN;
|
||||
}
|
||||
|
|
|
@ -202,9 +202,9 @@ static void backup_iostatus_reset(BlockJob *job)
|
|||
bdrv_iostatus_reset(s->target);
|
||||
}
|
||||
|
||||
static const BlockJobType backup_job_type = {
|
||||
static const BlockJobDriver backup_job_driver = {
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = "backup",
|
||||
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||
.set_speed = backup_set_speed,
|
||||
.iostatus_reset = backup_iostatus_reset,
|
||||
};
|
||||
|
@ -370,7 +370,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
|||
return;
|
||||
}
|
||||
|
||||
BackupBlockJob *job = block_job_create(&backup_job_type, bs, speed,
|
||||
BackupBlockJob *job = block_job_create(&backup_job_driver, bs, speed,
|
||||
cb, opaque, errp);
|
||||
if (!job) {
|
||||
return;
|
||||
|
|
|
@ -362,8 +362,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
opts = qemu_opts_create_nofail(&runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -373,6 +372,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
if (config) {
|
||||
ret = read_config(s, config);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not read blkdebug config file");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
@ -383,14 +383,14 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
/* Open the backing file */
|
||||
filename = qemu_opt_get(opts, "x-image");
|
||||
if (filename == NULL) {
|
||||
error_setg(errp, "Could not retrieve image file name");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,8 +128,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
opts = qemu_opts_create_nofail(&runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -137,20 +136,21 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
/* Parse the raw image filename */
|
||||
raw = qemu_opt_get(opts, "x-raw");
|
||||
if (raw == NULL) {
|
||||
error_setg(errp, "Could not retrieve raw image filename");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Open the test file */
|
||||
filename = qemu_opt_get(opts, "x-image");
|
||||
if (filename == NULL) {
|
||||
error_setg(errp, "Could not retrieve test image filename");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -158,8 +158,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
s->test_file = bdrv_new("");
|
||||
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
bdrv_unref(s->test_file);
|
||||
s->test_file = NULL;
|
||||
goto fail;
|
||||
|
@ -417,6 +416,8 @@ static BlockDriver bdrv_blkverify = {
|
|||
.bdrv_aio_readv = blkverify_aio_readv,
|
||||
.bdrv_aio_writev = blkverify_aio_writev,
|
||||
.bdrv_aio_flush = blkverify_aio_flush,
|
||||
|
||||
.bdrv_check_ext_snapshot = bdrv_check_ext_snapshot_forbidden,
|
||||
};
|
||||
|
||||
static void bdrv_blkverify_init(void)
|
||||
|
|
|
@ -173,9 +173,9 @@ static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
}
|
||||
|
||||
static const BlockJobType commit_job_type = {
|
||||
static const BlockJobDriver commit_job_driver = {
|
||||
.instance_size = sizeof(CommitBlockJob),
|
||||
.job_type = "commit",
|
||||
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||
.set_speed = commit_set_speed,
|
||||
};
|
||||
|
||||
|
@ -238,7 +238,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
|||
}
|
||||
|
||||
|
||||
s = block_job_create(&commit_job_type, bs, speed, cb, opaque, errp);
|
||||
s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -525,9 +525,9 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
|||
block_job_resume(job);
|
||||
}
|
||||
|
||||
static const BlockJobType mirror_job_type = {
|
||||
static const BlockJobDriver mirror_job_driver = {
|
||||
.instance_size = sizeof(MirrorBlockJob),
|
||||
.job_type = "mirror",
|
||||
.job_type = BLOCK_JOB_TYPE_MIRROR,
|
||||
.set_speed = mirror_set_speed,
|
||||
.iostatus_reset= mirror_iostatus_reset,
|
||||
.complete = mirror_complete,
|
||||
|
@ -563,7 +563,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
|||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(&mirror_job_type, bs, speed, cb, opaque, errp);
|
||||
s = block_job_create(&mirror_job_driver, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
|
124
block/qapi.c
124
block/qapi.c
|
@ -25,6 +25,9 @@
|
|||
#include "block/qapi.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qapi-visit.h"
|
||||
#include "qapi/qmp-output-visitor.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
|
||||
/*
|
||||
* Returns 0 on success, with *p_list either set to describe snapshot
|
||||
|
@ -134,6 +137,9 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
|||
info->dirty_flag = bdi.is_dirty;
|
||||
info->has_dirty_flag = true;
|
||||
}
|
||||
info->format_specific = bdrv_get_specific_info(bs);
|
||||
info->has_format_specific = info->format_specific != NULL;
|
||||
|
||||
backing_filename = bs->backing_file;
|
||||
if (backing_filename[0] != '\0') {
|
||||
info->backing_filename = g_strdup(backing_filename);
|
||||
|
@ -423,6 +429,119 @@ void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
|
|||
}
|
||||
}
|
||||
|
||||
static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
|
||||
QDict *dict);
|
||||
static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
|
||||
QList *list);
|
||||
|
||||
static void dump_qobject(fprintf_function func_fprintf, void *f,
|
||||
int comp_indent, QObject *obj)
|
||||
{
|
||||
switch (qobject_type(obj)) {
|
||||
case QTYPE_QINT: {
|
||||
QInt *value = qobject_to_qint(obj);
|
||||
func_fprintf(f, "%" PRId64, qint_get_int(value));
|
||||
break;
|
||||
}
|
||||
case QTYPE_QSTRING: {
|
||||
QString *value = qobject_to_qstring(obj);
|
||||
func_fprintf(f, "%s", qstring_get_str(value));
|
||||
break;
|
||||
}
|
||||
case QTYPE_QDICT: {
|
||||
QDict *value = qobject_to_qdict(obj);
|
||||
dump_qdict(func_fprintf, f, comp_indent, value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QLIST: {
|
||||
QList *value = qobject_to_qlist(obj);
|
||||
dump_qlist(func_fprintf, f, comp_indent, value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QFLOAT: {
|
||||
QFloat *value = qobject_to_qfloat(obj);
|
||||
func_fprintf(f, "%g", qfloat_get_double(value));
|
||||
break;
|
||||
}
|
||||
case QTYPE_QBOOL: {
|
||||
QBool *value = qobject_to_qbool(obj);
|
||||
func_fprintf(f, "%s", qbool_get_int(value) ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
case QTYPE_QERROR: {
|
||||
QString *value = qerror_human((QError *)obj);
|
||||
func_fprintf(f, "%s", qstring_get_str(value));
|
||||
break;
|
||||
}
|
||||
case QTYPE_NONE:
|
||||
break;
|
||||
case QTYPE_MAX:
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
|
||||
QList *list)
|
||||
{
|
||||
const QListEntry *entry;
|
||||
int i = 0;
|
||||
|
||||
for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
|
||||
qtype_code type = qobject_type(entry->value);
|
||||
bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
|
||||
const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: ";
|
||||
|
||||
func_fprintf(f, format, indentation * 4, "", i);
|
||||
dump_qobject(func_fprintf, f, indentation + 1, entry->value);
|
||||
if (!composite) {
|
||||
func_fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
|
||||
QDict *dict)
|
||||
{
|
||||
const QDictEntry *entry;
|
||||
|
||||
for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
|
||||
qtype_code type = qobject_type(entry->value);
|
||||
bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
|
||||
const char *format = composite ? "%*s%s:\n" : "%*s%s: ";
|
||||
char key[strlen(entry->key) + 1];
|
||||
int i;
|
||||
|
||||
/* replace dashes with spaces in key (variable) names */
|
||||
for (i = 0; entry->key[i]; i++) {
|
||||
key[i] = entry->key[i] == '-' ? ' ' : entry->key[i];
|
||||
}
|
||||
key[i] = 0;
|
||||
|
||||
func_fprintf(f, format, indentation * 4, "", key);
|
||||
dump_qobject(func_fprintf, f, indentation + 1, entry->value);
|
||||
if (!composite) {
|
||||
func_fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
|
||||
ImageInfoSpecific *info_spec)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
QmpOutputVisitor *ov = qmp_output_visitor_new();
|
||||
QObject *obj, *data;
|
||||
|
||||
visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL,
|
||||
&local_err);
|
||||
obj = qmp_output_get_qobject(ov);
|
||||
assert(qobject_type(obj) == QTYPE_QDICT);
|
||||
data = qdict_get(qobject_to_qdict(obj), "data");
|
||||
dump_qobject(func_fprintf, f, 1, data);
|
||||
qmp_output_visitor_cleanup(ov);
|
||||
}
|
||||
|
||||
void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
|
||||
ImageInfo *info)
|
||||
{
|
||||
|
@ -493,4 +612,9 @@ void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
|
|||
func_fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (info->has_format_specific) {
|
||||
func_fprintf(f, "Format specific information:\n");
|
||||
bdrv_image_info_specific_dump(func_fprintf, f, info->format_specific);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,15 +115,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
|||
}
|
||||
|
||||
if (c == s->refcount_block_cache) {
|
||||
ret = qcow2_pre_write_overlap_check(bs,
|
||||
QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_BLOCK,
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
|
||||
c->entries[i].offset, s->cluster_size);
|
||||
} else if (c == s->l2_table_cache) {
|
||||
ret = qcow2_pre_write_overlap_check(bs,
|
||||
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2,
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
|
||||
c->entries[i].offset, s->cluster_size);
|
||||
} else {
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0,
|
||||
c->entries[i].offset, s->cluster_size);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,8 +83,8 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
|||
|
||||
/* the L1 position has not yet been updated, so these clusters must
|
||||
* indeed be completely free */
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
new_l1_table_offset, new_l1_size2);
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset,
|
||||
new_l1_size2);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -160,8 +160,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
|||
buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs,
|
||||
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
|
||||
s->l1_table_offset + 8 * l1_start_index, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
@ -396,7 +395,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
|||
&s->aes_encrypt_key);
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0,
|
||||
cluster_offset + n_start * BDRV_SECTOR_SIZE, n * BDRV_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
|
@ -1604,8 +1603,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
|||
}
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
offset, s->cluster_size);
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
if (!preallocated) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
|
@ -1661,8 +1659,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
|||
}
|
||||
} else {
|
||||
if (l2_dirty) {
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT &
|
||||
~(QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2), l2_offset,
|
||||
ret = qcow2_pre_write_overlap_check(bs,
|
||||
QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, l2_offset,
|
||||
s->cluster_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
|
|
|
@ -796,11 +796,13 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
|||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits, type);
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
if (l2_entry & L2E_OFFSET_MASK) {
|
||||
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
|
||||
nb_clusters << s->cluster_bits, type);
|
||||
}
|
||||
break;
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
case QCOW2_CLUSTER_ZERO:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
|
@ -1309,9 +1311,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
}
|
||||
|
||||
if (l2_dirty) {
|
||||
ret = qcow2_pre_write_overlap_check(bs,
|
||||
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2, l2_offset,
|
||||
s->cluster_size);
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
|
||||
l2_offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR: Could not write L2 table; metadata "
|
||||
"overlap check failed: %s\n", strerror(-ret));
|
||||
|
@ -1352,8 +1353,7 @@ static int write_reftable_entry(BlockDriverState *bs, int rt_index)
|
|||
buf[i] = cpu_to_be64(s->refcount_table[rt_start_index + i]);
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs,
|
||||
QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_TABLE,
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_TABLE,
|
||||
s->refcount_table_offset + rt_start_index * sizeof(uint64_t),
|
||||
sizeof(buf));
|
||||
if (ret < 0) {
|
||||
|
@ -1404,8 +1404,7 @@ static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
|
|||
|
||||
/* new block has not yet been entered into refcount table, therefore it is
|
||||
* no refcount block yet (regarding this check) */
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, new_offset,
|
||||
s->cluster_size);
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, new_offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not write refcount block; metadata overlap "
|
||||
"check failed: %s\n", strerror(-ret));
|
||||
|
@ -1637,8 +1636,8 @@ fail:
|
|||
* looking for overlaps with important metadata sections (L1/L2 tables etc.),
|
||||
* i.e. a sanity check without relying on the refcount tables.
|
||||
*
|
||||
* The chk parameter specifies exactly what checks to perform (being a bitmask
|
||||
* of QCow2MetadataOverlap values).
|
||||
* The ign parameter specifies what checks not to perform (being a bitmask of
|
||||
* QCow2MetadataOverlap values), i.e., what sections to ignore.
|
||||
*
|
||||
* Returns:
|
||||
* - 0 if writing to this offset will not affect the mentioned metadata
|
||||
|
@ -1646,10 +1645,11 @@ fail:
|
|||
* - a negative value (-errno) indicating an error while performing a check,
|
||||
* e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2
|
||||
*/
|
||||
int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
|
||||
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int64_t size)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int chk = s->overlap_check & ~ign;
|
||||
int i, j;
|
||||
|
||||
if (!size) {
|
||||
|
@ -1719,12 +1719,11 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
|
|||
for (i = 0; i < s->nb_snapshots; i++) {
|
||||
uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
|
||||
uint32_t l1_sz = s->snapshots[i].l1_size;
|
||||
uint64_t *l1 = g_malloc(l1_sz * sizeof(uint64_t));
|
||||
uint64_t l1_sz2 = l1_sz * sizeof(uint64_t);
|
||||
uint64_t *l1 = g_malloc(l1_sz2);
|
||||
int ret;
|
||||
|
||||
ret = bdrv_read(bs->file, l1_ofs / BDRV_SECTOR_SIZE, (uint8_t *)l1,
|
||||
l1_sz * sizeof(uint64_t) / BDRV_SECTOR_SIZE);
|
||||
|
||||
ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2);
|
||||
if (ret < 0) {
|
||||
g_free(l1);
|
||||
return ret;
|
||||
|
@ -1766,10 +1765,10 @@ static const char *metadata_ol_names[] = {
|
|||
* Returns 0 if there were neither overlaps nor errors while checking for
|
||||
* overlaps; or a negative value (-errno) on error.
|
||||
*/
|
||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int chk, int64_t offset,
|
||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int64_t size)
|
||||
{
|
||||
int ret = qcow2_check_metadata_overlap(bs, chk, offset, size);
|
||||
int ret = qcow2_check_metadata_overlap(bs, ign, offset, size);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
|
|
@ -182,19 +182,19 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
|||
snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
|
||||
offset = snapshots_offset;
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
ret = offset;
|
||||
goto fail;
|
||||
}
|
||||
ret = bdrv_flush(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* The snapshot list position has not yet been updated, so these clusters
|
||||
* must indeed be completely free */
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, offset,
|
||||
snapshots_size);
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
|
@ -220,6 +220,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
|||
|
||||
id_str_size = strlen(sn->id_str);
|
||||
name_size = strlen(sn->name);
|
||||
assert(id_str_size <= UINT16_MAX && name_size <= UINT16_MAX);
|
||||
h.id_str_size = cpu_to_be16(id_str_size);
|
||||
h.name_size = cpu_to_be16(name_size);
|
||||
offset = align_offset(offset, 8);
|
||||
|
@ -278,6 +279,10 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
|||
return 0;
|
||||
|
||||
fail:
|
||||
if (snapshots_offset > 0) {
|
||||
qcow2_free_clusters(bs, snapshots_offset, snapshots_size,
|
||||
QCOW2_DISCARD_ALWAYS);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -286,7 +291,8 @@ static void find_new_snapshot_id(BlockDriverState *bs,
|
|||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
int i, id, id_max = 0;
|
||||
int i;
|
||||
unsigned long id, id_max = 0;
|
||||
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
sn = s->snapshots + i;
|
||||
|
@ -294,7 +300,7 @@ static void find_new_snapshot_id(BlockDriverState *bs,
|
|||
if (id > id_max)
|
||||
id_max = id;
|
||||
}
|
||||
snprintf(id_str, id_str_size, "%d", id_max + 1);
|
||||
snprintf(id_str, id_str_size, "%lu", id_max + 1);
|
||||
}
|
||||
|
||||
static int find_snapshot_by_id_and_name(BlockDriverState *bs,
|
||||
|
@ -388,8 +394,8 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||
l1_table[i] = cpu_to_be64(s->l1_table[i]);
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
sn->l1_table_offset, s->l1_size * sizeof(uint64_t));
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset,
|
||||
s->l1_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -427,6 +433,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||
if (ret < 0) {
|
||||
g_free(s->snapshots);
|
||||
s->snapshots = old_snapshot_list;
|
||||
s->nb_snapshots--;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -513,9 +520,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs,
|
||||
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
|
||||
s->l1_table_offset, cur_l1_bytes);
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
|
||||
s->l1_table_offset, cur_l1_bytes);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
|
129
block/qcow2.c
129
block/qcow2.c
|
@ -354,10 +354,67 @@ static QemuOptsList qcow2_runtime_opts = {
|
|||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Generate discard requests when other clusters are freed",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Selects which overlap checks to perform from a range of "
|
||||
"templates (none, constant, cached, all)",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_MAIN_HEADER,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Check for unintended writes into the main qcow2 header",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_ACTIVE_L1,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Check for unintended writes into the active L1 table",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_ACTIVE_L2,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Check for unintended writes into an active L2 table",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Check for unintended writes into the refcount table",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Check for unintended writes into a refcount block",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Check for unintended writes into the snapshot table",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_INACTIVE_L1,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Check for unintended writes into an inactive L1 table",
|
||||
},
|
||||
{
|
||||
.name = QCOW2_OPT_OVERLAP_INACTIVE_L2,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Check for unintended writes into an inactive L2 table",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
|
||||
[QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER,
|
||||
[QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1,
|
||||
[QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2,
|
||||
[QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
|
||||
[QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
|
||||
[QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
|
||||
[QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1,
|
||||
[QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
|
||||
};
|
||||
|
||||
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
|
@ -368,6 +425,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
Error *local_err = NULL;
|
||||
uint64_t ext_end;
|
||||
uint64_t l1_vm_state_index;
|
||||
const char *opt_overlap_check;
|
||||
int overlap_check_template = 0;
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
|
@ -631,6 +690,33 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
s->discard_passthrough[QCOW2_DISCARD_OTHER] =
|
||||
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);
|
||||
|
||||
opt_overlap_check = qemu_opt_get(opts, "overlap-check") ?: "cached";
|
||||
if (!strcmp(opt_overlap_check, "none")) {
|
||||
overlap_check_template = 0;
|
||||
} else if (!strcmp(opt_overlap_check, "constant")) {
|
||||
overlap_check_template = QCOW2_OL_CONSTANT;
|
||||
} else if (!strcmp(opt_overlap_check, "cached")) {
|
||||
overlap_check_template = QCOW2_OL_CACHED;
|
||||
} else if (!strcmp(opt_overlap_check, "all")) {
|
||||
overlap_check_template = QCOW2_OL_ALL;
|
||||
} else {
|
||||
error_setg(errp, "Unsupported value '%s' for qcow2 option "
|
||||
"'overlap-check'. Allowed are either of the following: "
|
||||
"none, constant, cached, all", opt_overlap_check);
|
||||
qemu_opts_del(opts);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->overlap_check = 0;
|
||||
for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) {
|
||||
/* overlap-check defines a template bitmask, but every flag may be
|
||||
* overwritten through the associated boolean option */
|
||||
s->overlap_check |=
|
||||
qemu_opt_get_bool(opts, overlap_bool_option_names[i],
|
||||
overlap_check_template & (1 << i)) << i;
|
||||
}
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
if (s->use_lazy_refcounts && s->qcow_version < 3) {
|
||||
|
@ -965,7 +1051,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
|||
cur_nr_sectors * 512);
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0,
|
||||
cluster_offset + index_in_cluster * BDRV_SECTOR_SIZE,
|
||||
cur_nr_sectors * BDRV_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
|
@ -1738,14 +1824,6 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
|||
|
||||
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
||||
/* could not compress: write normal cluster */
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
s->cluster_sectors * BDRV_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
|
@ -1759,8 +1837,7 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
|||
}
|
||||
cluster_offset &= s->cluster_offset_mask;
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
cluster_offset, out_len);
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1810,6 +1887,33 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1);
|
||||
|
||||
*spec_info = (ImageInfoSpecific){
|
||||
.kind = IMAGE_INFO_SPECIFIC_KIND_QCOW2,
|
||||
{
|
||||
.qcow2 = g_new(ImageInfoSpecificQCow2, 1),
|
||||
},
|
||||
};
|
||||
if (s->qcow_version == 2) {
|
||||
*spec_info->qcow2 = (ImageInfoSpecificQCow2){
|
||||
.compat = g_strdup("0.10"),
|
||||
};
|
||||
} else if (s->qcow_version == 3) {
|
||||
*spec_info->qcow2 = (ImageInfoSpecificQCow2){
|
||||
.compat = g_strdup("1.1"),
|
||||
.lazy_refcounts = s->compatible_features &
|
||||
QCOW2_COMPAT_LAZY_REFCOUNTS,
|
||||
.has_lazy_refcounts = true,
|
||||
};
|
||||
}
|
||||
|
||||
return spec_info;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void dump_refcounts(BlockDriverState *bs)
|
||||
{
|
||||
|
@ -1888,7 +1992,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version)
|
|||
* 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"
|
||||
error_report("qcow2_downgrade: Image refcount orders other than 4 are "
|
||||
"currently not supported.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
@ -2130,6 +2234,7 @@ static BlockDriver bdrv_qcow2 = {
|
|||
.bdrv_snapshot_list = qcow2_snapshot_list,
|
||||
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
|
||||
.bdrv_get_info = qcow2_get_info,
|
||||
.bdrv_get_specific_info = qcow2_get_specific_info,
|
||||
|
||||
.bdrv_save_vmstate = qcow2_save_vmstate,
|
||||
.bdrv_load_vmstate = qcow2_load_vmstate,
|
||||
|
|
|
@ -63,6 +63,15 @@
|
|||
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
|
||||
#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot"
|
||||
#define QCOW2_OPT_DISCARD_OTHER "pass-discard-other"
|
||||
#define QCOW2_OPT_OVERLAP "overlap-check"
|
||||
#define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header"
|
||||
#define QCOW2_OPT_OVERLAP_ACTIVE_L1 "overlap-check.active-l1"
|
||||
#define QCOW2_OPT_OVERLAP_ACTIVE_L2 "overlap-check.active-l2"
|
||||
#define QCOW2_OPT_OVERLAP_REFCOUNT_TABLE "overlap-check.refcount-table"
|
||||
#define QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK "overlap-check.refcount-block"
|
||||
#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table"
|
||||
#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1"
|
||||
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
|
||||
|
||||
typedef struct QCowHeader {
|
||||
uint32_t magic;
|
||||
|
@ -203,6 +212,8 @@ typedef struct BDRVQcowState {
|
|||
|
||||
bool discard_passthrough[QCOW2_DISCARD_MAX];
|
||||
|
||||
int overlap_check; /* bitmask of Qcow2MetadataOverlap values */
|
||||
|
||||
uint64_t incompatible_features;
|
||||
uint64_t compatible_features;
|
||||
uint64_t autoclear_features;
|
||||
|
@ -315,14 +326,19 @@ typedef enum QCow2MetadataOverlap {
|
|||
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
|
||||
} QCow2MetadataOverlap;
|
||||
|
||||
/* Perform all overlap checks which can be done in constant time */
|
||||
#define QCOW2_OL_CONSTANT \
|
||||
(QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \
|
||||
QCOW2_OL_SNAPSHOT_TABLE)
|
||||
|
||||
/* Perform all overlap checks which don't require disk access */
|
||||
#define QCOW2_OL_CACHED \
|
||||
(QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_ACTIVE_L2 | \
|
||||
QCOW2_OL_REFCOUNT_TABLE | QCOW2_OL_REFCOUNT_BLOCK | \
|
||||
QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_INACTIVE_L1)
|
||||
(QCOW2_OL_CONSTANT | QCOW2_OL_ACTIVE_L2 | QCOW2_OL_REFCOUNT_BLOCK | \
|
||||
QCOW2_OL_INACTIVE_L1)
|
||||
|
||||
/* The default checks to perform */
|
||||
#define QCOW2_OL_DEFAULT QCOW2_OL_CACHED
|
||||
/* Perform all overlap checks */
|
||||
#define QCOW2_OL_ALL \
|
||||
(QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2)
|
||||
|
||||
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||
#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||
|
@ -433,9 +449,9 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
|
||||
void qcow2_process_discards(BlockDriverState *bs, int ret);
|
||||
|
||||
int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
|
||||
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int64_t size);
|
||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int chk, int64_t offset,
|
||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int64_t size);
|
||||
|
||||
/* qcow2-cluster.c functions */
|
||||
|
|
|
@ -276,7 +276,7 @@ static QemuOptsList raw_runtime_opts = {
|
|||
};
|
||||
|
||||
static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
int bdrv_flags, int open_flags)
|
||||
int bdrv_flags, int open_flags, Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
|
@ -287,8 +287,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||
opts = qemu_opts_create_nofail(&raw_runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -297,6 +296,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||
|
||||
ret = raw_normalize_devicepath(&filename);
|
||||
if (ret != 0) {
|
||||
error_setg_errno(errp, -ret, "Could not normalize device path");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -310,6 +310,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||
if (ret == -EROFS) {
|
||||
ret = -EACCES;
|
||||
}
|
||||
error_setg_errno(errp, -ret, "Could not open file");
|
||||
goto fail;
|
||||
}
|
||||
s->fd = fd;
|
||||
|
@ -318,6 +319,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||
if (raw_set_aio(&s->aio_ctx, &s->use_aio, bdrv_flags)) {
|
||||
qemu_close(fd);
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, -ret, "Could not set AIO state");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
@ -339,9 +341,15 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
s->type = FTYPE_FILE;
|
||||
return raw_open_common(bs, options, flags, 0);
|
||||
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
|
@ -366,6 +374,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
|||
* valid in the 'false' condition even if aio_ctx is set, and raw_set_aio()
|
||||
* won't override aio_ctx if aio_ctx is non-NULL */
|
||||
if (raw_set_aio(&s->aio_ctx, &raw_s->use_aio, state->flags)) {
|
||||
error_setg(errp, "Could not set AIO state");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
@ -417,6 +426,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
|||
assert(!(raw_s->open_flags & O_CREAT));
|
||||
raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags);
|
||||
if (raw_s->fd == -1) {
|
||||
error_setg_errno(errp, errno, "Could not reopen file");
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
@ -1060,12 +1070,15 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
|
|||
0644);
|
||||
if (fd < 0) {
|
||||
result = -errno;
|
||||
error_setg_errno(errp, -result, "Could not create file");
|
||||
} else {
|
||||
if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
|
||||
result = -errno;
|
||||
error_setg_errno(errp, -result, "Could not resize file");
|
||||
}
|
||||
if (qemu_close(fd) != 0) {
|
||||
result = -errno;
|
||||
error_setg_errno(errp, -result, "Could not close the new file");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -1338,6 +1351,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
const char *filename = qdict_get_str(options, "filename");
|
||||
|
||||
|
@ -1381,8 +1395,11 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
}
|
||||
#endif
|
||||
|
||||
ret = raw_open_common(bs, options, flags, 0);
|
||||
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
||||
if (ret < 0) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1390,6 +1407,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
ret = check_hdev_writable(s);
|
||||
if (ret < 0) {
|
||||
raw_close(bs);
|
||||
error_setg_errno(errp, -ret, "The device is not writable");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -1525,15 +1543,23 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options,
|
|||
}
|
||||
|
||||
fd = qemu_open(filename, O_WRONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (fstat(fd, &stat_buf) < 0)
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode))
|
||||
error_setg_errno(errp, -ret, "Could not open device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fstat(fd, &stat_buf) < 0) {
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, -ret, "Could not stat device");
|
||||
} else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) {
|
||||
error_setg(errp,
|
||||
"The given file is neither a block nor a character device");
|
||||
ret = -ENODEV;
|
||||
else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE)
|
||||
} else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE) {
|
||||
error_setg(errp, "Device is too small");
|
||||
ret = -ENOSPC;
|
||||
}
|
||||
|
||||
qemu_close(fd);
|
||||
return ret;
|
||||
|
@ -1575,14 +1601,19 @@ static int floppy_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
s->type = FTYPE_FD;
|
||||
|
||||
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
|
||||
ret = raw_open_common(bs, options, flags, O_NONBLOCK);
|
||||
if (ret)
|
||||
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
||||
if (ret) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* close fd so that we can reopen it as needed */
|
||||
qemu_close(s->fd);
|
||||
|
@ -1698,11 +1729,17 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
s->type = FTYPE_CD;
|
||||
|
||||
/* open will not fail even if no CD is inserted, so add O_NONBLOCK */
|
||||
return raw_open_common(bs, options, flags, O_NONBLOCK);
|
||||
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdrom_probe_device(const char *filename)
|
||||
|
@ -1806,13 +1843,18 @@ static BlockDriver bdrv_host_cdrom = {
|
|||
static int cdrom_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
s->type = FTYPE_CD;
|
||||
|
||||
ret = raw_open_common(bs, options, flags, 0);
|
||||
if (ret)
|
||||
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
||||
if (ret) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* make sure the door isn't locked at this time */
|
||||
ioctl(s->fd, CDIOCALLOW);
|
||||
|
|
|
@ -251,8 +251,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
opts = qemu_opts_create_nofail(&raw_runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -264,6 +263,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
if ((flags & BDRV_O_NATIVE_AIO) && aio == NULL) {
|
||||
aio = win32_aio_init();
|
||||
if (aio == NULL) {
|
||||
error_setg(errp, "Could not initialize AIO");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -280,6 +280,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
error_setg_errno(errp, -ret, "Could not open file");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -287,6 +288,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
ret = win32_aio_attach(aio, s->hfile);
|
||||
if (ret < 0) {
|
||||
CloseHandle(s->hfile);
|
||||
error_setg_errno(errp, -ret, "Could not enable AIO");
|
||||
goto fail;
|
||||
}
|
||||
s->aio = aio;
|
||||
|
@ -438,8 +440,10 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
|
|||
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (fd < 0)
|
||||
if (fd < 0) {
|
||||
error_setg_errno(errp, errno, "Could not create file");
|
||||
return -EIO;
|
||||
}
|
||||
set_sparse(fd);
|
||||
ftruncate(fd, total_size * 512);
|
||||
qemu_close(fd);
|
||||
|
@ -550,8 +554,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
QemuOpts *opts = qemu_opts_create_nofail(&raw_runtime_opts);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
@ -560,6 +563,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
|
||||
if (strstart(filename, "/dev/cdrom", NULL)) {
|
||||
if (find_cdrom(device_name, sizeof(device_name)) < 0) {
|
||||
error_setg(errp, "Could not open CD-ROM drive");
|
||||
ret = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
@ -586,8 +590,10 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
int err = GetLastError();
|
||||
|
||||
if (err == ERROR_ACCESS_DENIED) {
|
||||
error_setg_errno(errp, EACCES, "Could not open device");
|
||||
ret = -EACCES;
|
||||
} else {
|
||||
error_setg(errp, "Could not open device");
|
||||
ret = -1;
|
||||
}
|
||||
goto done;
|
||||
|
|
|
@ -62,7 +62,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
|||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
return bdrv_get_block_status(bs->file, sector_num, nb_sectors, pnum);
|
||||
*pnum = nb_sectors;
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
|
||||
(sector_num << BDRV_SECTOR_BITS);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs,
|
||||
|
@ -138,8 +140,7 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
|
|||
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -203,9 +203,9 @@ static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
}
|
||||
|
||||
static const BlockJobType stream_job_type = {
|
||||
static const BlockJobDriver stream_job_driver = {
|
||||
.instance_size = sizeof(StreamBlockJob),
|
||||
.job_type = "stream",
|
||||
.job_type = BLOCK_JOB_TYPE_STREAM,
|
||||
.set_speed = stream_set_speed,
|
||||
};
|
||||
|
||||
|
@ -224,7 +224,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
|||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp);
|
||||
s = block_job_create(&stream_job_driver, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
|
127
block/vmdk.c
127
block/vmdk.c
|
@ -331,8 +331,7 @@ static int vmdk_reopen_prepare(BDRVReopenState *state,
|
|||
assert(state->bs != NULL);
|
||||
|
||||
if (queue == NULL) {
|
||||
error_set(errp, ERROR_CLASS_GENERIC_ERROR,
|
||||
"No reopen queue for VMDK extents");
|
||||
error_setg(errp, "No reopen queue for VMDK extents");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
@ -391,22 +390,23 @@ static int vmdk_add_extent(BlockDriverState *bs,
|
|||
int64_t l1_offset, int64_t l1_backup_offset,
|
||||
uint32_t l1_size,
|
||||
int l2_size, uint64_t cluster_sectors,
|
||||
VmdkExtent **new_extent)
|
||||
VmdkExtent **new_extent,
|
||||
Error **errp)
|
||||
{
|
||||
VmdkExtent *extent;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
|
||||
if (cluster_sectors > 0x200000) {
|
||||
/* 0x200000 * 512Bytes = 1GB for one cluster is unrealistic */
|
||||
error_report("invalid granularity, image may be corrupt");
|
||||
return -EINVAL;
|
||||
error_setg(errp, "Invalid granularity, image may be corrupt");
|
||||
return -EFBIG;
|
||||
}
|
||||
if (l1_size > 512 * 1024 * 1024) {
|
||||
/* Although with big capacity and small l1_entry_sectors, we can get a
|
||||
* big l1_size, we don't want unbounded value to allocate the table.
|
||||
* Limit it to 512M, which is 16PB for default cluster and L2 table
|
||||
* size */
|
||||
error_report("L1 size too big");
|
||||
error_setg(errp, "L1 size too big");
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
|
@ -438,7 +438,8 @@ static int vmdk_add_extent(BlockDriverState *bs,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
|
||||
static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
int l1_size, i;
|
||||
|
@ -447,10 +448,13 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
|
|||
l1_size = extent->l1_size * sizeof(uint32_t);
|
||||
extent->l1_table = g_malloc(l1_size);
|
||||
ret = bdrv_pread(extent->file,
|
||||
extent->l1_table_offset,
|
||||
extent->l1_table,
|
||||
l1_size);
|
||||
extent->l1_table_offset,
|
||||
extent->l1_table,
|
||||
l1_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read l1 table from extent '%s'",
|
||||
extent->file->filename);
|
||||
goto fail_l1;
|
||||
}
|
||||
for (i = 0; i < extent->l1_size; i++) {
|
||||
|
@ -460,10 +464,13 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
|
|||
if (extent->l1_backup_table_offset) {
|
||||
extent->l1_backup_table = g_malloc(l1_size);
|
||||
ret = bdrv_pread(extent->file,
|
||||
extent->l1_backup_table_offset,
|
||||
extent->l1_backup_table,
|
||||
l1_size);
|
||||
extent->l1_backup_table_offset,
|
||||
extent->l1_backup_table,
|
||||
l1_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read l1 backup table from extent '%s'",
|
||||
extent->file->filename);
|
||||
goto fail_l1b;
|
||||
}
|
||||
for (i = 0; i < extent->l1_size; i++) {
|
||||
|
@ -483,7 +490,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
|
|||
|
||||
static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
||||
BlockDriverState *file,
|
||||
int flags)
|
||||
int flags, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
uint32_t magic;
|
||||
|
@ -492,6 +499,9 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
|||
|
||||
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read header from file '%s'",
|
||||
file->filename);
|
||||
return ret;
|
||||
}
|
||||
ret = vmdk_add_extent(bs, file, false,
|
||||
|
@ -501,11 +511,12 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
|||
le32_to_cpu(header.l1dir_size),
|
||||
4096,
|
||||
le32_to_cpu(header.granularity),
|
||||
&extent);
|
||||
&extent,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = vmdk_init_tables(bs, extent);
|
||||
ret = vmdk_init_tables(bs, extent, errp);
|
||||
if (ret) {
|
||||
/* free extent allocated by vmdk_add_extent */
|
||||
vmdk_free_last_extent(bs);
|
||||
|
@ -514,11 +525,11 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
|||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
||||
uint64_t desc_offset);
|
||||
uint64_t desc_offset, Error **errp);
|
||||
|
||||
static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
BlockDriverState *file,
|
||||
int flags)
|
||||
int flags, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
uint32_t magic;
|
||||
|
@ -529,12 +540,14 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
|||
|
||||
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
error_setg_errno(errp, -ret,
|
||||
"Could not read header from file '%s'",
|
||||
file->filename);
|
||||
}
|
||||
if (header.capacity == 0) {
|
||||
uint64_t desc_offset = le64_to_cpu(header.desc_offset);
|
||||
if (desc_offset) {
|
||||
return vmdk_open_desc_file(bs, flags, desc_offset << 9);
|
||||
return vmdk_open_desc_file(bs, flags, desc_offset << 9, errp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -616,7 +629,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
|||
l1_size,
|
||||
le32_to_cpu(header.num_gtes_per_gt),
|
||||
le64_to_cpu(header.granularity),
|
||||
&extent);
|
||||
&extent,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -625,7 +639,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
|||
extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;
|
||||
extent->version = le32_to_cpu(header.version);
|
||||
extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN;
|
||||
ret = vmdk_init_tables(bs, extent);
|
||||
ret = vmdk_init_tables(bs, extent, errp);
|
||||
if (ret) {
|
||||
/* free extent allocated by vmdk_add_extent */
|
||||
vmdk_free_last_extent(bs);
|
||||
|
@ -663,7 +677,7 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
|
|||
/* Open an extent file and append to bs array */
|
||||
static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
BlockDriverState *file,
|
||||
int flags)
|
||||
int flags, Error **errp)
|
||||
{
|
||||
uint32_t magic;
|
||||
|
||||
|
@ -674,10 +688,10 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
|||
magic = be32_to_cpu(magic);
|
||||
switch (magic) {
|
||||
case VMDK3_MAGIC:
|
||||
return vmdk_open_vmfs_sparse(bs, file, flags);
|
||||
return vmdk_open_vmfs_sparse(bs, file, flags, errp);
|
||||
break;
|
||||
case VMDK4_MAGIC:
|
||||
return vmdk_open_vmdk4(bs, file, flags);
|
||||
return vmdk_open_vmdk4(bs, file, flags, errp);
|
||||
break;
|
||||
default:
|
||||
return -EMEDIUMTYPE;
|
||||
|
@ -686,7 +700,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
|||
}
|
||||
|
||||
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
const char *desc_file_path)
|
||||
const char *desc_file_path, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
char access[11];
|
||||
|
@ -697,7 +711,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||
int64_t flat_offset;
|
||||
char extent_path[PATH_MAX];
|
||||
BlockDriverState *extent_file;
|
||||
Error *local_err = NULL;
|
||||
|
||||
while (*p) {
|
||||
/* parse extent line:
|
||||
|
@ -712,9 +725,11 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||
goto next_line;
|
||||
} else if (!strcmp(type, "FLAT")) {
|
||||
if (ret != 5 || flat_offset < 0) {
|
||||
error_setg(errp, "Invalid extent lines: \n%s", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != 4) {
|
||||
error_setg(errp, "Invalid extent lines: \n%s", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -728,10 +743,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||
path_combine(extent_path, sizeof(extent_path),
|
||||
desc_file_path, fname);
|
||||
ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags,
|
||||
&local_err);
|
||||
errp);
|
||||
if (ret) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -741,35 +754,37 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||
VmdkExtent *extent;
|
||||
|
||||
ret = vmdk_add_extent(bs, extent_file, true, sectors,
|
||||
0, 0, 0, 0, 0, &extent);
|
||||
0, 0, 0, 0, 0, &extent, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
extent->flat_start_offset = flat_offset << 9;
|
||||
} else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
|
||||
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags);
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, errp);
|
||||
if (ret) {
|
||||
bdrv_unref(extent_file);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"VMDK: Not supported extent type \"%s\""".\n", type);
|
||||
error_setg(errp, "Unsupported extent type '%s'", type);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
next_line:
|
||||
/* move to next line */
|
||||
while (*p && *p != '\n') {
|
||||
while (*p) {
|
||||
if (*p == '\n') {
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
||||
uint64_t desc_offset)
|
||||
uint64_t desc_offset, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
char *buf = NULL;
|
||||
|
@ -798,13 +813,12 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
|||
strcmp(ct, "vmfsSparse") &&
|
||||
strcmp(ct, "twoGbMaxExtentSparse") &&
|
||||
strcmp(ct, "twoGbMaxExtentFlat")) {
|
||||
fprintf(stderr,
|
||||
"VMDK: Not supported image type \"%s\""".\n", ct);
|
||||
error_setg(errp, "Unsupported image type '%s'", ct);
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
}
|
||||
s->desc_offset = 0;
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->filename);
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
|
||||
exit:
|
||||
g_free(buf);
|
||||
return ret;
|
||||
|
@ -816,10 +830,10 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
int ret;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
|
||||
if (vmdk_open_sparse(bs, bs->file, flags) == 0) {
|
||||
if (vmdk_open_sparse(bs, bs->file, flags, errp) == 0) {
|
||||
s->desc_offset = 0x200;
|
||||
} else {
|
||||
ret = vmdk_open_desc_file(bs, flags, 0);
|
||||
ret = vmdk_open_desc_file(bs, flags, 0, errp);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1286,8 +1300,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
|||
VmdkMetaData m_data;
|
||||
|
||||
if (sector_num > bs->total_sectors) {
|
||||
fprintf(stderr,
|
||||
"(VMDK) Wrong offset: sector_num=0x%" PRIx64
|
||||
error_report("Wrong offset: sector_num=0x%" PRIx64
|
||||
" total_sectors=0x%" PRIx64 "\n",
|
||||
sector_num, bs->total_sectors);
|
||||
return -EIO;
|
||||
|
@ -1307,9 +1320,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
|||
if (extent->compressed) {
|
||||
if (ret == VMDK_OK) {
|
||||
/* Refuse write to allocated cluster for streamOptimized */
|
||||
fprintf(stderr,
|
||||
"VMDK: can't write to allocated cluster"
|
||||
" for streamOptimized\n");
|
||||
error_report("Could not write to allocated cluster"
|
||||
" for streamOptimized");
|
||||
return -EIO;
|
||||
} else {
|
||||
/* allocate */
|
||||
|
@ -1517,12 +1529,12 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||
}
|
||||
|
||||
static int filename_decompose(const char *filename, char *path, char *prefix,
|
||||
char *postfix, size_t buf_len)
|
||||
char *postfix, size_t buf_len, Error **errp)
|
||||
{
|
||||
const char *p, *q;
|
||||
|
||||
if (filename == NULL || !strlen(filename)) {
|
||||
fprintf(stderr, "Vmdk: no filename provided.\n");
|
||||
error_setg(errp, "No filename provided");
|
||||
return VMDK_ERROR;
|
||||
}
|
||||
p = strrchr(filename, '/');
|
||||
|
@ -1595,9 +1607,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||
"ddb.geometry.heads = \"%d\"\n"
|
||||
"ddb.geometry.sectors = \"63\"\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, errp)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Read out options */
|
||||
|
@ -1623,7 +1634,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||
strcmp(adapter_type, "buslogic") &&
|
||||
strcmp(adapter_type, "lsilogic") &&
|
||||
strcmp(adapter_type, "legacyESX")) {
|
||||
fprintf(stderr, "VMDK: Unknown adapter type: '%s'.\n", adapter_type);
|
||||
error_setg(errp, "Unknown adapter type: '%s'", adapter_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (strcmp(adapter_type, "ide") != 0) {
|
||||
|
@ -1639,7 +1650,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||
strcmp(fmt, "twoGbMaxExtentSparse") &&
|
||||
strcmp(fmt, "twoGbMaxExtentFlat") &&
|
||||
strcmp(fmt, "streamOptimized")) {
|
||||
fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt);
|
||||
error_setg(errp, "Unknown subformat: '%s'", fmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
|
||||
|
@ -1653,15 +1664,17 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||
desc_extent_line = "RW %lld SPARSE \"%s\"\n";
|
||||
}
|
||||
if (flat && backing_file) {
|
||||
/* not supporting backing file for flat image */
|
||||
error_setg(errp, "Flat image can't have backing file");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (flat && zeroed_grain) {
|
||||
error_setg(errp, "Flat image can't enable zeroed grain");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (backing_file) {
|
||||
BlockDriverState *bs = bdrv_new("");
|
||||
ret = bdrv_open(bs, backing_file, NULL, 0, NULL, &local_err);
|
||||
ret = bdrv_open(bs, backing_file, NULL, 0, NULL, errp);
|
||||
if (ret != 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
|
677
blockdev.c
677
blockdev.c
|
@ -38,6 +38,8 @@
|
|||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
#include "qapi-visit.h"
|
||||
#include "qapi/qmp-output-visitor.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qmp-commands.h"
|
||||
|
@ -89,6 +91,10 @@ void blockdev_mark_auto_del(BlockDriverState *bs)
|
|||
{
|
||||
DriveInfo *dinfo = drive_get_by_blockdev(bs);
|
||||
|
||||
if (dinfo && !dinfo->enable_auto_del) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bs->job) {
|
||||
block_job_cancel(bs->job);
|
||||
}
|
||||
|
@ -211,7 +217,10 @@ static void bdrv_format_print(void *opaque, const char *name)
|
|||
|
||||
static void drive_uninit(DriveInfo *dinfo)
|
||||
{
|
||||
qemu_opts_del(dinfo->opts);
|
||||
if (dinfo->opts) {
|
||||
qemu_opts_del(dinfo->opts);
|
||||
}
|
||||
|
||||
bdrv_unref(dinfo->bdrv);
|
||||
g_free(dinfo->id);
|
||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||
|
@ -263,7 +272,7 @@ static void bdrv_put_ref_bh_schedule(BlockDriverState *bs)
|
|||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
|
||||
static int parse_block_error_action(const char *buf, bool is_read)
|
||||
static int parse_block_error_action(const char *buf, bool is_read, Error **errp)
|
||||
{
|
||||
if (!strcmp(buf, "ignore")) {
|
||||
return BLOCKDEV_ON_ERROR_IGNORE;
|
||||
|
@ -274,8 +283,8 @@ static int parse_block_error_action(const char *buf, bool is_read)
|
|||
} else if (!strcmp(buf, "report")) {
|
||||
return BLOCKDEV_ON_ERROR_REPORT;
|
||||
} else {
|
||||
error_report("'%s' invalid %s error action",
|
||||
buf, is_read ? "read" : "write");
|
||||
error_setg(errp, "'%s' invalid %s error action",
|
||||
buf, is_read ? "read" : "write");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -296,23 +305,19 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
||||
BlockInterfaceType block_default_type)
|
||||
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
||||
|
||||
/* Takes the ownership of bs_opts */
|
||||
static DriveInfo *blockdev_init(QDict *bs_opts,
|
||||
BlockInterfaceType type,
|
||||
Error **errp)
|
||||
{
|
||||
const char *buf;
|
||||
const char *file = NULL;
|
||||
const char *serial;
|
||||
const char *mediastr = "";
|
||||
BlockInterfaceType type;
|
||||
enum { MEDIA_DISK, MEDIA_CDROM } media;
|
||||
int bus_id, unit_id;
|
||||
int cyls, heads, secs, translation;
|
||||
int max_devs;
|
||||
int index;
|
||||
int ro = 0;
|
||||
int bdrv_flags = 0;
|
||||
int on_read_error, on_write_error;
|
||||
const char *devaddr;
|
||||
DriveInfo *dinfo;
|
||||
ThrottleConfig cfg;
|
||||
int snapshot = 0;
|
||||
|
@ -320,30 +325,22 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
int ret;
|
||||
Error *error = NULL;
|
||||
QemuOpts *opts;
|
||||
QDict *bs_opts;
|
||||
const char *id;
|
||||
bool has_driver_specific_opts;
|
||||
BlockDriver *drv = NULL;
|
||||
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
media = MEDIA_DISK;
|
||||
|
||||
/* Check common options by copying from all_opts to opts, all other options
|
||||
* are stored in bs_opts. */
|
||||
id = qemu_opts_id(all_opts);
|
||||
/* Check common options by copying from bs_opts to opts, all other options
|
||||
* stay in bs_opts for processing by bdrv_open(). */
|
||||
id = qdict_get_try_str(bs_opts, "id");
|
||||
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
|
||||
if (error_is_set(&error)) {
|
||||
qerror_report_err(error);
|
||||
error_free(error);
|
||||
error_propagate(errp, error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bs_opts = qdict_new();
|
||||
qemu_opts_to_qdict(all_opts, bs_opts);
|
||||
qemu_opts_absorb_qdict(opts, bs_opts, &error);
|
||||
if (error_is_set(&error)) {
|
||||
qerror_report_err(error);
|
||||
error_free(error);
|
||||
error_propagate(errp, error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -354,14 +351,6 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
has_driver_specific_opts = !!qdict_size(bs_opts);
|
||||
|
||||
/* extract parameters */
|
||||
bus_id = qemu_opt_get_number(opts, "bus", 0);
|
||||
unit_id = qemu_opt_get_number(opts, "unit", -1);
|
||||
index = qemu_opt_get_number(opts, "index", -1);
|
||||
|
||||
cyls = qemu_opt_get_number(opts, "cyls", 0);
|
||||
heads = qemu_opt_get_number(opts, "heads", 0);
|
||||
secs = qemu_opt_get_number(opts, "secs", 0);
|
||||
|
||||
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
|
||||
ro = qemu_opt_get_bool(opts, "read-only", 0);
|
||||
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
|
||||
|
@ -369,70 +358,9 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
file = qemu_opt_get(opts, "file");
|
||||
serial = qemu_opt_get(opts, "serial");
|
||||
|
||||
if ((buf = qemu_opt_get(opts, "if")) != NULL) {
|
||||
for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
|
||||
;
|
||||
if (type == IF_COUNT) {
|
||||
error_report("unsupported bus type '%s'", buf);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
type = block_default_type;
|
||||
}
|
||||
|
||||
max_devs = if_max_devs[type];
|
||||
|
||||
if (cyls || heads || secs) {
|
||||
if (cyls < 1) {
|
||||
error_report("invalid physical cyls number");
|
||||
return NULL;
|
||||
}
|
||||
if (heads < 1) {
|
||||
error_report("invalid physical heads number");
|
||||
return NULL;
|
||||
}
|
||||
if (secs < 1) {
|
||||
error_report("invalid physical secs number");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
|
||||
if (!cyls) {
|
||||
error_report("'%s' trans must be used with cyls, heads and secs",
|
||||
buf);
|
||||
return NULL;
|
||||
}
|
||||
if (!strcmp(buf, "none"))
|
||||
translation = BIOS_ATA_TRANSLATION_NONE;
|
||||
else if (!strcmp(buf, "lba"))
|
||||
translation = BIOS_ATA_TRANSLATION_LBA;
|
||||
else if (!strcmp(buf, "auto"))
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
else {
|
||||
error_report("'%s' invalid translation type", buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((buf = qemu_opt_get(opts, "media")) != NULL) {
|
||||
if (!strcmp(buf, "disk")) {
|
||||
media = MEDIA_DISK;
|
||||
} else if (!strcmp(buf, "cdrom")) {
|
||||
if (cyls || secs || heads) {
|
||||
error_report("CHS can't be set with media=%s", buf);
|
||||
return NULL;
|
||||
}
|
||||
media = MEDIA_CDROM;
|
||||
} else {
|
||||
error_report("'%s' invalid media", buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
|
||||
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
|
||||
error_report("invalid discard option");
|
||||
error_setg(errp, "invalid discard option");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -454,7 +382,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
} else if (!strcmp(buf, "threads")) {
|
||||
/* this is the default */
|
||||
} else {
|
||||
error_report("invalid aio option");
|
||||
error_setg(errp, "invalid aio option");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -468,13 +396,9 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
drv = bdrv_find_whitelisted_format(buf, ro);
|
||||
drv = bdrv_find_format(buf);
|
||||
if (!drv) {
|
||||
if (!ro && bdrv_find_whitelisted_format(buf, !ro)) {
|
||||
error_report("'%s' can be only used as read-only device.", buf);
|
||||
} else {
|
||||
error_report("'%s' invalid format", buf);
|
||||
}
|
||||
error_setg(errp, "'%s' invalid format", buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -510,26 +434,20 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
|
||||
|
||||
if (!check_throttle_config(&cfg, &error)) {
|
||||
error_report("%s", error_get_pretty(error));
|
||||
error_free(error);
|
||||
error_propagate(errp, error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (qemu_opt_get(opts, "boot") != NULL) {
|
||||
fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
|
||||
"ignored. Future versions will reject this parameter. Please "
|
||||
"update your scripts.\n");
|
||||
}
|
||||
|
||||
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
||||
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
||||
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
|
||||
error_report("werror is not supported by this bus type");
|
||||
error_setg(errp, "werror is not supported by this bus type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
on_write_error = parse_block_error_action(buf, 0);
|
||||
if (on_write_error < 0) {
|
||||
on_write_error = parse_block_error_action(buf, 0, &error);
|
||||
if (error_is_set(&error)) {
|
||||
error_propagate(errp, error);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -541,92 +459,20 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
on_read_error = parse_block_error_action(buf, 1);
|
||||
if (on_read_error < 0) {
|
||||
on_read_error = parse_block_error_action(buf, 1, &error);
|
||||
if (error_is_set(&error)) {
|
||||
error_propagate(errp, error);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
|
||||
if (type != IF_VIRTIO) {
|
||||
error_report("addr is not supported by this bus type");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* compute bus and unit according index */
|
||||
|
||||
if (index != -1) {
|
||||
if (bus_id != 0 || unit_id != -1) {
|
||||
error_report("index cannot be used with bus and unit");
|
||||
return NULL;
|
||||
}
|
||||
bus_id = drive_index_to_bus_id(type, index);
|
||||
unit_id = drive_index_to_unit_id(type, index);
|
||||
}
|
||||
|
||||
/* if user doesn't specify a unit_id,
|
||||
* try to find the first free
|
||||
*/
|
||||
|
||||
if (unit_id == -1) {
|
||||
unit_id = 0;
|
||||
while (drive_get(type, bus_id, unit_id) != NULL) {
|
||||
unit_id++;
|
||||
if (max_devs && unit_id >= max_devs) {
|
||||
unit_id -= max_devs;
|
||||
bus_id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check unit id */
|
||||
|
||||
if (max_devs && unit_id >= max_devs) {
|
||||
error_report("unit %d too big (max is %d)",
|
||||
unit_id, max_devs - 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* catch multiple definitions
|
||||
*/
|
||||
|
||||
if (drive_get(type, bus_id, unit_id) != NULL) {
|
||||
error_report("drive with bus=%d, unit=%d (index=%d) exists",
|
||||
bus_id, unit_id, index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* init */
|
||||
|
||||
dinfo = g_malloc0(sizeof(*dinfo));
|
||||
if ((buf = qemu_opts_id(opts)) != NULL) {
|
||||
dinfo->id = g_strdup(buf);
|
||||
} else {
|
||||
/* no id supplied -> create one */
|
||||
dinfo->id = g_malloc0(32);
|
||||
if (type == IF_IDE || type == IF_SCSI)
|
||||
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
|
||||
if (max_devs)
|
||||
snprintf(dinfo->id, 32, "%s%i%s%i",
|
||||
if_name[type], bus_id, mediastr, unit_id);
|
||||
else
|
||||
snprintf(dinfo->id, 32, "%s%s%i",
|
||||
if_name[type], mediastr, unit_id);
|
||||
}
|
||||
dinfo->id = g_strdup(qemu_opts_id(opts));
|
||||
dinfo->bdrv = bdrv_new(dinfo->id);
|
||||
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
||||
dinfo->bdrv->read_only = ro;
|
||||
dinfo->devaddr = devaddr;
|
||||
dinfo->type = type;
|
||||
dinfo->bus = bus_id;
|
||||
dinfo->unit = unit_id;
|
||||
dinfo->cyls = cyls;
|
||||
dinfo->heads = heads;
|
||||
dinfo->secs = secs;
|
||||
dinfo->trans = translation;
|
||||
dinfo->opts = all_opts;
|
||||
dinfo->refcount = 1;
|
||||
if (serial != NULL) {
|
||||
dinfo->serial = g_strdup(serial);
|
||||
|
@ -641,36 +487,6 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
bdrv_set_io_limits(dinfo->bdrv, &cfg);
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case IF_IDE:
|
||||
case IF_SCSI:
|
||||
case IF_XEN:
|
||||
case IF_NONE:
|
||||
dinfo->media_cd = media == MEDIA_CDROM;
|
||||
break;
|
||||
case IF_SD:
|
||||
case IF_FLOPPY:
|
||||
case IF_PFLASH:
|
||||
case IF_MTD:
|
||||
break;
|
||||
case IF_VIRTIO:
|
||||
{
|
||||
/* add virtio block device */
|
||||
QemuOpts *devopts;
|
||||
devopts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-s390");
|
||||
} else {
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-pci");
|
||||
}
|
||||
qemu_opt_set(devopts, "drive", dinfo->id);
|
||||
if (devaddr)
|
||||
qemu_opt_set(devopts, "addr", devaddr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if (!file || !*file) {
|
||||
if (has_driver_specific_opts) {
|
||||
file = NULL;
|
||||
|
@ -692,29 +508,15 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||
bdrv_flags |= BDRV_O_INCOMING;
|
||||
}
|
||||
|
||||
if (media == MEDIA_CDROM) {
|
||||
/* CDROM is fine for any interface, don't check. */
|
||||
ro = 1;
|
||||
} else if (ro == 1) {
|
||||
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY &&
|
||||
type != IF_NONE && type != IF_PFLASH) {
|
||||
error_report("read-only not supported by this bus type");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
||||
|
||||
if (ro && copy_on_read) {
|
||||
error_report("warning: disabling copy_on_read on read-only drive");
|
||||
}
|
||||
|
||||
QINCREF(bs_opts);
|
||||
ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
|
||||
|
||||
if (ret < 0) {
|
||||
error_report("could not open disk image %s: %s",
|
||||
file ?: dinfo->id, error_get_pretty(error));
|
||||
error_setg(errp, "could not open disk image %s: %s",
|
||||
file ?: dinfo->id, error_get_pretty(error));
|
||||
error_free(error);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -747,9 +549,84 @@ static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to)
|
|||
}
|
||||
}
|
||||
|
||||
QemuOptsList qemu_legacy_drive_opts = {
|
||||
.name = "drive",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_legacy_drive_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "bus",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "bus number",
|
||||
},{
|
||||
.name = "unit",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "unit number (i.e. lun for scsi)",
|
||||
},{
|
||||
.name = "index",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "index number",
|
||||
},{
|
||||
.name = "media",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "media type (disk, cdrom)",
|
||||
},{
|
||||
.name = "if",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
||||
},{
|
||||
.name = "cyls",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of cylinders (ide disk geometry)",
|
||||
},{
|
||||
.name = "heads",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of heads (ide disk geometry)",
|
||||
},{
|
||||
.name = "secs",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of sectors (ide disk geometry)",
|
||||
},{
|
||||
.name = "trans",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "chs translation (auto, lba, none)",
|
||||
},{
|
||||
.name = "boot",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "(deprecated, ignored)",
|
||||
},{
|
||||
.name = "addr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "pci address (virtio only)",
|
||||
},
|
||||
|
||||
/* Options that are passed on, but have special semantics with -drive */
|
||||
{
|
||||
.name = "read-only",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "open drive file as read-only",
|
||||
},{
|
||||
.name = "copy-on-read",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "copy read data from backing file into image file",
|
||||
},
|
||||
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
{
|
||||
const char *value;
|
||||
DriveInfo *dinfo = NULL;
|
||||
QDict *bs_opts;
|
||||
QemuOpts *legacy_opts;
|
||||
DriveMediaType media = MEDIA_DISK;
|
||||
BlockInterfaceType type;
|
||||
int cyls, heads, secs, translation;
|
||||
int max_devs, bus_id, unit_id, index;
|
||||
const char *devaddr;
|
||||
bool read_only, copy_on_read;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Change legacy command line options into QMP ones */
|
||||
qemu_opt_rename(all_opts, "iops", "throttling.iops-total");
|
||||
|
@ -798,7 +675,232 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||
qemu_opt_unset(all_opts, "cache");
|
||||
}
|
||||
|
||||
return blockdev_init(all_opts, block_default_type);
|
||||
/* Get a QDict for processing the options */
|
||||
bs_opts = qdict_new();
|
||||
qemu_opts_to_qdict(all_opts, bs_opts);
|
||||
|
||||
legacy_opts = qemu_opts_create_nofail(&qemu_legacy_drive_opts);
|
||||
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Deprecated option boot=[on|off] */
|
||||
if (qemu_opt_get(legacy_opts, "boot") != NULL) {
|
||||
fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
|
||||
"ignored. Future versions will reject this parameter. Please "
|
||||
"update your scripts.\n");
|
||||
}
|
||||
|
||||
/* Media type */
|
||||
value = qemu_opt_get(legacy_opts, "media");
|
||||
if (value) {
|
||||
if (!strcmp(value, "disk")) {
|
||||
media = MEDIA_DISK;
|
||||
} else if (!strcmp(value, "cdrom")) {
|
||||
media = MEDIA_CDROM;
|
||||
qdict_put(bs_opts, "read-only", qstring_from_str("on"));
|
||||
} else {
|
||||
error_report("'%s' invalid media", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy-on-read is disabled with a warning for read-only devices */
|
||||
read_only = qemu_opt_get_bool(legacy_opts, "read-only", false);
|
||||
copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false);
|
||||
|
||||
if (read_only && copy_on_read) {
|
||||
error_report("warning: disabling copy-on-read on read-only drive");
|
||||
copy_on_read = false;
|
||||
}
|
||||
|
||||
qdict_put(bs_opts, "read-only",
|
||||
qstring_from_str(read_only ? "on" : "off"));
|
||||
qdict_put(bs_opts, "copy-on-read",
|
||||
qstring_from_str(copy_on_read ? "on" :"off"));
|
||||
|
||||
/* Controller type */
|
||||
value = qemu_opt_get(legacy_opts, "if");
|
||||
if (value) {
|
||||
for (type = 0;
|
||||
type < IF_COUNT && strcmp(value, if_name[type]);
|
||||
type++) {
|
||||
}
|
||||
if (type == IF_COUNT) {
|
||||
error_report("unsupported bus type '%s'", value);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
type = block_default_type;
|
||||
}
|
||||
|
||||
/* Geometry */
|
||||
cyls = qemu_opt_get_number(legacy_opts, "cyls", 0);
|
||||
heads = qemu_opt_get_number(legacy_opts, "heads", 0);
|
||||
secs = qemu_opt_get_number(legacy_opts, "secs", 0);
|
||||
|
||||
if (cyls || heads || secs) {
|
||||
if (cyls < 1) {
|
||||
error_report("invalid physical cyls number");
|
||||
goto fail;
|
||||
}
|
||||
if (heads < 1) {
|
||||
error_report("invalid physical heads number");
|
||||
goto fail;
|
||||
}
|
||||
if (secs < 1) {
|
||||
error_report("invalid physical secs number");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
value = qemu_opt_get(legacy_opts, "trans");
|
||||
if (value != NULL) {
|
||||
if (!cyls) {
|
||||
error_report("'%s' trans must be used with cyls, heads and secs",
|
||||
value);
|
||||
goto fail;
|
||||
}
|
||||
if (!strcmp(value, "none")) {
|
||||
translation = BIOS_ATA_TRANSLATION_NONE;
|
||||
} else if (!strcmp(value, "lba")) {
|
||||
translation = BIOS_ATA_TRANSLATION_LBA;
|
||||
} else if (!strcmp(value, "auto")) {
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
} else {
|
||||
error_report("'%s' invalid translation type", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (media == MEDIA_CDROM) {
|
||||
if (cyls || secs || heads) {
|
||||
error_report("CHS can't be set with media=cdrom");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Device address specified by bus/unit or index.
|
||||
* If none was specified, try to find the first free one. */
|
||||
bus_id = qemu_opt_get_number(legacy_opts, "bus", 0);
|
||||
unit_id = qemu_opt_get_number(legacy_opts, "unit", -1);
|
||||
index = qemu_opt_get_number(legacy_opts, "index", -1);
|
||||
|
||||
max_devs = if_max_devs[type];
|
||||
|
||||
if (index != -1) {
|
||||
if (bus_id != 0 || unit_id != -1) {
|
||||
error_report("index cannot be used with bus and unit");
|
||||
goto fail;
|
||||
}
|
||||
bus_id = drive_index_to_bus_id(type, index);
|
||||
unit_id = drive_index_to_unit_id(type, index);
|
||||
}
|
||||
|
||||
if (unit_id == -1) {
|
||||
unit_id = 0;
|
||||
while (drive_get(type, bus_id, unit_id) != NULL) {
|
||||
unit_id++;
|
||||
if (max_devs && unit_id >= max_devs) {
|
||||
unit_id -= max_devs;
|
||||
bus_id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (max_devs && unit_id >= max_devs) {
|
||||
error_report("unit %d too big (max is %d)", unit_id, max_devs - 1);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (drive_get(type, bus_id, unit_id) != NULL) {
|
||||
error_report("drive with bus=%d, unit=%d (index=%d) exists",
|
||||
bus_id, unit_id, index);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* no id supplied -> create one */
|
||||
if (qemu_opts_id(all_opts) == NULL) {
|
||||
char *new_id;
|
||||
const char *mediastr = "";
|
||||
if (type == IF_IDE || type == IF_SCSI) {
|
||||
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
|
||||
}
|
||||
if (max_devs) {
|
||||
new_id = g_strdup_printf("%s%i%s%i", if_name[type], bus_id,
|
||||
mediastr, unit_id);
|
||||
} else {
|
||||
new_id = g_strdup_printf("%s%s%i", if_name[type],
|
||||
mediastr, unit_id);
|
||||
}
|
||||
qdict_put(bs_opts, "id", qstring_from_str(new_id));
|
||||
g_free(new_id);
|
||||
}
|
||||
|
||||
/* Add virtio block device */
|
||||
devaddr = qemu_opt_get(legacy_opts, "addr");
|
||||
if (devaddr && type != IF_VIRTIO) {
|
||||
error_report("addr is not supported by this bus type");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (type == IF_VIRTIO) {
|
||||
QemuOpts *devopts;
|
||||
devopts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-s390");
|
||||
} else {
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-pci");
|
||||
}
|
||||
qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"));
|
||||
if (devaddr) {
|
||||
qemu_opt_set(devopts, "addr", devaddr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Actual block device init: Functionality shared with blockdev-add */
|
||||
dinfo = blockdev_init(bs_opts, type, &local_err);
|
||||
if (dinfo == NULL) {
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
}
|
||||
goto fail;
|
||||
} else {
|
||||
assert(!error_is_set(&local_err));
|
||||
}
|
||||
|
||||
/* Set legacy DriveInfo fields */
|
||||
dinfo->enable_auto_del = true;
|
||||
dinfo->opts = all_opts;
|
||||
|
||||
dinfo->cyls = cyls;
|
||||
dinfo->heads = heads;
|
||||
dinfo->secs = secs;
|
||||
dinfo->trans = translation;
|
||||
|
||||
dinfo->bus = bus_id;
|
||||
dinfo->unit = unit_id;
|
||||
dinfo->devaddr = devaddr;
|
||||
|
||||
switch(type) {
|
||||
case IF_IDE:
|
||||
case IF_SCSI:
|
||||
case IF_XEN:
|
||||
case IF_NONE:
|
||||
dinfo->media_cd = media == MEDIA_CDROM;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fail:
|
||||
qemu_opts_del(legacy_opts);
|
||||
return dinfo;
|
||||
}
|
||||
|
||||
void do_commit(Monitor *mon, const QDict *qdict)
|
||||
|
@ -1131,6 +1233,11 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
|||
}
|
||||
}
|
||||
|
||||
if (bdrv_check_ext_snapshot(state->old_bs) != EXT_SNAPSHOT_ALLOWED) {
|
||||
error_set(errp, QERR_FEATURE_DISABLED, "snapshot");
|
||||
return;
|
||||
}
|
||||
|
||||
flags = state->old_bs->open_flags;
|
||||
|
||||
/* create new image w/backing file */
|
||||
|
@ -2050,6 +2157,54 @@ void qmp_block_job_complete(const char *device, Error **errp)
|
|||
block_job_complete(job, errp);
|
||||
}
|
||||
|
||||
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *ov = qmp_output_visitor_new();
|
||||
QObject *obj;
|
||||
QDict *qdict;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Require an ID in the top level */
|
||||
if (!options->has_id) {
|
||||
error_setg(errp, "Block device needs an ID");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* TODO Sort it out in raw-posix and drive_init: Reject aio=native with
|
||||
* cache.direct=false instead of silently switching to aio=threads, except
|
||||
* if called from drive_init.
|
||||
*
|
||||
* For now, simply forbidding the combination for all drivers will do. */
|
||||
if (options->has_aio && options->aio == BLOCKDEV_AIO_OPTIONS_NATIVE) {
|
||||
bool direct = options->cache->has_direct && options->cache->direct;
|
||||
if (!options->has_cache && !direct) {
|
||||
error_setg(errp, "aio=native requires cache.direct=true");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
visit_type_BlockdevOptions(qmp_output_get_visitor(ov),
|
||||
&options, NULL, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
obj = qmp_output_get_qobject(ov);
|
||||
qdict = qobject_to_qdict(obj);
|
||||
|
||||
qdict_flatten(qdict);
|
||||
|
||||
blockdev_init(qdict, IF_NONE, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
qmp_output_visitor_cleanup(ov);
|
||||
}
|
||||
|
||||
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
|
||||
{
|
||||
BlockJobInfoList **prev = opaque;
|
||||
|
@ -2077,42 +2232,6 @@ QemuOptsList qemu_common_drive_opts = {
|
|||
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "bus",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "bus number",
|
||||
},{
|
||||
.name = "unit",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "unit number (i.e. lun for scsi)",
|
||||
},{
|
||||
.name = "if",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
||||
},{
|
||||
.name = "index",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "index number",
|
||||
},{
|
||||
.name = "cyls",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of cylinders (ide disk geometry)",
|
||||
},{
|
||||
.name = "heads",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of heads (ide disk geometry)",
|
||||
},{
|
||||
.name = "secs",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of sectors (ide disk geometry)",
|
||||
},{
|
||||
.name = "trans",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "chs translation (auto, lba. none)",
|
||||
},{
|
||||
.name = "media",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "media type (disk, cdrom)",
|
||||
},{
|
||||
.name = "snapshot",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enable/disable snapshot mode",
|
||||
|
@ -2156,10 +2275,6 @@ QemuOptsList qemu_common_drive_opts = {
|
|||
.name = "werror",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "write error action",
|
||||
},{
|
||||
.name = "addr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "pci address (virtio only)",
|
||||
},{
|
||||
.name = "read-only",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
|
@ -2220,10 +2335,6 @@ QemuOptsList qemu_common_drive_opts = {
|
|||
.name = "copy-on-read",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "copy read data from backing file into image file",
|
||||
},{
|
||||
.name = "boot",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "(deprecated, ignored)",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
|
|
22
blockjob.c
22
blockjob.c
|
@ -35,7 +35,7 @@
|
|||
#include "qmp-commands.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
|
||||
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
int64_t speed, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
|
@ -48,8 +48,8 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
|
|||
bdrv_ref(bs);
|
||||
bdrv_set_in_use(bs, 1);
|
||||
|
||||
job = g_malloc0(job_type->instance_size);
|
||||
job->job_type = job_type;
|
||||
job = g_malloc0(driver->instance_size);
|
||||
job->driver = driver;
|
||||
job->bs = bs;
|
||||
job->cb = cb;
|
||||
job->opaque = opaque;
|
||||
|
@ -87,11 +87,11 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!job->job_type->set_speed) {
|
||||
if (!job->driver->set_speed) {
|
||||
error_set(errp, QERR_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
job->job_type->set_speed(job, speed, &local_err);
|
||||
job->driver->set_speed(job, speed, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
|
@ -102,12 +102,12 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||
|
||||
void block_job_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
if (job->paused || job->cancelled || !job->job_type->complete) {
|
||||
if (job->paused || job->cancelled || !job->driver->complete) {
|
||||
error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
|
||||
return;
|
||||
}
|
||||
|
||||
job->job_type->complete(job, errp);
|
||||
job->driver->complete(job, errp);
|
||||
}
|
||||
|
||||
void block_job_pause(BlockJob *job)
|
||||
|
@ -143,8 +143,8 @@ bool block_job_is_cancelled(BlockJob *job)
|
|||
void block_job_iostatus_reset(BlockJob *job)
|
||||
{
|
||||
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
|
||||
if (job->job_type->iostatus_reset) {
|
||||
job->job_type->iostatus_reset(job);
|
||||
if (job->driver->iostatus_reset) {
|
||||
job->driver->iostatus_reset(job);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
|
|||
BlockJobInfo *block_job_query(BlockJob *job)
|
||||
{
|
||||
BlockJobInfo *info = g_new0(BlockJobInfo, 1);
|
||||
info->type = g_strdup(job->job_type->job_type);
|
||||
info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]);
|
||||
info->device = g_strdup(bdrv_get_device_name(job->bs));
|
||||
info->len = job->len;
|
||||
info->busy = job->busy;
|
||||
|
@ -236,7 +236,7 @@ QObject *qobject_from_block_job(BlockJob *job)
|
|||
"'len': %" PRId64 ","
|
||||
"'offset': %" PRId64 ","
|
||||
"'speed': %" PRId64 " }",
|
||||
job->job_type->job_type,
|
||||
BlockJobType_lookup[job->driver->job_type],
|
||||
bdrv_get_device_name(job->bs),
|
||||
job->len,
|
||||
job->offset,
|
||||
|
|
|
@ -53,6 +53,23 @@ The use of '*' as a prefix to the name means the member is optional. Optional
|
|||
members should always be added to the end of the dictionary to preserve
|
||||
backwards compatibility.
|
||||
|
||||
|
||||
A complex type definition can specify another complex type as its base.
|
||||
In this case, the fields of the base type are included as top-level fields
|
||||
of the new complex type's dictionary in the QMP wire format. An example
|
||||
definition is:
|
||||
|
||||
{ 'type': 'BlockdevOptionsGenericFormat', 'data': { 'file': 'str' } }
|
||||
{ 'type': 'BlockdevOptionsGenericCOWFormat',
|
||||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { '*backing': 'str' } }
|
||||
|
||||
An example BlockdevOptionsGenericCOWFormat object on the wire could use
|
||||
both fields like this:
|
||||
|
||||
{ "file": "/some/place/my-image",
|
||||
"backing": "/some/place/my-backing-file" }
|
||||
|
||||
=== Enumeration types ===
|
||||
|
||||
An enumeration type is a dictionary containing a single key whose value is a
|
||||
|
|
|
@ -355,3 +355,6 @@ Snapshot table entry:
|
|||
variable: Unique ID string for the snapshot (not null terminated)
|
||||
|
||||
variable: Name of the snapshot (not null terminated)
|
||||
|
||||
variable: Padding to round up the snapshot table entry size to the
|
||||
next multiple of 8.
|
||||
|
|
|
@ -624,6 +624,11 @@ static int m25p80_init(SSISlave *ss)
|
|||
if (dinfo && dinfo->bdrv) {
|
||||
DB_PRINT_L(0, "Binding to IF_MTD drive\n");
|
||||
s->bdrv = dinfo->bdrv;
|
||||
if (bdrv_is_read_only(s->bdrv)) {
|
||||
fprintf(stderr, "Can't use a read-only drive");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FIXME: Move to late init */
|
||||
if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
|
||||
BDRV_SECTOR_SIZE))) {
|
||||
|
|
|
@ -830,6 +830,11 @@ static int blk_connect(struct XenDevice *xendev)
|
|||
/* setup via qemu cmdline -> already setup for us */
|
||||
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
|
||||
blkdev->bs = blkdev->dinfo->bdrv;
|
||||
if (bdrv_is_read_only(blkdev->bs) && !readonly) {
|
||||
xen_be_printf(&blkdev->xendev, 0, "Unexpected read-only drive");
|
||||
blkdev->bs = NULL;
|
||||
return -1;
|
||||
}
|
||||
/* blkdev->bs is not create by us, we get a reference
|
||||
* so we can bdrv_unref() unconditionally */
|
||||
bdrv_ref(blkdev->bs);
|
||||
|
|
|
@ -1198,7 +1198,15 @@ void ahci_reset(AHCIState *s)
|
|||
int i;
|
||||
|
||||
s->control_regs.irqstatus = 0;
|
||||
s->control_regs.ghc = 0;
|
||||
/* AHCI Enable (AE)
|
||||
* The implementation of this bit is dependent upon the value of the
|
||||
* CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and
|
||||
* shall have a reset value of '0'. If CAP.SAM is '1', then AE shall be
|
||||
* read-only and shall have a reset value of '1'.
|
||||
*
|
||||
* We set HOST_CAP_AHCI so we must enable AHCI at reset.
|
||||
*/
|
||||
s->control_regs.ghc = HOST_CTL_AHCI_EN;
|
||||
|
||||
for (i = 0; i < s->ports; i++) {
|
||||
pr = &s->dev[i].port_regs;
|
||||
|
|
|
@ -255,6 +255,10 @@ static int milkymist_memcard_init(SysBusDevice *dev)
|
|||
|
||||
dinfo = drive_get_next(IF_SD);
|
||||
s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
|
||||
if (s->card == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->enabled = dinfo ? bdrv_is_inserted(dinfo->bdrv) : 0;
|
||||
|
||||
memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s,
|
||||
|
|
|
@ -593,6 +593,9 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
|
|||
|
||||
/* Instantiate the storage */
|
||||
s->card = sd_init(bd, false);
|
||||
if (s->card == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -618,6 +621,9 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
|
|||
|
||||
/* Instantiate the storage */
|
||||
s->card = sd_init(bd, false);
|
||||
if (s->card == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0];
|
||||
sd_set_cb(s->card, NULL, s->cdet);
|
||||
|
|
|
@ -491,6 +491,10 @@ static int pl181_init(SysBusDevice *sbd)
|
|||
qdev_init_gpio_out(dev, s->cardstatus, 2);
|
||||
dinfo = drive_get_next(IF_SD);
|
||||
s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
|
||||
if (s->card == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -539,6 +539,9 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
|
|||
|
||||
/* Instantiate the actual storage */
|
||||
s->card = sd_init(bd, false);
|
||||
if (s->card == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
register_savevm(NULL, "pxa2xx_mmci", 0, 0,
|
||||
pxa2xx_mmci_save, pxa2xx_mmci_load, s);
|
||||
|
|
|
@ -494,6 +494,11 @@ SDState *sd_init(BlockDriverState *bs, bool is_spi)
|
|||
{
|
||||
SDState *sd;
|
||||
|
||||
if (bdrv_is_read_only(bs)) {
|
||||
fprintf(stderr, "sd_init: Cannot use read-only drive\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sd = (SDState *) g_malloc0(sizeof(SDState));
|
||||
sd->buf = qemu_blockalign(bs, 512);
|
||||
sd->spi = is_spi;
|
||||
|
|
|
@ -1166,6 +1166,9 @@ static void sdhci_initfn(Object *obj)
|
|||
|
||||
di = drive_get_next(IF_SD);
|
||||
s->card = sd_init(di ? di->bdrv : NULL, false);
|
||||
if (s->card == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
s->eject_cb = qemu_allocate_irqs(sdhci_insert_eject_cb, s, 1)[0];
|
||||
s->ro_cb = qemu_allocate_irqs(sdhci_card_readonly_cb, s, 1)[0];
|
||||
sd_set_cb(s->card, s->ro_cb, s->eject_cb);
|
||||
|
|
|
@ -246,6 +246,9 @@ static int ssi_sd_init(SSISlave *dev)
|
|||
s->mode = SSI_SD_CMD;
|
||||
dinfo = drive_get_next(IF_SD);
|
||||
s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, true);
|
||||
if (s->sd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -84,6 +84,9 @@ typedef struct BlockDevOps {
|
|||
/* BDRV_BLOCK_DATA: data is read from bs->file or another file
|
||||
* BDRV_BLOCK_ZERO: sectors read as zero
|
||||
* BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data
|
||||
* BDRV_BLOCK_RAW: used internally to indicate that the request
|
||||
* was answered by the raw driver and that one
|
||||
* should look in bs->file directly.
|
||||
*
|
||||
* If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 represent the offset in
|
||||
* bs->file where sector data can be read from as raw data.
|
||||
|
@ -105,6 +108,7 @@ typedef struct BlockDevOps {
|
|||
#define BDRV_BLOCK_DATA 1
|
||||
#define BDRV_BLOCK_ZERO 2
|
||||
#define BDRV_BLOCK_OFFSET_VALID 4
|
||||
#define BDRV_BLOCK_RAW 8
|
||||
#define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK
|
||||
|
||||
typedef enum {
|
||||
|
@ -244,6 +248,20 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
|
|||
|
||||
int bdrv_amend_options(BlockDriverState *bs_new, QEMUOptionParameter *options);
|
||||
|
||||
/* external snapshots */
|
||||
|
||||
typedef enum {
|
||||
EXT_SNAPSHOT_ALLOWED,
|
||||
EXT_SNAPSHOT_FORBIDDEN,
|
||||
} ExtSnapshotPerm;
|
||||
|
||||
/* return EXT_SNAPSHOT_ALLOWED if external snapshot is allowed
|
||||
* return EXT_SNAPSHOT_FORBIDDEN if external snapshot is forbidden
|
||||
*/
|
||||
ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs);
|
||||
/* helper used to forbid external snapshots like in blkverify */
|
||||
ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs);
|
||||
|
||||
/* async block I/O */
|
||||
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
|
||||
int sector_num);
|
||||
|
@ -335,6 +353,7 @@ int bdrv_get_flags(BlockDriverState *bs);
|
|||
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
|
||||
void bdrv_round_to_clusters(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int64_t *cluster_sector_num,
|
||||
|
|
|
@ -67,6 +67,12 @@ typedef struct BdrvTrackedRequest {
|
|||
struct BlockDriver {
|
||||
const char *format_name;
|
||||
int instance_size;
|
||||
|
||||
/* if not defined external snapshots are allowed
|
||||
* future block filters will query their children to build the response
|
||||
*/
|
||||
ExtSnapshotPerm (*bdrv_check_ext_snapshot)(BlockDriverState *bs);
|
||||
|
||||
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
|
||||
int (*bdrv_probe_device)(const char *filename);
|
||||
|
||||
|
@ -168,6 +174,7 @@ struct BlockDriver {
|
|||
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
|
||||
const char *snapshot_name);
|
||||
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
|
||||
|
||||
int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
int64_t pos);
|
||||
|
|
|
@ -28,16 +28,16 @@
|
|||
#include "block/block.h"
|
||||
|
||||
/**
|
||||
* BlockJobType:
|
||||
* BlockJobDriver:
|
||||
*
|
||||
* A class type for block job objects.
|
||||
* A class type for block job driver.
|
||||
*/
|
||||
typedef struct BlockJobType {
|
||||
typedef struct BlockJobDriver {
|
||||
/** Derived BlockJob struct size */
|
||||
size_t instance_size;
|
||||
|
||||
/** String describing the operation, part of query-block-jobs QMP API */
|
||||
const char *job_type;
|
||||
BlockJobType job_type;
|
||||
|
||||
/** Optional callback for job types that support setting a speed limit */
|
||||
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
|
||||
|
@ -50,7 +50,7 @@ typedef struct BlockJobType {
|
|||
* manually.
|
||||
*/
|
||||
void (*complete)(BlockJob *job, Error **errp);
|
||||
} BlockJobType;
|
||||
} BlockJobDriver;
|
||||
|
||||
/**
|
||||
* BlockJob:
|
||||
|
@ -59,7 +59,7 @@ typedef struct BlockJobType {
|
|||
*/
|
||||
struct BlockJob {
|
||||
/** The job type, including the job vtable. */
|
||||
const BlockJobType *job_type;
|
||||
const BlockJobDriver *driver;
|
||||
|
||||
/** The block device on which the job is operating. */
|
||||
BlockDriverState *bs;
|
||||
|
@ -128,7 +128,7 @@ struct BlockJob {
|
|||
* This function is not part of the public job interface; it should be
|
||||
* called from a wrapper that is specific to the job type.
|
||||
*/
|
||||
void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
|
||||
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
int64_t speed, BlockDriverCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ BlockStats *bdrv_query_stats(const BlockDriverState *bs);
|
|||
|
||||
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
|
||||
QEMUSnapshotInfo *sn);
|
||||
void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
|
||||
ImageInfoSpecific *info_spec);
|
||||
void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
|
||||
ImageInfo *info);
|
||||
#endif
|
||||
|
|
|
@ -142,6 +142,7 @@ void qemu_opts_loc_restore(QemuOpts *opts);
|
|||
int qemu_opts_set(QemuOptsList *list, const char *id,
|
||||
const char *name, const char *value);
|
||||
const char *qemu_opts_id(QemuOpts *opts);
|
||||
void qemu_opts_set_id(QemuOpts *opts, char *id);
|
||||
void qemu_opts_del(QemuOpts *opts);
|
||||
void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp);
|
||||
int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname);
|
||||
|
|
|
@ -37,6 +37,7 @@ struct DriveInfo {
|
|||
int bus;
|
||||
int unit;
|
||||
int auto_del; /* see blockdev_mark_auto_del() */
|
||||
bool enable_auto_del; /* Only for legacy drive_init() */
|
||||
int media_cd;
|
||||
int cyls, heads, secs, trans;
|
||||
QemuOpts *opts;
|
||||
|
|
288
qapi-schema.json
288
qapi-schema.json
|
@ -209,6 +209,34 @@
|
|||
'date-sec': 'int', 'date-nsec': 'int',
|
||||
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
|
||||
|
||||
##
|
||||
# @ImageInfoSpecificQCow2:
|
||||
#
|
||||
# @compat: compatibility level
|
||||
#
|
||||
# @lazy-refcounts: #optional on or off; only valid for compat >= 1.1
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'ImageInfoSpecificQCow2',
|
||||
'data': {
|
||||
'compat': 'str',
|
||||
'*lazy-refcounts': 'bool'
|
||||
} }
|
||||
|
||||
##
|
||||
# @ImageInfoSpecific:
|
||||
#
|
||||
# A discriminated record of image format specific information structures.
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
|
||||
{ 'union': 'ImageInfoSpecific',
|
||||
'data': {
|
||||
'qcow2': 'ImageInfoSpecificQCow2'
|
||||
} }
|
||||
|
||||
##
|
||||
# @ImageInfo:
|
||||
#
|
||||
|
@ -238,6 +266,9 @@
|
|||
#
|
||||
# @backing-image: #optional info of the backing image (since 1.6)
|
||||
#
|
||||
# @format-specific: #optional structure supplying additional format-specific
|
||||
# information (since 1.7)
|
||||
#
|
||||
# Since: 1.3
|
||||
#
|
||||
##
|
||||
|
@ -248,7 +279,8 @@
|
|||
'*cluster-size': 'int', '*encrypted': 'bool',
|
||||
'*backing-filename': 'str', '*full-backing-filename': 'str',
|
||||
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
|
||||
'*backing-image': 'ImageInfo' } }
|
||||
'*backing-image': 'ImageInfo',
|
||||
'*format-specific': 'ImageInfoSpecific' } }
|
||||
|
||||
##
|
||||
# @ImageCheck:
|
||||
|
@ -1365,6 +1397,24 @@
|
|||
{ 'enum': 'MirrorSyncMode',
|
||||
'data': ['top', 'full', 'none'] }
|
||||
|
||||
##
|
||||
# @BlockJobType:
|
||||
#
|
||||
# Type of a block job.
|
||||
#
|
||||
# @commit: block commit job type, see "block-commit"
|
||||
#
|
||||
# @stream: block stream job type, see "block-stream"
|
||||
#
|
||||
# @mirror: drive mirror job type, see "drive-mirror"
|
||||
#
|
||||
# @backup: drive backup job type, see "drive-backup"
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'enum': 'BlockJobType',
|
||||
'data': ['commit', 'stream', 'mirror', 'backup'] }
|
||||
|
||||
##
|
||||
# @BlockJobInfo:
|
||||
#
|
||||
|
@ -3902,3 +3952,239 @@
|
|||
##
|
||||
{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
|
||||
'returns': ['RxFilterInfo'] }
|
||||
|
||||
|
||||
##
|
||||
# @BlockdevDiscardOptions
|
||||
#
|
||||
# Determines how to handle discard requests.
|
||||
#
|
||||
# @ignore: Ignore the request
|
||||
# @unmap: Forward as an unmap request
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'enum': 'BlockdevDiscardOptions',
|
||||
'data': [ 'ignore', 'unmap' ] }
|
||||
|
||||
##
|
||||
# @BlockdevAioOptions
|
||||
#
|
||||
# Selects the AIO backend to handle I/O requests
|
||||
#
|
||||
# @threads: Use qemu's thread pool
|
||||
# @native: Use native AIO backend (only Linux and Windows)
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'enum': 'BlockdevAioOptions',
|
||||
'data': [ 'threads', 'native' ] }
|
||||
|
||||
##
|
||||
# @BlockdevCacheOptions
|
||||
#
|
||||
# Includes cache-related options for block devices
|
||||
#
|
||||
# @writeback: #optional enables writeback mode for any caches (default: true)
|
||||
# @direct: #optional enables use of O_DIRECT (bypass the host page cache;
|
||||
# default: false)
|
||||
# @no-flush: #optional ignore any flush requests for the device (default:
|
||||
# false)
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevCacheOptions',
|
||||
'data': { '*writeback': 'bool',
|
||||
'*direct': 'bool',
|
||||
'*no-flush': 'bool' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsBase
|
||||
#
|
||||
# Options that are available for all block devices, independent of the block
|
||||
# driver.
|
||||
#
|
||||
# @driver: block driver name
|
||||
# @id: #optional id by which the new block device can be referred to.
|
||||
# This is a required option on the top level of blockdev-add, and
|
||||
# currently not allowed on any other level.
|
||||
# @discard: #optional discard-related options (default: ignore)
|
||||
# @cache: #optional cache-related options
|
||||
# @aio: #optional AIO backend (default: threads)
|
||||
# @rerror: #optional how to handle read errors on the device
|
||||
# (default: report)
|
||||
# @werror: #optional how to handle write errors on the device
|
||||
# (default: enospc)
|
||||
# @read-only: #optional whether the block device should be read-only
|
||||
# (default: false)
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsBase',
|
||||
'data': { 'driver': 'str',
|
||||
'*id': 'str',
|
||||
'*discard': 'BlockdevDiscardOptions',
|
||||
'*cache': 'BlockdevCacheOptions',
|
||||
'*aio': 'BlockdevAioOptions',
|
||||
'*rerror': 'BlockdevOnError',
|
||||
'*werror': 'BlockdevOnError',
|
||||
'*read-only': 'bool' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsFile
|
||||
#
|
||||
# Driver specific block device options for the file backend and similar
|
||||
# protocols.
|
||||
#
|
||||
# @filename: path to the image file
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsFile',
|
||||
'data': { 'filename': 'str' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsVVFAT
|
||||
#
|
||||
# Driver specific block device options for the vvfat protocol.
|
||||
#
|
||||
# @dir: directory to be exported as FAT image
|
||||
# @fat-type: #optional FAT type: 12, 16 or 32
|
||||
# @floppy: #optional whether to export a floppy image (true) or
|
||||
# partitioned hard disk (false; default)
|
||||
# @rw: #optional whether to allow write operations (default: false)
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsVVFAT',
|
||||
'data': { 'dir': 'str', '*fat-type': 'int', '*floppy': 'bool',
|
||||
'*rw': 'bool' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsGenericFormat
|
||||
#
|
||||
# Driver specific block device options for image format that have no option
|
||||
# besides their data source.
|
||||
#
|
||||
# @file: reference to or definition of the data source block device
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsGenericFormat',
|
||||
'data': { 'file': 'BlockdevRef' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsGenericCOWFormat
|
||||
#
|
||||
# Driver specific block device options for image format that have no option
|
||||
# besides their data source and an optional backing file.
|
||||
#
|
||||
# @backing: #optional reference to or definition of the backing file block
|
||||
# device (if missing, taken from the image file content). It is
|
||||
# allowed to pass an empty string here in order to disable the
|
||||
# default backing file.
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsGenericCOWFormat',
|
||||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { '*backing': 'BlockdevRef' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsQcow2
|
||||
#
|
||||
# Driver specific block device options for qcow2.
|
||||
#
|
||||
# @lazy-refcounts: #optional whether to enable the lazy refcounts
|
||||
# feature (default is taken from the image file)
|
||||
#
|
||||
# @pass-discard-request: #optional whether discard requests to the qcow2
|
||||
# device should be forwarded to the data source
|
||||
#
|
||||
# @pass-discard-snapshot: #optional whether discard requests for the data source
|
||||
# should be issued when a snapshot operation (e.g.
|
||||
# deleting a snapshot) frees clusters in the qcow2 file
|
||||
#
|
||||
# @pass-discard-other: #optional whether discard requests for the data source
|
||||
# should be issued on other occasions where a cluster
|
||||
# gets freed
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsQcow2',
|
||||
'base': 'BlockdevOptionsGenericCOWFormat',
|
||||
'data': { '*lazy-refcounts': 'bool',
|
||||
'*pass-discard-request': 'bool',
|
||||
'*pass-discard-snapshot': 'bool',
|
||||
'*pass-discard-other': 'bool' } }
|
||||
|
||||
##
|
||||
# @BlockdevOptions
|
||||
#
|
||||
# Options for creating a block device.
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'union': 'BlockdevOptions',
|
||||
'base': 'BlockdevOptionsBase',
|
||||
'discriminator': 'driver',
|
||||
'data': {
|
||||
'file': 'BlockdevOptionsFile',
|
||||
'http': 'BlockdevOptionsFile',
|
||||
'https': 'BlockdevOptionsFile',
|
||||
'ftp': 'BlockdevOptionsFile',
|
||||
'ftps': 'BlockdevOptionsFile',
|
||||
'tftp': 'BlockdevOptionsFile',
|
||||
# TODO gluster: Wait for structured options
|
||||
# TODO iscsi: Wait for structured options
|
||||
# TODO nbd: Should take InetSocketAddress for 'host'?
|
||||
# TODO rbd: Wait for structured options
|
||||
# TODO sheepdog: Wait for structured options
|
||||
# TODO ssh: Should take InetSocketAddress for 'host'?
|
||||
'vvfat': 'BlockdevOptionsVVFAT',
|
||||
|
||||
# TODO blkdebug: Wait for structured options
|
||||
# TODO blkverify: Wait for structured options
|
||||
|
||||
'bochs': 'BlockdevOptionsGenericFormat',
|
||||
'cloop': 'BlockdevOptionsGenericFormat',
|
||||
'cow': 'BlockdevOptionsGenericCOWFormat',
|
||||
'dmg': 'BlockdevOptionsGenericFormat',
|
||||
'parallels': 'BlockdevOptionsGenericFormat',
|
||||
'qcow': 'BlockdevOptionsGenericCOWFormat',
|
||||
'qcow2': 'BlockdevOptionsQcow2',
|
||||
'qed': 'BlockdevOptionsGenericCOWFormat',
|
||||
'raw': 'BlockdevOptionsGenericFormat',
|
||||
'vdi': 'BlockdevOptionsGenericFormat',
|
||||
'vhdx': 'BlockdevOptionsGenericFormat',
|
||||
'vmdk': 'BlockdevOptionsGenericCOWFormat',
|
||||
'vpc': 'BlockdevOptionsGenericFormat'
|
||||
} }
|
||||
|
||||
##
|
||||
# @BlockdevRef
|
||||
#
|
||||
# Reference to a block device.
|
||||
#
|
||||
# @definition: defines a new block device inline
|
||||
# @reference: references the ID of an existing block device. An
|
||||
# empty string means that no block device should be
|
||||
# referenced.
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'union': 'BlockdevRef',
|
||||
'discriminator': {},
|
||||
'data': { 'definition': 'BlockdevOptions',
|
||||
'reference': 'str' } }
|
||||
|
||||
##
|
||||
# @blockdev-add:
|
||||
#
|
||||
# Creates a new block device.
|
||||
#
|
||||
# @options: block device options for the new device
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "qemu-io.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qapi.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
#define CMD_NOFILE_OK 0x01
|
||||
|
@ -1678,6 +1679,7 @@ static const cmdinfo_t length_cmd = {
|
|||
static int info_f(BlockDriverState *bs, int argc, char **argv)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
ImageInfoSpecific *spec_info;
|
||||
char s1[64], s2[64];
|
||||
int ret;
|
||||
|
||||
|
@ -1699,6 +1701,13 @@ static int info_f(BlockDriverState *bs, int argc, char **argv)
|
|||
printf("cluster size: %s\n", s1);
|
||||
printf("vm state offset: %s\n", s2);
|
||||
|
||||
spec_info = bdrv_get_specific_info(bs);
|
||||
if (spec_info) {
|
||||
printf("Format specific information:\n");
|
||||
bdrv_image_info_specific_dump(fprintf, stdout, spec_info);
|
||||
qapi_free_ImageInfoSpecific(spec_info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
39
qemu-io.c
39
qemu-io.c
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "qemu-io.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "block/block_int.h"
|
||||
#include "trace/control.h"
|
||||
|
||||
|
@ -44,7 +46,7 @@ static const cmdinfo_t close_cmd = {
|
|||
.oneline = "close the current open file",
|
||||
};
|
||||
|
||||
static int openfile(char *name, int flags, int growable)
|
||||
static int openfile(char *name, int flags, int growable, QDict *opts)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
|
@ -54,7 +56,7 @@ static int openfile(char *name, int flags, int growable)
|
|||
}
|
||||
|
||||
if (growable) {
|
||||
if (bdrv_file_open(&qemuio_bs, name, NULL, flags, &local_err)) {
|
||||
if (bdrv_file_open(&qemuio_bs, name, opts, flags, &local_err)) {
|
||||
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
|
@ -63,7 +65,7 @@ static int openfile(char *name, int flags, int growable)
|
|||
} else {
|
||||
qemuio_bs = bdrv_new("hda");
|
||||
|
||||
if (bdrv_open(qemuio_bs, name, NULL, flags, NULL, &local_err) < 0) {
|
||||
if (bdrv_open(qemuio_bs, name, opts, flags, NULL, &local_err) < 0) {
|
||||
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
|
@ -89,7 +91,8 @@ static void open_help(void)
|
|||
" -r, -- open file read-only\n"
|
||||
" -s, -- use snapshot file\n"
|
||||
" -n, -- disable host cache\n"
|
||||
" -g, -- allow file to grow (only applies to protocols)"
|
||||
" -g, -- allow file to grow (only applies to protocols)\n"
|
||||
" -o, -- options to be given to the block driver"
|
||||
"\n");
|
||||
}
|
||||
|
||||
|
@ -102,19 +105,30 @@ static const cmdinfo_t open_cmd = {
|
|||
.argmin = 1,
|
||||
.argmax = -1,
|
||||
.flags = CMD_NOFILE_OK,
|
||||
.args = "[-Crsn] [path]",
|
||||
.args = "[-Crsn] [-o options] [path]",
|
||||
.oneline = "open the file specified by path",
|
||||
.help = open_help,
|
||||
};
|
||||
|
||||
static QemuOptsList empty_opts = {
|
||||
.name = "drive",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(empty_opts.head),
|
||||
.desc = {
|
||||
/* no elements => accept any params */
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static int open_f(BlockDriverState *bs, int argc, char **argv)
|
||||
{
|
||||
int flags = 0;
|
||||
int readonly = 0;
|
||||
int growable = 0;
|
||||
int c;
|
||||
QemuOpts *qopts;
|
||||
QDict *opts = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv, "snrg")) != EOF) {
|
||||
while ((c = getopt(argc, argv, "snrgo:")) != EOF) {
|
||||
switch (c) {
|
||||
case 's':
|
||||
flags |= BDRV_O_SNAPSHOT;
|
||||
|
@ -128,6 +142,15 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
|
|||
case 'g':
|
||||
growable = 1;
|
||||
break;
|
||||
case 'o':
|
||||
qopts = qemu_opts_parse(&empty_opts, optarg, 0);
|
||||
if (qopts == NULL) {
|
||||
printf("could not parse option list -- %s\n", optarg);
|
||||
return 0;
|
||||
}
|
||||
opts = qemu_opts_to_qdict(qopts, opts);
|
||||
qemu_opts_del(qopts);
|
||||
break;
|
||||
default:
|
||||
return qemuio_command_usage(&open_cmd);
|
||||
}
|
||||
|
@ -141,7 +164,7 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
|
|||
return qemuio_command_usage(&open_cmd);
|
||||
}
|
||||
|
||||
return openfile(argv[optind], flags, growable);
|
||||
return openfile(argv[optind], flags, growable, opts);
|
||||
}
|
||||
|
||||
static int quit_f(BlockDriverState *bs, int argc, char **argv)
|
||||
|
@ -418,7 +441,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
if ((argc - optind) == 1) {
|
||||
openfile(argv[optind], flags, growable);
|
||||
openfile(argv[optind], flags, growable, NULL);
|
||||
}
|
||||
command_loop();
|
||||
|
||||
|
|
|
@ -3239,4 +3239,59 @@ Example:
|
|||
]
|
||||
}
|
||||
|
||||
EQMP
|
||||
|
||||
{
|
||||
.name = "blockdev-add",
|
||||
.args_type = "options:q",
|
||||
.mhandler.cmd_new = qmp_marshal_input_blockdev_add,
|
||||
},
|
||||
|
||||
SQMP
|
||||
blockdev-add
|
||||
------------
|
||||
|
||||
Add a block device.
|
||||
|
||||
Arguments:
|
||||
|
||||
- "options": block driver options
|
||||
|
||||
Example (1):
|
||||
|
||||
-> { "execute": "blockdev-add",
|
||||
"arguments": { "options" : { "driver": "qcow2",
|
||||
"file": { "driver": "file",
|
||||
"filename": "test.qcow2" } } } }
|
||||
<- { "return": {} }
|
||||
|
||||
Example (2):
|
||||
|
||||
-> { "execute": "blockdev-add",
|
||||
"arguments": {
|
||||
"options": {
|
||||
"driver": "qcow2",
|
||||
"id": "my_disk",
|
||||
"discard": "unmap",
|
||||
"cache": {
|
||||
"direct": true,
|
||||
"writeback": true
|
||||
},
|
||||
"file": {
|
||||
"driver": "file",
|
||||
"filename": "/tmp/test.qcow2"
|
||||
},
|
||||
"backing": {
|
||||
"driver": "raw",
|
||||
"file": {
|
||||
"driver": "file",
|
||||
"filename": "/dev/fdset/4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<- { "return": {} }
|
||||
|
||||
EQMP
|
||||
|
|
|
@ -71,7 +71,7 @@ def generate_struct_fields(members):
|
|||
c_name=c_var(argname))
|
||||
if structured:
|
||||
push_indent()
|
||||
ret += generate_struct("", argname, argentry)
|
||||
ret += generate_struct({ "field": argname, "data": argentry})
|
||||
pop_indent()
|
||||
else:
|
||||
ret += mcgen('''
|
||||
|
@ -81,13 +81,22 @@ def generate_struct_fields(members):
|
|||
|
||||
return ret
|
||||
|
||||
def generate_struct(structname, fieldname, members):
|
||||
def generate_struct(expr):
|
||||
|
||||
structname = expr.get('type', "")
|
||||
fieldname = expr.get('field', "")
|
||||
members = expr['data']
|
||||
base = expr.get('base')
|
||||
|
||||
ret = mcgen('''
|
||||
struct %(name)s
|
||||
{
|
||||
''',
|
||||
name=structname)
|
||||
|
||||
if base:
|
||||
ret += generate_struct_fields({'base': base})
|
||||
|
||||
ret += generate_struct_fields(members)
|
||||
|
||||
if len(fieldname):
|
||||
|
@ -417,7 +426,7 @@ def maybe_open(really, name, opt):
|
|||
for expr in exprs:
|
||||
ret = "\n"
|
||||
if expr.has_key('type'):
|
||||
ret += generate_struct(expr['type'], "", expr['data']) + "\n"
|
||||
ret += generate_struct(expr) + "\n"
|
||||
ret += generate_type_cleanup_decl(expr['type'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['type'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['type'])
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import getopt
|
||||
import errno
|
||||
|
||||
def generate_visit_struct_fields(name, field_prefix, fn_prefix, members):
|
||||
def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
|
||||
substructs = []
|
||||
ret = ''
|
||||
full_name = name if not fn_prefix else "%s_%s" % (name, fn_prefix)
|
||||
|
@ -42,6 +42,19 @@ def generate_visit_struct_fields(name, field_prefix, fn_prefix, members):
|
|||
name=name, full_name=full_name)
|
||||
push_indent()
|
||||
|
||||
if base:
|
||||
ret += mcgen('''
|
||||
visit_start_implicit_struct(m, obj ? (void**) &(*obj)->%(c_name)s : NULL, sizeof(%(type)s), &err);
|
||||
if (!err) {
|
||||
visit_type_%(type)s_fields(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, &err);
|
||||
error_propagate(errp, err);
|
||||
err = NULL;
|
||||
visit_end_implicit_struct(m, &err);
|
||||
}
|
||||
''',
|
||||
c_prefix=c_var(field_prefix),
|
||||
type=type_name(base), c_name=c_var('base'))
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(members):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
|
@ -120,8 +133,13 @@ def generate_visit_struct_body(field_prefix, name, members):
|
|||
''')
|
||||
return ret
|
||||
|
||||
def generate_visit_struct(name, members):
|
||||
ret = generate_visit_struct_fields(name, "", "", members)
|
||||
def generate_visit_struct(expr):
|
||||
|
||||
name = expr['type']
|
||||
members = expr['data']
|
||||
base = expr.get('base')
|
||||
|
||||
ret = generate_visit_struct_fields(name, "", "", members, base)
|
||||
|
||||
ret += mcgen('''
|
||||
|
||||
|
@ -472,7 +490,7 @@ def maybe_open(really, name, opt):
|
|||
|
||||
for expr in exprs:
|
||||
if expr.has_key('type'):
|
||||
ret = generate_visit_struct(expr['type'], expr['data'])
|
||||
ret = generate_visit_struct(expr)
|
||||
ret += generate_visit_list(expr['type'], expr['data'])
|
||||
fdef.write(ret)
|
||||
|
||||
|
|
|
@ -196,6 +196,7 @@ check-help:
|
|||
@echo " make check-qapi-schema Run QAPI schema tests"
|
||||
@echo " make check-block Run block tests"
|
||||
@echo " make check-report.html Generates an HTML test report"
|
||||
@echo " make check-clean Clean the tests"
|
||||
@echo
|
||||
@echo "Please note that HTML reports do not regenerate if the unit tests"
|
||||
@echo "has not changed."
|
||||
|
@ -252,8 +253,10 @@ check-report.html: check-report.xml
|
|||
|
||||
# Other tests
|
||||
|
||||
QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF)
|
||||
|
||||
.PHONY: check-tests/qemu-iotests-quick.sh
|
||||
check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) tests/qemu-iotests/socket_scm_helper$(EXESUF)
|
||||
check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) $(QEMU_IOTESTS_HELPERS-y)
|
||||
$<
|
||||
|
||||
.PHONY: check-tests/test-qapi.py
|
||||
|
@ -268,12 +271,21 @@ $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json
|
|||
|
||||
# Consolidated targets
|
||||
|
||||
.PHONY: check-qapi-schema check-qtest check-unit check
|
||||
.PHONY: check-qapi-schema check-qtest check-unit check check-clean
|
||||
check-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y))
|
||||
check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
|
||||
check-unit: $(patsubst %,check-%, $(check-unit-y))
|
||||
check-block: $(patsubst %,check-%, $(check-block-y))
|
||||
check: check-qapi-schema check-unit check-qtest
|
||||
check-clean:
|
||||
$(MAKE) -C tests/tcg clean
|
||||
rm -rf $(check-unit-y) $(check-qtest-i386-y) $(check-qtest-x86_64-y) $(check-qtest-sparc64-y) $(check-qtest-sparc-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y)
|
||||
|
||||
clean: check-clean
|
||||
|
||||
# Build the help program automatically
|
||||
|
||||
all: $(QEMU_IOTESTS_HELPERS-y)
|
||||
|
||||
-include $(wildcard tests/*.d)
|
||||
-include $(wildcard tests/libqos/*.d)
|
||||
|
|
|
@ -139,7 +139,10 @@ QEMU X.Y.Z monitor - type 'help' for more information
|
|||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on: read-only not supported by this bus type
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: Can't use a read-only drive
|
||||
QEMU_PROG: Device initialization failed.
|
||||
QEMU_PROG: Initialization of device ide-hd failed
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
|
@ -223,6 +226,6 @@ Testing: -drive file=foo:bar
|
|||
QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol
|
||||
|
||||
Testing: -drive file.filename=foo:bar
|
||||
QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open 'foo:bar': No such file or directory
|
||||
QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open file: No such file or directory
|
||||
|
||||
*** done
|
||||
|
|
|
@ -47,30 +47,34 @@ capacity_offset=16
|
|||
granularity_offset=20
|
||||
grain_table_size_offset=44
|
||||
|
||||
echo "=== Testing invalid granularity ==="
|
||||
echo
|
||||
echo "=== Testing invalid granularity ==="
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "$granularity_offset" "\xff\xff\xff\xff\xff\xff\xff\xff"
|
||||
{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||
|
||||
echo "=== Testing too big L2 table size ==="
|
||||
echo
|
||||
echo "=== Testing too big L2 table size ==="
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "$grain_table_size_offset" "\xff\xff\xff\xff"
|
||||
{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||
|
||||
echo "=== Testing too big L1 table size ==="
|
||||
echo
|
||||
echo "=== Testing too big L1 table size ==="
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "$capacity_offset" "\xff\xff\xff\xff"
|
||||
poke_file "$TEST_IMG" "$grain_table_size_offset" "\x01\x00\x00\x00"
|
||||
{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||
|
||||
echo "=== Testing monolithicFlat creation and opening ==="
|
||||
echo
|
||||
echo "=== Testing monolithicFlat creation and opening ==="
|
||||
IMGOPTS="subformat=monolithicFlat" _make_test_img 2G
|
||||
$QEMU_IMG info $TEST_IMG | _filter_testdir
|
||||
|
||||
echo
|
||||
echo "=== Testing monolithicFlat with zeroed_grain ==="
|
||||
IMGOPTS="subformat=monolithicFlat,zeroed_grain=on" _make_test_img 2G
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
QA output created by 059
|
||||
|
||||
=== Testing invalid granularity ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
invalid granularity, image may be corrupt
|
||||
qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
|
||||
qemu-io: can't open device TEST_DIR/t.vmdk: Invalid granularity, image may be corrupt
|
||||
no file open, try 'help open'
|
||||
=== Testing too big L2 table size ===
|
||||
|
||||
=== Testing too big L2 table size ===
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
L2 table size too big
|
||||
qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
|
||||
no file open, try 'help open'
|
||||
|
||||
=== Testing too big L1 table size ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
L1 size too big
|
||||
qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
|
||||
qemu-io: can't open device TEST_DIR/t.vmdk: L1 size too big
|
||||
no file open, try 'help open'
|
||||
=== Testing monolithicFlat creation and opening ===
|
||||
|
||||
=== Testing monolithicFlat creation and opening ===
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648
|
||||
image: TEST_DIR/t.vmdk
|
||||
file format: vmdk
|
||||
virtual size: 2.0G (2147483648 bytes)
|
||||
disk size: 4.0K
|
||||
|
||||
=== Testing monolithicFlat with zeroed_grain ===
|
||||
qemu-img: TEST_DIR/t.IMGFMT: Flat image can't enable zeroed grain
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648
|
||||
*** done
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
# creator
|
||||
owner=mreitz@redhat.com
|
||||
|
||||
seq=`basename $0`
|
||||
seq="$(basename $0)"
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
here="$PWD"
|
||||
tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
|
@ -47,9 +47,15 @@ rt_offset=65536 # 0x10000 (XXX: just an assumption)
|
|||
rb_offset=131072 # 0x20000 (XXX: just an assumption)
|
||||
l1_offset=196608 # 0x30000 (XXX: just an assumption)
|
||||
l2_offset=262144 # 0x40000 (XXX: just an assumption)
|
||||
l2_offset_after_snapshot=524288 # 0x80000 (XXX: just an assumption)
|
||||
|
||||
IMGOPTS="compat=1.1"
|
||||
|
||||
OPEN_RW="open -o overlap-check=all $TEST_IMG"
|
||||
# Overlap checks are done before write operations only, therefore opening an
|
||||
# image read-only makes the overlap-check option irrelevant
|
||||
OPEN_RO="open -r $TEST_IMG"
|
||||
|
||||
echo
|
||||
echo "=== Testing L2 reference into L1 ==="
|
||||
echo
|
||||
|
@ -65,16 +71,18 @@ _check_test_img
|
|||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Try to write something, thereby forcing the corrupt bit to be set
|
||||
$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
|
||||
|
||||
# The corrupt bit must now be set
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Try to open the image R/W (which should fail)
|
||||
$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir | _filter_imgfmt
|
||||
$QEMU_IO -c "$OPEN_RW" -c "read 0 512" 2>&1 | _filter_qemu_io \
|
||||
| _filter_testdir \
|
||||
| _filter_imgfmt
|
||||
|
||||
# Try to open it RO (which should succeed)
|
||||
$QEMU_IO -c "read 0 512" -r "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "$OPEN_RO" -c "read 0 512" | _filter_qemu_io
|
||||
|
||||
# We could now try to fix the image, but this would probably fail (how should an
|
||||
# L2 table linked onto the L1 table be fixed?)
|
||||
|
@ -92,7 +100,7 @@ poke_file "$TEST_IMG" "$(($rb_offset+8))" "\x00\x01"
|
|||
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00"
|
||||
_check_test_img
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Try to fix it
|
||||
|
@ -102,9 +110,34 @@ _check_test_img -r all
|
|||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Look if it's really really fixed
|
||||
$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
echo
|
||||
echo "=== Testing cluster data reference into inactive L2 table ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 1 0 512" | _filter_qemu_io
|
||||
$QEMU_IMG snapshot -c foo "$TEST_IMG"
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 2 0 512" | _filter_qemu_io
|
||||
# The inactive L2 table remains at its old offset
|
||||
poke_file "$TEST_IMG" "$l2_offset_after_snapshot" \
|
||||
"\x80\x00\x00\x00\x00\x04\x00\x00"
|
||||
_check_test_img
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 3 0 512" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
_check_test_img -r all
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 4 0 512" | _filter_qemu_io
|
||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||
|
||||
# Check data
|
||||
$QEMU_IO -c "$OPEN_RO" -c "read -P 4 0 512" | _filter_qemu_io
|
||||
$QEMU_IMG snapshot -a foo "$TEST_IMG"
|
||||
_check_test_img
|
||||
$QEMU_IO -c "$OPEN_RO" -c "read -P 1 0 512" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
|
|
@ -12,7 +12,6 @@ qcow2: Preventing invalid write on metadata (overlaps with active L1 table); ima
|
|||
write failed: Input/output error
|
||||
incompatible_features 0x2
|
||||
qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write
|
||||
no file open, try 'help open'
|
||||
read 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
|
@ -40,4 +39,43 @@ incompatible_features 0x0
|
|||
wrote 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
incompatible_features 0x0
|
||||
|
||||
=== Testing cluster data reference into inactive L2 table ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
ERROR cluster 4 refcount=1 reference=2
|
||||
Leaked cluster 9 refcount=1 reference=0
|
||||
|
||||
1 errors were found on the image.
|
||||
Data may be corrupted, or further writes to the image may corrupt it.
|
||||
|
||||
1 leaked clusters were found on the image.
|
||||
This means waste of disk space, but no harm to data.
|
||||
incompatible_features 0x0
|
||||
qcow2: Preventing invalid write on metadata (overlaps with inactive L2 table); image marked as corrupt.
|
||||
write failed: Input/output error
|
||||
incompatible_features 0x2
|
||||
Repairing cluster 4 refcount=1 reference=2
|
||||
Repairing cluster 9 refcount=1 reference=0
|
||||
Repairing OFLAG_COPIED data cluster: l2_entry=8000000000040000 refcount=2
|
||||
The following inconsistencies were found and repaired:
|
||||
|
||||
1 leaked clusters
|
||||
2 corruptions
|
||||
|
||||
Double checking the fixed image now...
|
||||
No errors were found on the image.
|
||||
incompatible_features 0x0
|
||||
wrote 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
incompatible_features 0x0
|
||||
read 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
read 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Test VHDX read/write from a sample image created with Hyper-V
|
||||
#
|
||||
# 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=jcody@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
|
||||
|
||||
_supported_fmt vhdx
|
||||
_supported_proto generic
|
||||
_supported_os Linux
|
||||
|
||||
_use_sample_img iotest-dynamic-1G.vhdx.bz2
|
||||
|
||||
echo
|
||||
echo "=== Verify pattern 0xa5, 0 - 33MB ==="
|
||||
$QEMU_IO -r -c "read -pP 0xa5 0 33M" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Verify pattern 0x96, 33M - 66M ==="
|
||||
$QEMU_IO -r -c "read -pP 0x96 33M 33M" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Verify pattern 0x00, 66M - 1024M ==="
|
||||
$QEMU_IO -r -c "read -pP 0x00 66M 958M" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
|
@ -0,0 +1,14 @@
|
|||
QA output created by 064
|
||||
|
||||
=== Verify pattern 0xa5, 0 - 33MB ===
|
||||
read 34603008/34603008 bytes at offset 0
|
||||
33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
=== Verify pattern 0x96, 33M - 66M ===
|
||||
read 34603008/34603008 bytes at offset 34603008
|
||||
33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
=== Verify pattern 0x00, 66M - 1024M ===
|
||||
read 1004535808/1004535808 bytes at offset 69206016
|
||||
958 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
|
@ -0,0 +1,125 @@
|
|||
#!/usr/bin/env python2
|
||||
#
|
||||
# Test for additional information emitted by qemu-img info on qcow2
|
||||
# images
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import iotests
|
||||
from iotests import qemu_img, qemu_img_pipe
|
||||
import unittest
|
||||
|
||||
test_img = os.path.join(iotests.test_dir, 'test.img')
|
||||
|
||||
class TestImageInfoSpecific(iotests.QMPTestCase):
|
||||
'''Abstract base class for ImageInfoSpecific tests'''
|
||||
|
||||
def setUp(self):
|
||||
if self.img_options is None:
|
||||
self.skipTest('Skipping abstract test class')
|
||||
qemu_img('create', '-f', iotests.imgfmt, '-o', self.img_options,
|
||||
test_img, '128K')
|
||||
|
||||
def tearDown(self):
|
||||
os.remove(test_img)
|
||||
|
||||
class TestQemuImgInfo(TestImageInfoSpecific):
|
||||
'''Abstract base class for qemu-img info tests'''
|
||||
|
||||
img_options = None
|
||||
json_compare = None
|
||||
human_compare = None
|
||||
|
||||
def test_json(self):
|
||||
data = json.loads(qemu_img_pipe('info', '--output=json', test_img))
|
||||
data = data['format-specific']
|
||||
self.assertEqual(data['type'], iotests.imgfmt)
|
||||
self.assertEqual(data['data'], self.json_compare)
|
||||
|
||||
def test_human(self):
|
||||
data = qemu_img_pipe('info', '--output=human', test_img).split('\n')
|
||||
data = data[(data.index('Format specific information:') + 1)
|
||||
:data.index('')]
|
||||
for field in data:
|
||||
self.assertTrue(re.match('^ {4}[^ ]', field) is not None)
|
||||
data = map(lambda line: line.strip(), data)
|
||||
self.assertEqual(data, self.human_compare)
|
||||
|
||||
class TestQMP(TestImageInfoSpecific):
|
||||
'''Abstract base class for qemu QMP tests'''
|
||||
|
||||
img_options = None
|
||||
qemu_options = ''
|
||||
TestImageInfoSpecific = TestImageInfoSpecific
|
||||
|
||||
def setUp(self):
|
||||
self.TestImageInfoSpecific.setUp(self)
|
||||
self.vm = iotests.VM().add_drive(test_img, self.qemu_options)
|
||||
self.vm.launch()
|
||||
|
||||
def tearDown(self):
|
||||
self.vm.shutdown()
|
||||
self.TestImageInfoSpecific.tearDown(self)
|
||||
|
||||
def test_qmp(self):
|
||||
result = self.vm.qmp('query-block')['return']
|
||||
drive = filter(lambda drive: drive['device'] == 'drive0', result)[0]
|
||||
data = drive['inserted']['image']['format-specific']
|
||||
self.assertEqual(data['type'], iotests.imgfmt)
|
||||
self.assertEqual(data['data'], self.compare)
|
||||
|
||||
class TestQCow2(TestQemuImgInfo):
|
||||
'''Testing a qcow2 version 2 image'''
|
||||
img_options = 'compat=0.10'
|
||||
json_compare = { 'compat': '0.10' }
|
||||
human_compare = [ 'compat: 0.10' ]
|
||||
|
||||
class TestQCow3NotLazy(TestQemuImgInfo):
|
||||
'''Testing a qcow2 version 3 image with lazy refcounts disabled'''
|
||||
img_options = 'compat=1.1,lazy_refcounts=off'
|
||||
json_compare = { 'compat': '1.1', 'lazy-refcounts': False }
|
||||
human_compare = [ 'compat: 1.1', 'lazy refcounts: false' ]
|
||||
|
||||
class TestQCow3Lazy(TestQemuImgInfo):
|
||||
'''Testing a qcow2 version 3 image with lazy refcounts enabled'''
|
||||
img_options = 'compat=1.1,lazy_refcounts=on'
|
||||
json_compare = { 'compat': '1.1', 'lazy-refcounts': True }
|
||||
human_compare = [ 'compat: 1.1', 'lazy refcounts: true' ]
|
||||
|
||||
class TestQCow3NotLazyQMP(TestQMP):
|
||||
'''Testing a qcow2 version 3 image with lazy refcounts disabled, opening
|
||||
with lazy refcounts enabled'''
|
||||
img_options = 'compat=1.1,lazy_refcounts=off'
|
||||
qemu_options = 'lazy-refcounts=on'
|
||||
compare = { 'compat': '1.1', 'lazy-refcounts': False }
|
||||
|
||||
class TestQCow3LazyQMP(TestQMP):
|
||||
'''Testing a qcow2 version 3 image with lazy refcounts enabled, opening
|
||||
with lazy refcounts disabled'''
|
||||
img_options = 'compat=1.1,lazy_refcounts=on'
|
||||
qemu_options = 'lazy-refcounts=off'
|
||||
compare = { 'compat': '1.1', 'lazy-refcounts': True }
|
||||
|
||||
TestImageInfoSpecific = None
|
||||
TestQemuImgInfo = None
|
||||
TestQMP = None
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['qcow2'])
|
|
@ -0,0 +1,5 @@
|
|||
........
|
||||
----------------------------------------------------------------------
|
||||
Ran 8 tests
|
||||
|
||||
OK
|
|
@ -0,0 +1,63 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Test case for discarding preallocated zero clusters 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
|
||||
|
||||
IMGOPTS="compat=1.1"
|
||||
IMG_SIZE=64M
|
||||
|
||||
echo
|
||||
echo "=== Testing snapshotting an image with zero clusters ==="
|
||||
echo
|
||||
_make_test_img $IMG_SIZE
|
||||
# Write some normal clusters, zero them (creating preallocated zero clusters)
|
||||
# and discard those
|
||||
$QEMU_IO -c "write 0 256k" -c "write -z 0 256k" -c "discard 0 256k" "$TEST_IMG" \
|
||||
| _filter_qemu_io
|
||||
# Check the image (there shouldn't be any leaks)
|
||||
_check_test_img
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
|
@ -0,0 +1,13 @@
|
|||
QA output created by 066
|
||||
|
||||
=== Testing snapshotting an image with zero clusters ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 262144/262144 bytes at offset 0
|
||||
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 262144/262144 bytes at offset 0
|
||||
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
discard 262144/262144 bytes at offset 0
|
||||
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
*** done
|
|
@ -0,0 +1,133 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Test automatic deletion of BDSes created by -drive/drive_add
|
||||
#
|
||||
# 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=kwolf@redhat.com
|
||||
|
||||
seq=`basename $0`
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here=`pwd`
|
||||
tmp=/tmp/$$
|
||||
status=1 # failure is the default!
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
function do_run_qemu()
|
||||
{
|
||||
echo Testing: "$@"
|
||||
$QEMU -nographic -qmp stdio -serial none "$@"
|
||||
echo
|
||||
}
|
||||
|
||||
function run_qemu()
|
||||
{
|
||||
do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp
|
||||
}
|
||||
|
||||
size=128M
|
||||
|
||||
_make_test_img $size
|
||||
|
||||
echo
|
||||
echo === -drive/-device and device_del ===
|
||||
echo
|
||||
|
||||
run_qemu -drive file=$TEST_IMG,format=$IMGFMT,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0 <<EOF
|
||||
{ "execute": "qmp_capabilities" }
|
||||
{ "execute": "query-block" }
|
||||
{ "execute": "device_del", "arguments": { "id": "virtio0" } }
|
||||
{ "execute": "system_reset" }
|
||||
{ "execute": "query-block" }
|
||||
{ "execute": "quit" }
|
||||
EOF
|
||||
|
||||
echo
|
||||
echo === -drive/device_add and device_del ===
|
||||
echo
|
||||
|
||||
run_qemu -drive file=$TEST_IMG,format=$IMGFMT,if=none,id=disk <<EOF
|
||||
{ "execute": "qmp_capabilities" }
|
||||
{ "execute": "query-block" }
|
||||
{ "execute": "device_add",
|
||||
"arguments": { "driver": "virtio-blk-pci", "drive": "disk",
|
||||
"id": "virtio0" } }
|
||||
{ "execute": "device_del", "arguments": { "id": "virtio0" } }
|
||||
{ "execute": "system_reset" }
|
||||
{ "execute": "query-block" }
|
||||
{ "execute": "quit" }
|
||||
EOF
|
||||
|
||||
echo
|
||||
echo === drive_add/device_add and device_del ===
|
||||
echo
|
||||
|
||||
run_qemu <<EOF
|
||||
{ "execute": "qmp_capabilities" }
|
||||
{ "execute": "human-monitor-command",
|
||||
"arguments": { "command-line": "drive_add 0 file=$TEST_IMG,format=$IMGFMT,if=none,id=disk" } }
|
||||
{ "execute": "query-block" }
|
||||
{ "execute": "device_add",
|
||||
"arguments": { "driver": "virtio-blk-pci", "drive": "disk",
|
||||
"id": "virtio0" } }
|
||||
{ "execute": "device_del", "arguments": { "id": "virtio0" } }
|
||||
{ "execute": "system_reset" }
|
||||
{ "execute": "query-block" }
|
||||
{ "execute": "quit" }
|
||||
EOF
|
||||
|
||||
echo
|
||||
echo === blockdev_add/device_add and device_del ===
|
||||
echo
|
||||
|
||||
run_qemu <<EOF
|
||||
{ "execute": "qmp_capabilities" }
|
||||
{ "execute": "blockdev-add",
|
||||
"arguments": {
|
||||
"options": {
|
||||
"driver": "$IMGFMT",
|
||||
"id": "disk",
|
||||
"file": {
|
||||
"driver": "file",
|
||||
"filename": "$TEST_IMG"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{ "execute": "query-block" }
|
||||
{ "execute": "device_add",
|
||||
"arguments": { "driver": "virtio-blk-pci", "drive": "disk",
|
||||
"id": "virtio0" } }
|
||||
{ "execute": "device_del", "arguments": { "id": "virtio0" } }
|
||||
{ "execute": "system_reset" }
|
||||
{ "execute": "query-block" }
|
||||
{ "execute": "quit" }
|
||||
EOF
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
|
@ -0,0 +1,80 @@
|
|||
QA output created by 067
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||
|
||||
=== -drive/-device and device_del ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0
|
||||
QMP_VERSION
|
||||
{"return": {}}
|
||||
{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
|
||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
|
||||
|
||||
|
||||
=== -drive/device_add and device_del ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
|
||||
QMP_VERSION
|
||||
{"return": {}}
|
||||
{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
|
||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
|
||||
|
||||
|
||||
=== drive_add/device_add and device_del ===
|
||||
|
||||
Testing:
|
||||
QMP_VERSION
|
||||
{"return": {}}
|
||||
{"return": "OK\r\n"}
|
||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
|
||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
|
||||
|
||||
|
||||
=== blockdev_add/device_add and device_del ===
|
||||
|
||||
Testing:
|
||||
QMP_VERSION
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
|
||||
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
|
||||
|
||||
*** done
|
|
@ -45,6 +45,7 @@ valgrind=false
|
|||
rm -f $tmp.list $tmp.tmp $tmp.sed
|
||||
|
||||
export IMGFMT=raw
|
||||
export IMGFMT_GENERIC=true
|
||||
export IMGPROTO=file
|
||||
export IMGOPTS=""
|
||||
export QEMU_IO_OPTIONS=""
|
||||
|
@ -133,6 +134,7 @@ check options
|
|||
-qed test qed
|
||||
-vdi test vdi
|
||||
-vpc test vpc
|
||||
-vhdx test vhdx
|
||||
-vmdk test vmdk
|
||||
-rbd test rbd
|
||||
-sheepdog test sheepdog
|
||||
|
@ -195,6 +197,12 @@ testlist options
|
|||
xpand=false
|
||||
;;
|
||||
|
||||
-vhdx)
|
||||
IMGFMT=vhdx
|
||||
xpand=false
|
||||
IMGFMT_GENERIC=false
|
||||
;;
|
||||
|
||||
-rbd)
|
||||
IMGPROTO=rbd
|
||||
xpand=false
|
||||
|
|
|
@ -159,5 +159,13 @@ _filter_qemu()
|
|||
-e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#'
|
||||
}
|
||||
|
||||
# replace problematic QMP output like timestamps
|
||||
_filter_qmp()
|
||||
{
|
||||
_filter_win32 | \
|
||||
sed -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \
|
||||
-e 's#^{"QMP":.*}$#QMP_VERSION#'
|
||||
}
|
||||
|
||||
# make sure this script returns success
|
||||
/bin/true
|
||||
|
|
|
@ -197,12 +197,30 @@ _check_test_img()
|
|||
|
||||
_img_info()
|
||||
{
|
||||
discard=0
|
||||
regex_json_spec_start='^ *"format-specific": \{'
|
||||
$QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \
|
||||
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
|
||||
-e "s#$TEST_DIR#TEST_DIR#g" \
|
||||
-e "s#$IMGFMT#IMGFMT#g" \
|
||||
-e "/^disk size:/ D" \
|
||||
-e "/actual-size/ D"
|
||||
-e "/actual-size/ D" | \
|
||||
while IFS='' read line; do
|
||||
if [[ $line == "Format specific information:" ]]; then
|
||||
discard=1
|
||||
elif [[ $line =~ $regex_json_spec_start ]]; then
|
||||
discard=2
|
||||
regex_json_spec_end="^${line%%[^ ]*}\\},? *$"
|
||||
fi
|
||||
if [[ $discard == 0 ]]; then
|
||||
echo "$line"
|
||||
elif [[ $discard == 1 && ! $line ]]; then
|
||||
echo
|
||||
discard=0
|
||||
elif [[ $discard == 2 && $line =~ $regex_json_spec_end ]]; then
|
||||
discard=0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
_get_pids_by_name()
|
||||
|
@ -321,7 +339,7 @@ _fail()
|
|||
_supported_fmt()
|
||||
{
|
||||
for f; do
|
||||
if [ "$f" = "$IMGFMT" -o "$f" = "generic" ]; then
|
||||
if [ "$f" = "$IMGFMT" -o "$f" = "generic" -a "$IMGFMT_GENERIC" = "true" ]; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -69,3 +69,7 @@
|
|||
061 rw auto
|
||||
062 rw auto
|
||||
063 rw auto
|
||||
064 rw auto
|
||||
065 rw auto
|
||||
066 rw auto
|
||||
067 rw auto
|
||||
|
|
|
@ -49,6 +49,10 @@ def qemu_img_verbose(*args):
|
|||
'''Run qemu-img without suppressing its output and return the exit code'''
|
||||
return subprocess.call(qemu_img_args + list(args))
|
||||
|
||||
def qemu_img_pipe(*args):
|
||||
'''Run qemu-img and return its output'''
|
||||
return subprocess.Popen(qemu_img_args + list(args), stdout=subprocess.PIPE).communicate()[0]
|
||||
|
||||
def qemu_io(*args):
|
||||
'''Run qemu-io and return the stdout data'''
|
||||
args = qemu_io_args + list(args)
|
||||
|
|
Binary file not shown.
|
@ -834,6 +834,12 @@ const char *qemu_opts_id(QemuOpts *opts)
|
|||
return opts->id;
|
||||
}
|
||||
|
||||
/* The id string will be g_free()d by qemu_opts_del */
|
||||
void qemu_opts_set_id(QemuOpts *opts, char *id)
|
||||
{
|
||||
opts->id = id;
|
||||
}
|
||||
|
||||
void qemu_opts_del(QemuOpts *opts)
|
||||
{
|
||||
QemuOpt *opt;
|
||||
|
|
Loading…
Reference in New Issue