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:
Anthony Liguori 2013-10-11 09:29:58 -07:00
commit 33c6cae44e
66 changed files with 2110 additions and 517 deletions

View File

@ -246,7 +246,6 @@ clean:
rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp) rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp)
rm -rf qapi-generated rm -rf qapi-generated
rm -rf qga/qapi-generated rm -rf qga/qapi-generated
$(MAKE) -C tests/tcg clean
for d in $(ALL_SUBDIRS); do \ for d in $(ALL_SUBDIRS); do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
rm -f $$d/qemu-options.def; \ rm -f $$d/qemu-options.def; \

53
block.c
View File

@ -769,13 +769,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
bs->read_only = !(open_flags & BDRV_O_RDWR); bs->read_only = !(open_flags & BDRV_O_RDWR);
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
error_setg(errp, "Driver '%s' is not whitelisted", drv->format_name); 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; return -ENOTSUP;
} }
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */ assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
if (!bs->read_only && (flags & BDRV_O_COPY_ON_READ)) { if (flags & BDRV_O_COPY_ON_READ) {
bdrv_enable_copy_on_read(bs); 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) { if (filename != NULL) {
@ -881,7 +890,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
/* Find the right block driver */ /* Find the right block driver */
drvname = qdict_get_try_str(options, "driver"); drvname = qdict_get_try_str(options, "driver");
if (drvname) { if (drvname) {
drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR)); drv = bdrv_find_format(drvname);
if (!drv) { if (!drv) {
error_setg(errp, "Unknown driver '%s'", drvname); 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 */ /* Find the right image format driver */
drvname = qdict_get_try_str(options, "driver"); drvname = qdict_get_try_str(options, "driver");
if (drvname) { if (drvname) {
drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR)); drv = bdrv_find_format(drvname);
qdict_del(options, "driver"); qdict_del(options, "driver");
} }
@ -3147,6 +3156,12 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
return ret; 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 (!(ret & BDRV_BLOCK_DATA)) {
if (bdrv_has_zero_init(bs)) { if (bdrv_has_zero_init(bs)) {
ret |= BDRV_BLOCK_ZERO; ret |= BDRV_BLOCK_ZERO;
@ -3322,6 +3337,15 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return drv->bdrv_get_info(bs, 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, int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int64_t pos, int size) 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); 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;
}

View File

@ -202,9 +202,9 @@ static void backup_iostatus_reset(BlockJob *job)
bdrv_iostatus_reset(s->target); bdrv_iostatus_reset(s->target);
} }
static const BlockJobType backup_job_type = { static const BlockJobDriver backup_job_driver = {
.instance_size = sizeof(BackupBlockJob), .instance_size = sizeof(BackupBlockJob),
.job_type = "backup", .job_type = BLOCK_JOB_TYPE_BACKUP,
.set_speed = backup_set_speed, .set_speed = backup_set_speed,
.iostatus_reset = backup_iostatus_reset, .iostatus_reset = backup_iostatus_reset,
}; };
@ -370,7 +370,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
return; return;
} }
BackupBlockJob *job = block_job_create(&backup_job_type, bs, speed, BackupBlockJob *job = block_job_create(&backup_job_driver, bs, speed,
cb, opaque, errp); cb, opaque, errp);
if (!job) { if (!job) {
return; return;

View File

@ -362,8 +362,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
opts = qemu_opts_create_nofail(&runtime_opts); opts = qemu_opts_create_nofail(&runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err); qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -373,6 +372,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
if (config) { if (config) {
ret = read_config(s, config); ret = read_config(s, config);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read blkdebug config file");
goto fail; goto fail;
} }
} }
@ -383,14 +383,14 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
/* Open the backing file */ /* Open the backing file */
filename = qemu_opt_get(opts, "x-image"); filename = qemu_opt_get(opts, "x-image");
if (filename == NULL) { if (filename == NULL) {
error_setg(errp, "Could not retrieve image file name");
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err); ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
goto fail; goto fail;
} }

View File

@ -128,8 +128,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
opts = qemu_opts_create_nofail(&runtime_opts); opts = qemu_opts_create_nofail(&runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err); qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -137,20 +136,21 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
/* Parse the raw image filename */ /* Parse the raw image filename */
raw = qemu_opt_get(opts, "x-raw"); raw = qemu_opt_get(opts, "x-raw");
if (raw == NULL) { if (raw == NULL) {
error_setg(errp, "Could not retrieve raw image filename");
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err); ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
goto fail; goto fail;
} }
/* Open the test file */ /* Open the test file */
filename = qemu_opt_get(opts, "x-image"); filename = qemu_opt_get(opts, "x-image");
if (filename == NULL) { if (filename == NULL) {
error_setg(errp, "Could not retrieve test image filename");
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -158,8 +158,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
s->test_file = bdrv_new(""); s->test_file = bdrv_new("");
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err); ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
bdrv_unref(s->test_file); bdrv_unref(s->test_file);
s->test_file = NULL; s->test_file = NULL;
goto fail; goto fail;
@ -417,6 +416,8 @@ static BlockDriver bdrv_blkverify = {
.bdrv_aio_readv = blkverify_aio_readv, .bdrv_aio_readv = blkverify_aio_readv,
.bdrv_aio_writev = blkverify_aio_writev, .bdrv_aio_writev = blkverify_aio_writev,
.bdrv_aio_flush = blkverify_aio_flush, .bdrv_aio_flush = blkverify_aio_flush,
.bdrv_check_ext_snapshot = bdrv_check_ext_snapshot_forbidden,
}; };
static void bdrv_blkverify_init(void) static void bdrv_blkverify_init(void)

View File

@ -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); 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), .instance_size = sizeof(CommitBlockJob),
.job_type = "commit", .job_type = BLOCK_JOB_TYPE_COMMIT,
.set_speed = commit_set_speed, .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) { if (!s) {
return; return;
} }

View File

@ -525,9 +525,9 @@ static void mirror_complete(BlockJob *job, Error **errp)
block_job_resume(job); block_job_resume(job);
} }
static const BlockJobType mirror_job_type = { static const BlockJobDriver mirror_job_driver = {
.instance_size = sizeof(MirrorBlockJob), .instance_size = sizeof(MirrorBlockJob),
.job_type = "mirror", .job_type = BLOCK_JOB_TYPE_MIRROR,
.set_speed = mirror_set_speed, .set_speed = mirror_set_speed,
.iostatus_reset= mirror_iostatus_reset, .iostatus_reset= mirror_iostatus_reset,
.complete = mirror_complete, .complete = mirror_complete,
@ -563,7 +563,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
return; 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) { if (!s) {
return; return;
} }

View File

@ -25,6 +25,9 @@
#include "block/qapi.h" #include "block/qapi.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qmp-commands.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 * 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->dirty_flag = bdi.is_dirty;
info->has_dirty_flag = true; 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; backing_filename = bs->backing_file;
if (backing_filename[0] != '\0') { if (backing_filename[0] != '\0') {
info->backing_filename = g_strdup(backing_filename); 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, void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
ImageInfo *info) ImageInfo *info)
{ {
@ -493,4 +612,9 @@ void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
func_fprintf(f, "\n"); 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);
}
} }

View File

@ -115,15 +115,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
} }
if (c == s->refcount_block_cache) { if (c == s->refcount_block_cache) {
ret = qcow2_pre_write_overlap_check(bs, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_BLOCK,
c->entries[i].offset, s->cluster_size); c->entries[i].offset, s->cluster_size);
} else if (c == s->l2_table_cache) { } else if (c == s->l2_table_cache) {
ret = qcow2_pre_write_overlap_check(bs, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2,
c->entries[i].offset, s->cluster_size); c->entries[i].offset, s->cluster_size);
} else { } 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); c->entries[i].offset, s->cluster_size);
} }

View File

@ -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 /* the L1 position has not yet been updated, so these clusters must
* indeed be completely free */ * indeed be completely free */
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset,
new_l1_table_offset, new_l1_size2); new_l1_size2);
if (ret < 0) { if (ret < 0) {
goto fail; 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]); buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
} }
ret = qcow2_pre_write_overlap_check(bs, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
s->l1_table_offset + 8 * l1_start_index, sizeof(buf)); s->l1_table_offset + 8 * l1_start_index, sizeof(buf));
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@ -396,7 +395,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
&s->aes_encrypt_key); &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); cluster_offset + n_start * BDRV_SECTOR_SIZE, n * BDRV_SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
goto out; 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, ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
offset, s->cluster_size);
if (ret < 0) { if (ret < 0) {
if (!preallocated) { if (!preallocated) {
qcow2_free_clusters(bs, offset, s->cluster_size, 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 { } else {
if (l2_dirty) { if (l2_dirty) {
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT & ret = qcow2_pre_write_overlap_check(bs,
~(QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2), l2_offset, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, l2_offset,
s->cluster_size); s->cluster_size);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;

View File

@ -796,11 +796,13 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
} }
break; break;
case QCOW2_CLUSTER_NORMAL: case QCOW2_CLUSTER_NORMAL:
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK, case QCOW2_CLUSTER_ZERO:
nb_clusters << s->cluster_bits, type); if (l2_entry & L2E_OFFSET_MASK) {
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
nb_clusters << s->cluster_bits, type);
}
break; break;
case QCOW2_CLUSTER_UNALLOCATED: case QCOW2_CLUSTER_UNALLOCATED:
case QCOW2_CLUSTER_ZERO:
break; break;
default: default:
abort(); abort();
@ -1309,9 +1311,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
} }
if (l2_dirty) { if (l2_dirty) {
ret = qcow2_pre_write_overlap_check(bs, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2, l2_offset, l2_offset, s->cluster_size);
s->cluster_size);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "ERROR: Could not write L2 table; metadata " fprintf(stderr, "ERROR: Could not write L2 table; metadata "
"overlap check failed: %s\n", strerror(-ret)); "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]); buf[i] = cpu_to_be64(s->refcount_table[rt_start_index + i]);
} }
ret = qcow2_pre_write_overlap_check(bs, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_TABLE,
QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_TABLE,
s->refcount_table_offset + rt_start_index * sizeof(uint64_t), s->refcount_table_offset + rt_start_index * sizeof(uint64_t),
sizeof(buf)); sizeof(buf));
if (ret < 0) { 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 /* new block has not yet been entered into refcount table, therefore it is
* no refcount block yet (regarding this check) */ * no refcount block yet (regarding this check) */
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, new_offset, ret = qcow2_pre_write_overlap_check(bs, 0, new_offset, s->cluster_size);
s->cluster_size);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "Could not write refcount block; metadata overlap " fprintf(stderr, "Could not write refcount block; metadata overlap "
"check failed: %s\n", strerror(-ret)); "check failed: %s\n", strerror(-ret));
@ -1637,8 +1636,8 @@ fail:
* looking for overlaps with important metadata sections (L1/L2 tables etc.), * looking for overlaps with important metadata sections (L1/L2 tables etc.),
* i.e. a sanity check without relying on the refcount tables. * i.e. a sanity check without relying on the refcount tables.
* *
* The chk parameter specifies exactly what checks to perform (being a bitmask * The ign parameter specifies what checks not to perform (being a bitmask of
* of QCow2MetadataOverlap values). * QCow2MetadataOverlap values), i.e., what sections to ignore.
* *
* Returns: * Returns:
* - 0 if writing to this offset will not affect the mentioned metadata * - 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, * - a negative value (-errno) indicating an error while performing a check,
* e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2 * 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) int64_t size)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int chk = s->overlap_check & ~ign;
int i, j; int i, j;
if (!size) { 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++) { for (i = 0; i < s->nb_snapshots; i++) {
uint64_t l1_ofs = s->snapshots[i].l1_table_offset; uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
uint32_t l1_sz = s->snapshots[i].l1_size; 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; int ret;
ret = bdrv_read(bs->file, l1_ofs / BDRV_SECTOR_SIZE, (uint8_t *)l1, ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2);
l1_sz * sizeof(uint64_t) / BDRV_SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
g_free(l1); g_free(l1);
return ret; return ret;
@ -1766,10 +1765,10 @@ static const char *metadata_ol_names[] = {
* Returns 0 if there were neither overlaps nor errors while checking for * Returns 0 if there were neither overlaps nor errors while checking for
* overlaps; or a negative value (-errno) on error. * 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) 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) { if (ret < 0) {
return ret; return ret;

View File

@ -182,19 +182,19 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
offset = snapshots_offset; offset = snapshots_offset;
if (offset < 0) { if (offset < 0) {
return offset; ret = offset;
goto fail;
} }
ret = bdrv_flush(bs); ret = bdrv_flush(bs);
if (ret < 0) { if (ret < 0) {
return ret; goto fail;
} }
/* The snapshot list position has not yet been updated, so these clusters /* The snapshot list position has not yet been updated, so these clusters
* must indeed be completely free */ * must indeed be completely free */
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, offset, ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size);
snapshots_size);
if (ret < 0) { 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); id_str_size = strlen(sn->id_str);
name_size = strlen(sn->name); 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.id_str_size = cpu_to_be16(id_str_size);
h.name_size = cpu_to_be16(name_size); h.name_size = cpu_to_be16(name_size);
offset = align_offset(offset, 8); offset = align_offset(offset, 8);
@ -278,6 +279,10 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
return 0; return 0;
fail: fail:
if (snapshots_offset > 0) {
qcow2_free_clusters(bs, snapshots_offset, snapshots_size,
QCOW2_DISCARD_ALWAYS);
}
return ret; return ret;
} }
@ -286,7 +291,8 @@ static void find_new_snapshot_id(BlockDriverState *bs,
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn; QCowSnapshot *sn;
int i, id, id_max = 0; int i;
unsigned long id, id_max = 0;
for(i = 0; i < s->nb_snapshots; i++) { for(i = 0; i < s->nb_snapshots; i++) {
sn = s->snapshots + i; sn = s->snapshots + i;
@ -294,7 +300,7 @@ static void find_new_snapshot_id(BlockDriverState *bs,
if (id > id_max) if (id > id_max)
id_max = id; 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, 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]); l1_table[i] = cpu_to_be64(s->l1_table[i]);
} }
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset,
sn->l1_table_offset, s->l1_size * sizeof(uint64_t)); s->l1_size * sizeof(uint64_t));
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -427,6 +433,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
if (ret < 0) { if (ret < 0) {
g_free(s->snapshots); g_free(s->snapshots);
s->snapshots = old_snapshot_list; s->snapshots = old_snapshot_list;
s->nb_snapshots--;
goto fail; goto fail;
} }
@ -513,9 +520,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
goto fail; goto fail;
} }
ret = qcow2_pre_write_overlap_check(bs, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1, s->l1_table_offset, cur_l1_bytes);
s->l1_table_offset, cur_l1_bytes);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }

View File

@ -354,10 +354,67 @@ static QemuOptsList qcow2_runtime_opts = {
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
.help = "Generate discard requests when other clusters are freed", .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 */ } { /* 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, static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp) Error **errp)
{ {
@ -368,6 +425,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
Error *local_err = NULL; Error *local_err = NULL;
uint64_t ext_end; uint64_t ext_end;
uint64_t l1_vm_state_index; 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)); ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) { if (ret < 0) {
@ -631,6 +690,33 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
s->discard_passthrough[QCOW2_DISCARD_OTHER] = s->discard_passthrough[QCOW2_DISCARD_OTHER] =
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); 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); qemu_opts_del(opts);
if (s->use_lazy_refcounts && s->qcow_version < 3) { 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); 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, cluster_offset + index_in_cluster * BDRV_SECTOR_SIZE,
cur_nr_sectors * BDRV_SECTOR_SIZE); cur_nr_sectors * BDRV_SECTOR_SIZE);
if (ret < 0) { 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) { if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */ /* 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); ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
@ -1759,8 +1837,7 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
} }
cluster_offset &= s->cluster_offset_mask; cluster_offset &= s->cluster_offset_mask;
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len);
cluster_offset, out_len);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -1810,6 +1887,33 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return 0; 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 #if 0
static void dump_refcounts(BlockDriverState *bs) 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 * 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 * so right now; however, we should error out (if qemu supports this in
* the future and this code has not been adapted) */ * 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."); "currently not supported.");
return -ENOTSUP; return -ENOTSUP;
} }
@ -2130,6 +2234,7 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_snapshot_list = qcow2_snapshot_list, .bdrv_snapshot_list = qcow2_snapshot_list,
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
.bdrv_get_info = qcow2_get_info, .bdrv_get_info = qcow2_get_info,
.bdrv_get_specific_info = qcow2_get_specific_info,
.bdrv_save_vmstate = qcow2_save_vmstate, .bdrv_save_vmstate = qcow2_save_vmstate,
.bdrv_load_vmstate = qcow2_load_vmstate, .bdrv_load_vmstate = qcow2_load_vmstate,

View File

@ -63,6 +63,15 @@
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot"
#define QCOW2_OPT_DISCARD_OTHER "pass-discard-other" #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 { typedef struct QCowHeader {
uint32_t magic; uint32_t magic;
@ -203,6 +212,8 @@ typedef struct BDRVQcowState {
bool discard_passthrough[QCOW2_DISCARD_MAX]; bool discard_passthrough[QCOW2_DISCARD_MAX];
int overlap_check; /* bitmask of Qcow2MetadataOverlap values */
uint64_t incompatible_features; uint64_t incompatible_features;
uint64_t compatible_features; uint64_t compatible_features;
uint64_t autoclear_features; uint64_t autoclear_features;
@ -315,14 +326,19 @@ typedef enum QCow2MetadataOverlap {
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
} QCow2MetadataOverlap; } 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 */ /* Perform all overlap checks which don't require disk access */
#define QCOW2_OL_CACHED \ #define QCOW2_OL_CACHED \
(QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_ACTIVE_L2 | \ (QCOW2_OL_CONSTANT | QCOW2_OL_ACTIVE_L2 | QCOW2_OL_REFCOUNT_BLOCK | \
QCOW2_OL_REFCOUNT_TABLE | QCOW2_OL_REFCOUNT_BLOCK | \ QCOW2_OL_INACTIVE_L1)
QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_INACTIVE_L1)
/* The default checks to perform */ /* Perform all overlap checks */
#define QCOW2_OL_DEFAULT QCOW2_OL_CACHED #define QCOW2_OL_ALL \
(QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2)
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL #define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
#define L2E_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); 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); 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); int64_t size);
/* qcow2-cluster.c functions */ /* qcow2-cluster.c functions */

View File

@ -276,7 +276,7 @@ static QemuOptsList raw_runtime_opts = {
}; };
static int raw_open_common(BlockDriverState *bs, QDict *options, 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; BDRVRawState *s = bs->opaque;
QemuOpts *opts; QemuOpts *opts;
@ -287,8 +287,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
opts = qemu_opts_create_nofail(&raw_runtime_opts); opts = qemu_opts_create_nofail(&raw_runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err); qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -297,6 +296,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
ret = raw_normalize_devicepath(&filename); ret = raw_normalize_devicepath(&filename);
if (ret != 0) { if (ret != 0) {
error_setg_errno(errp, -ret, "Could not normalize device path");
goto fail; goto fail;
} }
@ -310,6 +310,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
if (ret == -EROFS) { if (ret == -EROFS) {
ret = -EACCES; ret = -EACCES;
} }
error_setg_errno(errp, -ret, "Could not open file");
goto fail; goto fail;
} }
s->fd = fd; 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)) { if (raw_set_aio(&s->aio_ctx, &s->use_aio, bdrv_flags)) {
qemu_close(fd); qemu_close(fd);
ret = -errno; ret = -errno;
error_setg_errno(errp, -ret, "Could not set AIO state");
goto fail; goto fail;
} }
#endif #endif
@ -339,9 +341,15 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp) Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
Error *local_err = NULL;
int ret;
s->type = FTYPE_FILE; 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, 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() * 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 */ * won't override aio_ctx if aio_ctx is non-NULL */
if (raw_set_aio(&s->aio_ctx, &raw_s->use_aio, state->flags)) { if (raw_set_aio(&s->aio_ctx, &raw_s->use_aio, state->flags)) {
error_setg(errp, "Could not set AIO state");
return -1; return -1;
} }
#endif #endif
@ -417,6 +426,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
assert(!(raw_s->open_flags & O_CREAT)); assert(!(raw_s->open_flags & O_CREAT));
raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags); raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags);
if (raw_s->fd == -1) { if (raw_s->fd == -1) {
error_setg_errno(errp, errno, "Could not reopen file");
ret = -1; ret = -1;
} }
} }
@ -1060,12 +1070,15 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
0644); 0644);
if (fd < 0) { if (fd < 0) {
result = -errno; result = -errno;
error_setg_errno(errp, -result, "Could not create file");
} else { } else {
if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) { if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
result = -errno; result = -errno;
error_setg_errno(errp, -result, "Could not resize file");
} }
if (qemu_close(fd) != 0) { if (qemu_close(fd) != 0) {
result = -errno; result = -errno;
error_setg_errno(errp, -result, "Could not close the new file");
} }
} }
return result; return result;
@ -1338,6 +1351,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp) Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
Error *local_err = NULL;
int ret; int ret;
const char *filename = qdict_get_str(options, "filename"); const char *filename = qdict_get_str(options, "filename");
@ -1381,8 +1395,11 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
} }
#endif #endif
ret = raw_open_common(bs, options, flags, 0); ret = raw_open_common(bs, options, flags, 0, &local_err);
if (ret < 0) { if (ret < 0) {
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
}
return ret; return ret;
} }
@ -1390,6 +1407,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
ret = check_hdev_writable(s); ret = check_hdev_writable(s);
if (ret < 0) { if (ret < 0) {
raw_close(bs); raw_close(bs);
error_setg_errno(errp, -ret, "The device is not writable");
return ret; return ret;
} }
} }
@ -1525,15 +1543,23 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options,
} }
fd = qemu_open(filename, O_WRONLY | O_BINARY); fd = qemu_open(filename, O_WRONLY | O_BINARY);
if (fd < 0) if (fd < 0) {
return -errno;
if (fstat(fd, &stat_buf) < 0)
ret = -errno; 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; 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; ret = -ENOSPC;
}
qemu_close(fd); qemu_close(fd);
return ret; return ret;
@ -1575,14 +1601,19 @@ static int floppy_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp) Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
Error *local_err = NULL;
int ret; int ret;
s->type = FTYPE_FD; s->type = FTYPE_FD;
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */ /* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
ret = raw_open_common(bs, options, flags, O_NONBLOCK); ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
if (ret) if (ret) {
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
}
return ret; return ret;
}
/* close fd so that we can reopen it as needed */ /* close fd so that we can reopen it as needed */
qemu_close(s->fd); qemu_close(s->fd);
@ -1698,11 +1729,17 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp) Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
Error *local_err = NULL;
int ret;
s->type = FTYPE_CD; s->type = FTYPE_CD;
/* open will not fail even if no CD is inserted, so add O_NONBLOCK */ /* 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) 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) static int cdrom_open(BlockDriverState *bs, QDict *options, int flags)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
Error *local_err = NULL;
int ret; int ret;
s->type = FTYPE_CD; s->type = FTYPE_CD;
ret = raw_open_common(bs, options, flags, 0); ret = raw_open_common(bs, options, flags, 0, &local_err);
if (ret) if (ret) {
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
}
return ret; return ret;
}
/* make sure the door isn't locked at this time */ /* make sure the door isn't locked at this time */
ioctl(s->fd, CDIOCALLOW); ioctl(s->fd, CDIOCALLOW);

View File

@ -251,8 +251,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
opts = qemu_opts_create_nofail(&raw_runtime_opts); opts = qemu_opts_create_nofail(&raw_runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err); qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -264,6 +263,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
if ((flags & BDRV_O_NATIVE_AIO) && aio == NULL) { if ((flags & BDRV_O_NATIVE_AIO) && aio == NULL) {
aio = win32_aio_init(); aio = win32_aio_init();
if (aio == NULL) { if (aio == NULL) {
error_setg(errp, "Could not initialize AIO");
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -280,6 +280,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
} else { } else {
ret = -EINVAL; ret = -EINVAL;
} }
error_setg_errno(errp, -ret, "Could not open file");
goto fail; goto fail;
} }
@ -287,6 +288,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
ret = win32_aio_attach(aio, s->hfile); ret = win32_aio_attach(aio, s->hfile);
if (ret < 0) { if (ret < 0) {
CloseHandle(s->hfile); CloseHandle(s->hfile);
error_setg_errno(errp, -ret, "Could not enable AIO");
goto fail; goto fail;
} }
s->aio = aio; 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, fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
0644); 0644);
if (fd < 0) if (fd < 0) {
error_setg_errno(errp, errno, "Could not create file");
return -EIO; return -EIO;
}
set_sparse(fd); set_sparse(fd);
ftruncate(fd, total_size * 512); ftruncate(fd, total_size * 512);
qemu_close(fd); 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); QemuOpts *opts = qemu_opts_create_nofail(&raw_runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err); qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
ret = -EINVAL; ret = -EINVAL;
goto done; goto done;
} }
@ -560,6 +563,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
if (strstart(filename, "/dev/cdrom", NULL)) { if (strstart(filename, "/dev/cdrom", NULL)) {
if (find_cdrom(device_name, sizeof(device_name)) < 0) { if (find_cdrom(device_name, sizeof(device_name)) < 0) {
error_setg(errp, "Could not open CD-ROM drive");
ret = -ENOENT; ret = -ENOENT;
goto done; goto done;
} }
@ -586,8 +590,10 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
int err = GetLastError(); int err = GetLastError();
if (err == ERROR_ACCESS_DENIED) { if (err == ERROR_ACCESS_DENIED) {
error_setg_errno(errp, EACCES, "Could not open device");
ret = -EACCES; ret = -EACCES;
} else { } else {
error_setg(errp, "Could not open device");
ret = -1; ret = -1;
} }
goto done; goto done;

View File

@ -62,7 +62,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int *pnum) 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, 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); ret = bdrv_create_file(filename, options, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
} }
return ret; return ret;
} }

View File

@ -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); 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), .instance_size = sizeof(StreamBlockJob),
.job_type = "stream", .job_type = BLOCK_JOB_TYPE_STREAM,
.set_speed = stream_set_speed, .set_speed = stream_set_speed,
}; };
@ -224,7 +224,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
return; 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) { if (!s) {
return; return;
} }

View File

@ -331,8 +331,7 @@ static int vmdk_reopen_prepare(BDRVReopenState *state,
assert(state->bs != NULL); assert(state->bs != NULL);
if (queue == NULL) { if (queue == NULL) {
error_set(errp, ERROR_CLASS_GENERIC_ERROR, error_setg(errp, "No reopen queue for VMDK extents");
"No reopen queue for VMDK extents");
goto exit; goto exit;
} }
@ -391,22 +390,23 @@ static int vmdk_add_extent(BlockDriverState *bs,
int64_t l1_offset, int64_t l1_backup_offset, int64_t l1_offset, int64_t l1_backup_offset,
uint32_t l1_size, uint32_t l1_size,
int l2_size, uint64_t cluster_sectors, int l2_size, uint64_t cluster_sectors,
VmdkExtent **new_extent) VmdkExtent **new_extent,
Error **errp)
{ {
VmdkExtent *extent; VmdkExtent *extent;
BDRVVmdkState *s = bs->opaque; BDRVVmdkState *s = bs->opaque;
if (cluster_sectors > 0x200000) { if (cluster_sectors > 0x200000) {
/* 0x200000 * 512Bytes = 1GB for one cluster is unrealistic */ /* 0x200000 * 512Bytes = 1GB for one cluster is unrealistic */
error_report("invalid granularity, image may be corrupt"); error_setg(errp, "Invalid granularity, image may be corrupt");
return -EINVAL; return -EFBIG;
} }
if (l1_size > 512 * 1024 * 1024) { if (l1_size > 512 * 1024 * 1024) {
/* Although with big capacity and small l1_entry_sectors, we can get a /* 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. * 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 * Limit it to 512M, which is 16PB for default cluster and L2 table
* size */ * size */
error_report("L1 size too big"); error_setg(errp, "L1 size too big");
return -EFBIG; return -EFBIG;
} }
@ -438,7 +438,8 @@ static int vmdk_add_extent(BlockDriverState *bs,
return 0; 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 ret;
int l1_size, i; 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); l1_size = extent->l1_size * sizeof(uint32_t);
extent->l1_table = g_malloc(l1_size); extent->l1_table = g_malloc(l1_size);
ret = bdrv_pread(extent->file, ret = bdrv_pread(extent->file,
extent->l1_table_offset, extent->l1_table_offset,
extent->l1_table, extent->l1_table,
l1_size); l1_size);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret,
"Could not read l1 table from extent '%s'",
extent->file->filename);
goto fail_l1; goto fail_l1;
} }
for (i = 0; i < extent->l1_size; i++) { 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) { if (extent->l1_backup_table_offset) {
extent->l1_backup_table = g_malloc(l1_size); extent->l1_backup_table = g_malloc(l1_size);
ret = bdrv_pread(extent->file, ret = bdrv_pread(extent->file,
extent->l1_backup_table_offset, extent->l1_backup_table_offset,
extent->l1_backup_table, extent->l1_backup_table,
l1_size); l1_size);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret,
"Could not read l1 backup table from extent '%s'",
extent->file->filename);
goto fail_l1b; goto fail_l1b;
} }
for (i = 0; i < extent->l1_size; i++) { 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, static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
BlockDriverState *file, BlockDriverState *file,
int flags) int flags, Error **errp)
{ {
int ret; int ret;
uint32_t magic; uint32_t magic;
@ -492,6 +499,9 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret,
"Could not read header from file '%s'",
file->filename);
return ret; return ret;
} }
ret = vmdk_add_extent(bs, file, false, 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), le32_to_cpu(header.l1dir_size),
4096, 4096,
le32_to_cpu(header.granularity), le32_to_cpu(header.granularity),
&extent); &extent,
errp);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = vmdk_init_tables(bs, extent); ret = vmdk_init_tables(bs, extent, errp);
if (ret) { if (ret) {
/* free extent allocated by vmdk_add_extent */ /* free extent allocated by vmdk_add_extent */
vmdk_free_last_extent(bs); 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, 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, static int vmdk_open_vmdk4(BlockDriverState *bs,
BlockDriverState *file, BlockDriverState *file,
int flags) int flags, Error **errp)
{ {
int ret; int ret;
uint32_t magic; uint32_t magic;
@ -529,12 +540,14 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
if (ret < 0) { if (ret < 0) {
return ret; error_setg_errno(errp, -ret,
"Could not read header from file '%s'",
file->filename);
} }
if (header.capacity == 0) { if (header.capacity == 0) {
uint64_t desc_offset = le64_to_cpu(header.desc_offset); uint64_t desc_offset = le64_to_cpu(header.desc_offset);
if (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, l1_size,
le32_to_cpu(header.num_gtes_per_gt), le32_to_cpu(header.num_gtes_per_gt),
le64_to_cpu(header.granularity), le64_to_cpu(header.granularity),
&extent); &extent,
errp);
if (ret < 0) { if (ret < 0) {
return ret; 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->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;
extent->version = le32_to_cpu(header.version); extent->version = le32_to_cpu(header.version);
extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN; 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) { if (ret) {
/* free extent allocated by vmdk_add_extent */ /* free extent allocated by vmdk_add_extent */
vmdk_free_last_extent(bs); 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 */ /* Open an extent file and append to bs array */
static int vmdk_open_sparse(BlockDriverState *bs, static int vmdk_open_sparse(BlockDriverState *bs,
BlockDriverState *file, BlockDriverState *file,
int flags) int flags, Error **errp)
{ {
uint32_t magic; uint32_t magic;
@ -674,10 +688,10 @@ static int vmdk_open_sparse(BlockDriverState *bs,
magic = be32_to_cpu(magic); magic = be32_to_cpu(magic);
switch (magic) { switch (magic) {
case VMDK3_MAGIC: case VMDK3_MAGIC:
return vmdk_open_vmfs_sparse(bs, file, flags); return vmdk_open_vmfs_sparse(bs, file, flags, errp);
break; break;
case VMDK4_MAGIC: case VMDK4_MAGIC:
return vmdk_open_vmdk4(bs, file, flags); return vmdk_open_vmdk4(bs, file, flags, errp);
break; break;
default: default:
return -EMEDIUMTYPE; return -EMEDIUMTYPE;
@ -686,7 +700,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
} }
static int vmdk_parse_extents(const char *desc, 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; int ret;
char access[11]; char access[11];
@ -697,7 +711,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
int64_t flat_offset; int64_t flat_offset;
char extent_path[PATH_MAX]; char extent_path[PATH_MAX];
BlockDriverState *extent_file; BlockDriverState *extent_file;
Error *local_err = NULL;
while (*p) { while (*p) {
/* parse extent line: /* parse extent line:
@ -712,9 +725,11 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
goto next_line; goto next_line;
} else if (!strcmp(type, "FLAT")) { } else if (!strcmp(type, "FLAT")) {
if (ret != 5 || flat_offset < 0) { if (ret != 5 || flat_offset < 0) {
error_setg(errp, "Invalid extent lines: \n%s", p);
return -EINVAL; return -EINVAL;
} }
} else if (ret != 4) { } else if (ret != 4) {
error_setg(errp, "Invalid extent lines: \n%s", p);
return -EINVAL; return -EINVAL;
} }
@ -728,10 +743,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
path_combine(extent_path, sizeof(extent_path), path_combine(extent_path, sizeof(extent_path),
desc_file_path, fname); desc_file_path, fname);
ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags, ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags,
&local_err); errp);
if (ret) { if (ret) {
qerror_report_err(local_err);
error_free(local_err);
return ret; return ret;
} }
@ -741,35 +754,37 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
VmdkExtent *extent; VmdkExtent *extent;
ret = vmdk_add_extent(bs, extent_file, true, sectors, 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) { if (ret < 0) {
return ret; return ret;
} }
extent->flat_start_offset = flat_offset << 9; extent->flat_start_offset = flat_offset << 9;
} else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) { } else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/ /* 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) { if (ret) {
bdrv_unref(extent_file); bdrv_unref(extent_file);
return ret; return ret;
} }
} else { } else {
fprintf(stderr, error_setg(errp, "Unsupported extent type '%s'", type);
"VMDK: Not supported extent type \"%s\""".\n", type);
return -ENOTSUP; return -ENOTSUP;
} }
next_line: next_line:
/* move to next line */ /* move to next line */
while (*p && *p != '\n') { while (*p) {
if (*p == '\n') {
p++;
break;
}
p++; p++;
} }
p++;
} }
return 0; return 0;
} }
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
uint64_t desc_offset) uint64_t desc_offset, Error **errp)
{ {
int ret; int ret;
char *buf = NULL; char *buf = NULL;
@ -798,13 +813,12 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
strcmp(ct, "vmfsSparse") && strcmp(ct, "vmfsSparse") &&
strcmp(ct, "twoGbMaxExtentSparse") && strcmp(ct, "twoGbMaxExtentSparse") &&
strcmp(ct, "twoGbMaxExtentFlat")) { strcmp(ct, "twoGbMaxExtentFlat")) {
fprintf(stderr, error_setg(errp, "Unsupported image type '%s'", ct);
"VMDK: Not supported image type \"%s\""".\n", ct);
ret = -ENOTSUP; ret = -ENOTSUP;
goto exit; goto exit;
} }
s->desc_offset = 0; s->desc_offset = 0;
ret = vmdk_parse_extents(buf, bs, bs->file->filename); ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
exit: exit:
g_free(buf); g_free(buf);
return ret; return ret;
@ -816,10 +830,10 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
int ret; int ret;
BDRVVmdkState *s = bs->opaque; 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; s->desc_offset = 0x200;
} else { } else {
ret = vmdk_open_desc_file(bs, flags, 0); ret = vmdk_open_desc_file(bs, flags, 0, errp);
if (ret) { if (ret) {
goto fail; goto fail;
} }
@ -1286,8 +1300,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
VmdkMetaData m_data; VmdkMetaData m_data;
if (sector_num > bs->total_sectors) { if (sector_num > bs->total_sectors) {
fprintf(stderr, error_report("Wrong offset: sector_num=0x%" PRIx64
"(VMDK) Wrong offset: sector_num=0x%" PRIx64
" total_sectors=0x%" PRIx64 "\n", " total_sectors=0x%" PRIx64 "\n",
sector_num, bs->total_sectors); sector_num, bs->total_sectors);
return -EIO; return -EIO;
@ -1307,9 +1320,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
if (extent->compressed) { if (extent->compressed) {
if (ret == VMDK_OK) { if (ret == VMDK_OK) {
/* Refuse write to allocated cluster for streamOptimized */ /* Refuse write to allocated cluster for streamOptimized */
fprintf(stderr, error_report("Could not write to allocated cluster"
"VMDK: can't write to allocated cluster" " for streamOptimized");
" for streamOptimized\n");
return -EIO; return -EIO;
} else { } else {
/* allocate */ /* 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, 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; const char *p, *q;
if (filename == NULL || !strlen(filename)) { if (filename == NULL || !strlen(filename)) {
fprintf(stderr, "Vmdk: no filename provided.\n"); error_setg(errp, "No filename provided");
return VMDK_ERROR; return VMDK_ERROR;
} }
p = strrchr(filename, '/'); p = strrchr(filename, '/');
@ -1595,9 +1607,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
"ddb.geometry.heads = \"%d\"\n" "ddb.geometry.heads = \"%d\"\n"
"ddb.geometry.sectors = \"63\"\n" "ddb.geometry.sectors = \"63\"\n"
"ddb.adapterType = \"%s\"\n"; "ddb.adapterType = \"%s\"\n";
Error *local_err = NULL;
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
return -EINVAL; return -EINVAL;
} }
/* Read out options */ /* Read out options */
@ -1623,7 +1634,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
strcmp(adapter_type, "buslogic") && strcmp(adapter_type, "buslogic") &&
strcmp(adapter_type, "lsilogic") && strcmp(adapter_type, "lsilogic") &&
strcmp(adapter_type, "legacyESX")) { 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; return -EINVAL;
} }
if (strcmp(adapter_type, "ide") != 0) { if (strcmp(adapter_type, "ide") != 0) {
@ -1639,7 +1650,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
strcmp(fmt, "twoGbMaxExtentSparse") && strcmp(fmt, "twoGbMaxExtentSparse") &&
strcmp(fmt, "twoGbMaxExtentFlat") && strcmp(fmt, "twoGbMaxExtentFlat") &&
strcmp(fmt, "streamOptimized")) { strcmp(fmt, "streamOptimized")) {
fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt); error_setg(errp, "Unknown subformat: '%s'", fmt);
return -EINVAL; return -EINVAL;
} }
split = !(strcmp(fmt, "twoGbMaxExtentFlat") && 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"; desc_extent_line = "RW %lld SPARSE \"%s\"\n";
} }
if (flat && backing_file) { 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; return -ENOTSUP;
} }
if (backing_file) { if (backing_file) {
BlockDriverState *bs = bdrv_new(""); 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) { if (ret != 0) {
qerror_report_err(local_err);
error_free(local_err);
bdrv_unref(bs); bdrv_unref(bs);
return ret; return ret;
} }

View File

@ -38,6 +38,8 @@
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qapi/qmp/types.h" #include "qapi/qmp/types.h"
#include "qapi-visit.h"
#include "qapi/qmp-output-visitor.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qmp-commands.h" #include "qmp-commands.h"
@ -89,6 +91,10 @@ void blockdev_mark_auto_del(BlockDriverState *bs)
{ {
DriveInfo *dinfo = drive_get_by_blockdev(bs); DriveInfo *dinfo = drive_get_by_blockdev(bs);
if (dinfo && !dinfo->enable_auto_del) {
return;
}
if (bs->job) { if (bs->job) {
block_job_cancel(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) static void drive_uninit(DriveInfo *dinfo)
{ {
qemu_opts_del(dinfo->opts); if (dinfo->opts) {
qemu_opts_del(dinfo->opts);
}
bdrv_unref(dinfo->bdrv); bdrv_unref(dinfo->bdrv);
g_free(dinfo->id); g_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next); QTAILQ_REMOVE(&drives, dinfo, next);
@ -263,7 +272,7 @@ static void bdrv_put_ref_bh_schedule(BlockDriverState *bs)
qemu_bh_schedule(s->bh); 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")) { if (!strcmp(buf, "ignore")) {
return BLOCKDEV_ON_ERROR_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")) { } else if (!strcmp(buf, "report")) {
return BLOCKDEV_ON_ERROR_REPORT; return BLOCKDEV_ON_ERROR_REPORT;
} else { } else {
error_report("'%s' invalid %s error action", error_setg(errp, "'%s' invalid %s error action",
buf, is_read ? "read" : "write"); buf, is_read ? "read" : "write");
return -1; return -1;
} }
} }
@ -296,23 +305,19 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
return true; return true;
} }
static DriveInfo *blockdev_init(QemuOpts *all_opts, typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
BlockInterfaceType block_default_type)
/* Takes the ownership of bs_opts */
static DriveInfo *blockdev_init(QDict *bs_opts,
BlockInterfaceType type,
Error **errp)
{ {
const char *buf; const char *buf;
const char *file = NULL; const char *file = NULL;
const char *serial; 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 ro = 0;
int bdrv_flags = 0; int bdrv_flags = 0;
int on_read_error, on_write_error; int on_read_error, on_write_error;
const char *devaddr;
DriveInfo *dinfo; DriveInfo *dinfo;
ThrottleConfig cfg; ThrottleConfig cfg;
int snapshot = 0; int snapshot = 0;
@ -320,30 +325,22 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
int ret; int ret;
Error *error = NULL; Error *error = NULL;
QemuOpts *opts; QemuOpts *opts;
QDict *bs_opts;
const char *id; const char *id;
bool has_driver_specific_opts; bool has_driver_specific_opts;
BlockDriver *drv = NULL; BlockDriver *drv = NULL;
translation = BIOS_ATA_TRANSLATION_AUTO; /* Check common options by copying from bs_opts to opts, all other options
media = MEDIA_DISK; * stay in bs_opts for processing by bdrv_open(). */
id = qdict_get_try_str(bs_opts, "id");
/* Check common options by copying from all_opts to opts, all other options
* are stored in bs_opts. */
id = qemu_opts_id(all_opts);
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error); opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
if (error_is_set(&error)) { if (error_is_set(&error)) {
qerror_report_err(error); error_propagate(errp, error);
error_free(error);
return NULL; return NULL;
} }
bs_opts = qdict_new();
qemu_opts_to_qdict(all_opts, bs_opts);
qemu_opts_absorb_qdict(opts, bs_opts, &error); qemu_opts_absorb_qdict(opts, bs_opts, &error);
if (error_is_set(&error)) { if (error_is_set(&error)) {
qerror_report_err(error); error_propagate(errp, error);
error_free(error);
return NULL; return NULL;
} }
@ -354,14 +351,6 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
has_driver_specific_opts = !!qdict_size(bs_opts); has_driver_specific_opts = !!qdict_size(bs_opts);
/* extract parameters */ /* 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); snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
ro = qemu_opt_get_bool(opts, "read-only", 0); ro = qemu_opt_get_bool(opts, "read-only", 0);
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false); 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"); file = qemu_opt_get(opts, "file");
serial = qemu_opt_get(opts, "serial"); 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 ((buf = qemu_opt_get(opts, "discard")) != NULL) {
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) { if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
error_report("invalid discard option"); error_setg(errp, "invalid discard option");
return NULL; return NULL;
} }
} }
@ -454,7 +382,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
} else if (!strcmp(buf, "threads")) { } else if (!strcmp(buf, "threads")) {
/* this is the default */ /* this is the default */
} else { } else {
error_report("invalid aio option"); error_setg(errp, "invalid aio option");
return NULL; return NULL;
} }
} }
@ -468,13 +396,9 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
return NULL; return NULL;
} }
drv = bdrv_find_whitelisted_format(buf, ro); drv = bdrv_find_format(buf);
if (!drv) { if (!drv) {
if (!ro && bdrv_find_whitelisted_format(buf, !ro)) { error_setg(errp, "'%s' invalid format", buf);
error_report("'%s' can be only used as read-only device.", buf);
} else {
error_report("'%s' invalid format", buf);
}
return NULL; 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); cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
if (!check_throttle_config(&cfg, &error)) { if (!check_throttle_config(&cfg, &error)) {
error_report("%s", error_get_pretty(error)); error_propagate(errp, error);
error_free(error);
return NULL; 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; on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
if ((buf = qemu_opt_get(opts, "werror")) != NULL) { if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { 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; return NULL;
} }
on_write_error = parse_block_error_action(buf, 0); on_write_error = parse_block_error_action(buf, 0, &error);
if (on_write_error < 0) { if (error_is_set(&error)) {
error_propagate(errp, error);
return NULL; return NULL;
} }
} }
@ -541,92 +459,20 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
return NULL; return NULL;
} }
on_read_error = parse_block_error_action(buf, 1); on_read_error = parse_block_error_action(buf, 1, &error);
if (on_read_error < 0) { if (error_is_set(&error)) {
error_propagate(errp, error);
return NULL; 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 */ /* init */
dinfo = g_malloc0(sizeof(*dinfo)); dinfo = g_malloc0(sizeof(*dinfo));
if ((buf = qemu_opts_id(opts)) != NULL) { dinfo->id = g_strdup(qemu_opts_id(opts));
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->bdrv = bdrv_new(dinfo->id); dinfo->bdrv = bdrv_new(dinfo->id);
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
dinfo->bdrv->read_only = ro; dinfo->bdrv->read_only = ro;
dinfo->devaddr = devaddr;
dinfo->type = type; 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; dinfo->refcount = 1;
if (serial != NULL) { if (serial != NULL) {
dinfo->serial = g_strdup(serial); dinfo->serial = g_strdup(serial);
@ -641,36 +487,6 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
bdrv_set_io_limits(dinfo->bdrv, &cfg); 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 (!file || !*file) {
if (has_driver_specific_opts) { if (has_driver_specific_opts) {
file = NULL; file = NULL;
@ -692,29 +508,15 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
bdrv_flags |= BDRV_O_INCOMING; 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; 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); QINCREF(bs_opts);
ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error); ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
if (ret < 0) { if (ret < 0) {
error_report("could not open disk image %s: %s", error_setg(errp, "could not open disk image %s: %s",
file ?: dinfo->id, error_get_pretty(error)); file ?: dinfo->id, error_get_pretty(error));
error_free(error);
goto err; 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) DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
{ {
const char *value; 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 */ /* Change legacy command line options into QMP ones */
qemu_opt_rename(all_opts, "iops", "throttling.iops-total"); 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"); 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) 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; flags = state->old_bs->open_flags;
/* create new image w/backing file */ /* 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); 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) static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
{ {
BlockJobInfoList **prev = opaque; BlockJobInfoList **prev = opaque;
@ -2077,42 +2232,6 @@ QemuOptsList qemu_common_drive_opts = {
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
.desc = { .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", .name = "snapshot",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
.help = "enable/disable snapshot mode", .help = "enable/disable snapshot mode",
@ -2156,10 +2275,6 @@ QemuOptsList qemu_common_drive_opts = {
.name = "werror", .name = "werror",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "write error action", .help = "write error action",
},{
.name = "addr",
.type = QEMU_OPT_STRING,
.help = "pci address (virtio only)",
},{ },{
.name = "read-only", .name = "read-only",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
@ -2220,10 +2335,6 @@ QemuOptsList qemu_common_drive_opts = {
.name = "copy-on-read", .name = "copy-on-read",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
.help = "copy read data from backing file into image file", .help = "copy read data from backing file into image file",
},{
.name = "boot",
.type = QEMU_OPT_BOOL,
.help = "(deprecated, ignored)",
}, },
{ /* end of list */ } { /* end of list */ }
}, },

View File

@ -35,7 +35,7 @@
#include "qmp-commands.h" #include "qmp-commands.h"
#include "qemu/timer.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, int64_t speed, BlockDriverCompletionFunc *cb,
void *opaque, Error **errp) void *opaque, Error **errp)
{ {
@ -48,8 +48,8 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
bdrv_ref(bs); bdrv_ref(bs);
bdrv_set_in_use(bs, 1); bdrv_set_in_use(bs, 1);
job = g_malloc0(job_type->instance_size); job = g_malloc0(driver->instance_size);
job->job_type = job_type; job->driver = driver;
job->bs = bs; job->bs = bs;
job->cb = cb; job->cb = cb;
job->opaque = opaque; job->opaque = opaque;
@ -87,11 +87,11 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
{ {
Error *local_err = NULL; Error *local_err = NULL;
if (!job->job_type->set_speed) { if (!job->driver->set_speed) {
error_set(errp, QERR_NOT_SUPPORTED); error_set(errp, QERR_NOT_SUPPORTED);
return; return;
} }
job->job_type->set_speed(job, speed, &local_err); job->driver->set_speed(job, speed, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; 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) 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); error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
return; return;
} }
job->job_type->complete(job, errp); job->driver->complete(job, errp);
} }
void block_job_pause(BlockJob *job) void block_job_pause(BlockJob *job)
@ -143,8 +143,8 @@ bool block_job_is_cancelled(BlockJob *job)
void block_job_iostatus_reset(BlockJob *job) void block_job_iostatus_reset(BlockJob *job)
{ {
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
if (job->job_type->iostatus_reset) { if (job->driver->iostatus_reset) {
job->job_type->iostatus_reset(job); 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 *block_job_query(BlockJob *job)
{ {
BlockJobInfo *info = g_new0(BlockJobInfo, 1); 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->device = g_strdup(bdrv_get_device_name(job->bs));
info->len = job->len; info->len = job->len;
info->busy = job->busy; info->busy = job->busy;
@ -236,7 +236,7 @@ QObject *qobject_from_block_job(BlockJob *job)
"'len': %" PRId64 "," "'len': %" PRId64 ","
"'offset': %" PRId64 "," "'offset': %" PRId64 ","
"'speed': %" PRId64 " }", "'speed': %" PRId64 " }",
job->job_type->job_type, BlockJobType_lookup[job->driver->job_type],
bdrv_get_device_name(job->bs), bdrv_get_device_name(job->bs),
job->len, job->len,
job->offset, job->offset,

View File

@ -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 members should always be added to the end of the dictionary to preserve
backwards compatibility. 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 === === Enumeration types ===
An enumeration type is a dictionary containing a single key whose value is a An enumeration type is a dictionary containing a single key whose value is a

View File

@ -355,3 +355,6 @@ Snapshot table entry:
variable: Unique ID string for the snapshot (not null terminated) variable: Unique ID string for the snapshot (not null terminated)
variable: Name of 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.

View File

@ -624,6 +624,11 @@ static int m25p80_init(SSISlave *ss)
if (dinfo && dinfo->bdrv) { if (dinfo && dinfo->bdrv) {
DB_PRINT_L(0, "Binding to IF_MTD drive\n"); DB_PRINT_L(0, "Binding to IF_MTD drive\n");
s->bdrv = dinfo->bdrv; 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 */ /* FIXME: Move to late init */
if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size, if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
BDRV_SECTOR_SIZE))) { BDRV_SECTOR_SIZE))) {

View File

@ -830,6 +830,11 @@ static int blk_connect(struct XenDevice *xendev)
/* setup via qemu cmdline -> already setup for us */ /* setup via qemu cmdline -> already setup for us */
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
blkdev->bs = blkdev->dinfo->bdrv; 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 /* blkdev->bs is not create by us, we get a reference
* so we can bdrv_unref() unconditionally */ * so we can bdrv_unref() unconditionally */
bdrv_ref(blkdev->bs); bdrv_ref(blkdev->bs);

View File

@ -1198,7 +1198,15 @@ void ahci_reset(AHCIState *s)
int i; int i;
s->control_regs.irqstatus = 0; 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++) { for (i = 0; i < s->ports; i++) {
pr = &s->dev[i].port_regs; pr = &s->dev[i].port_regs;

View File

@ -255,6 +255,10 @@ static int milkymist_memcard_init(SysBusDevice *dev)
dinfo = drive_get_next(IF_SD); dinfo = drive_get_next(IF_SD);
s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false); s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
if (s->card == NULL) {
return -1;
}
s->enabled = dinfo ? bdrv_is_inserted(dinfo->bdrv) : 0; s->enabled = dinfo ? bdrv_is_inserted(dinfo->bdrv) : 0;
memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s, memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s,

View File

@ -593,6 +593,9 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
/* Instantiate the storage */ /* Instantiate the storage */
s->card = sd_init(bd, false); s->card = sd_init(bd, false);
if (s->card == NULL) {
exit(1);
}
return s; return s;
} }
@ -618,6 +621,9 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
/* Instantiate the storage */ /* Instantiate the storage */
s->card = sd_init(bd, false); s->card = sd_init(bd, false);
if (s->card == NULL) {
exit(1);
}
s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0]; s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0];
sd_set_cb(s->card, NULL, s->cdet); sd_set_cb(s->card, NULL, s->cdet);

View File

@ -491,6 +491,10 @@ static int pl181_init(SysBusDevice *sbd)
qdev_init_gpio_out(dev, s->cardstatus, 2); qdev_init_gpio_out(dev, s->cardstatus, 2);
dinfo = drive_get_next(IF_SD); dinfo = drive_get_next(IF_SD);
s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false); s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
if (s->card == NULL) {
return -1;
}
return 0; return 0;
} }

View File

@ -539,6 +539,9 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
/* Instantiate the actual storage */ /* Instantiate the actual storage */
s->card = sd_init(bd, false); s->card = sd_init(bd, false);
if (s->card == NULL) {
exit(1);
}
register_savevm(NULL, "pxa2xx_mmci", 0, 0, register_savevm(NULL, "pxa2xx_mmci", 0, 0,
pxa2xx_mmci_save, pxa2xx_mmci_load, s); pxa2xx_mmci_save, pxa2xx_mmci_load, s);

View File

@ -494,6 +494,11 @@ SDState *sd_init(BlockDriverState *bs, bool is_spi)
{ {
SDState *sd; 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 = (SDState *) g_malloc0(sizeof(SDState));
sd->buf = qemu_blockalign(bs, 512); sd->buf = qemu_blockalign(bs, 512);
sd->spi = is_spi; sd->spi = is_spi;

View File

@ -1166,6 +1166,9 @@ static void sdhci_initfn(Object *obj)
di = drive_get_next(IF_SD); di = drive_get_next(IF_SD);
s->card = sd_init(di ? di->bdrv : NULL, false); 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->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]; s->ro_cb = qemu_allocate_irqs(sdhci_card_readonly_cb, s, 1)[0];
sd_set_cb(s->card, s->ro_cb, s->eject_cb); sd_set_cb(s->card, s->ro_cb, s->eject_cb);

View File

@ -246,6 +246,9 @@ static int ssi_sd_init(SSISlave *dev)
s->mode = SSI_SD_CMD; s->mode = SSI_SD_CMD;
dinfo = drive_get_next(IF_SD); dinfo = drive_get_next(IF_SD);
s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, true); 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); register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
return 0; return 0;
} }

View File

@ -84,6 +84,9 @@ typedef struct BlockDevOps {
/* BDRV_BLOCK_DATA: data is read from bs->file or another file /* BDRV_BLOCK_DATA: data is read from bs->file or another file
* BDRV_BLOCK_ZERO: sectors read as zero * BDRV_BLOCK_ZERO: sectors read as zero
* BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data * 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 * 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. * 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_DATA 1
#define BDRV_BLOCK_ZERO 2 #define BDRV_BLOCK_ZERO 2
#define BDRV_BLOCK_OFFSET_VALID 4 #define BDRV_BLOCK_OFFSET_VALID 4
#define BDRV_BLOCK_RAW 8
#define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK #define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK
typedef enum { typedef enum {
@ -244,6 +248,20 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
int bdrv_amend_options(BlockDriverState *bs_new, QEMUOptionParameter *options); 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 */ /* async block I/O */
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector, typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
int sector_num); int sector_num);
@ -335,6 +353,7 @@ int bdrv_get_flags(BlockDriverState *bs);
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors); const uint8_t *buf, int nb_sectors);
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
void bdrv_round_to_clusters(BlockDriverState *bs, void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int64_t sector_num, int nb_sectors,
int64_t *cluster_sector_num, int64_t *cluster_sector_num,

View File

@ -67,6 +67,12 @@ typedef struct BdrvTrackedRequest {
struct BlockDriver { struct BlockDriver {
const char *format_name; const char *format_name;
int instance_size; 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)(const uint8_t *buf, int buf_size, const char *filename);
int (*bdrv_probe_device)(const char *filename); int (*bdrv_probe_device)(const char *filename);
@ -168,6 +174,7 @@ struct BlockDriver {
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
const char *snapshot_name); const char *snapshot_name);
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov, int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos); int64_t pos);

View File

@ -28,16 +28,16 @@
#include "block/block.h" #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 */ /** Derived BlockJob struct size */
size_t instance_size; size_t instance_size;
/** String describing the operation, part of query-block-jobs QMP API */ /** 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 */ /** Optional callback for job types that support setting a speed limit */
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp); void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
@ -50,7 +50,7 @@ typedef struct BlockJobType {
* manually. * manually.
*/ */
void (*complete)(BlockJob *job, Error **errp); void (*complete)(BlockJob *job, Error **errp);
} BlockJobType; } BlockJobDriver;
/** /**
* BlockJob: * BlockJob:
@ -59,7 +59,7 @@ typedef struct BlockJobType {
*/ */
struct BlockJob { struct BlockJob {
/** The job type, including the job vtable. */ /** The job type, including the job vtable. */
const BlockJobType *job_type; const BlockJobDriver *driver;
/** The block device on which the job is operating. */ /** The block device on which the job is operating. */
BlockDriverState *bs; BlockDriverState *bs;
@ -128,7 +128,7 @@ struct BlockJob {
* This function is not part of the public job interface; it should be * This function is not part of the public job interface; it should be
* called from a wrapper that is specific to the job type. * 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, int64_t speed, BlockDriverCompletionFunc *cb,
void *opaque, Error **errp); void *opaque, Error **errp);

View File

@ -42,6 +42,8 @@ BlockStats *bdrv_query_stats(const BlockDriverState *bs);
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
QEMUSnapshotInfo *sn); 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, void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
ImageInfo *info); ImageInfo *info);
#endif #endif

View File

@ -142,6 +142,7 @@ void qemu_opts_loc_restore(QemuOpts *opts);
int qemu_opts_set(QemuOptsList *list, const char *id, int qemu_opts_set(QemuOptsList *list, const char *id,
const char *name, const char *value); const char *name, const char *value);
const char *qemu_opts_id(QemuOpts *opts); 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_del(QemuOpts *opts);
void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp); void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp);
int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname); int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname);

View File

@ -37,6 +37,7 @@ struct DriveInfo {
int bus; int bus;
int unit; int unit;
int auto_del; /* see blockdev_mark_auto_del() */ int auto_del; /* see blockdev_mark_auto_del() */
bool enable_auto_del; /* Only for legacy drive_init() */
int media_cd; int media_cd;
int cyls, heads, secs, trans; int cyls, heads, secs, trans;
QemuOpts *opts; QemuOpts *opts;

View File

@ -209,6 +209,34 @@
'date-sec': 'int', 'date-nsec': 'int', 'date-sec': 'int', 'date-nsec': 'int',
'vm-clock-sec': 'int', 'vm-clock-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: # @ImageInfo:
# #
@ -238,6 +266,9 @@
# #
# @backing-image: #optional info of the backing image (since 1.6) # @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 # Since: 1.3
# #
## ##
@ -248,7 +279,8 @@
'*cluster-size': 'int', '*encrypted': 'bool', '*cluster-size': 'int', '*encrypted': 'bool',
'*backing-filename': 'str', '*full-backing-filename': 'str', '*backing-filename': 'str', '*full-backing-filename': 'str',
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'], '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
'*backing-image': 'ImageInfo' } } '*backing-image': 'ImageInfo',
'*format-specific': 'ImageInfoSpecific' } }
## ##
# @ImageCheck: # @ImageCheck:
@ -1365,6 +1397,24 @@
{ 'enum': 'MirrorSyncMode', { 'enum': 'MirrorSyncMode',
'data': ['top', 'full', 'none'] } '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: # @BlockJobInfo:
# #
@ -3902,3 +3952,239 @@
## ##
{ 'command': 'query-rx-filter', 'data': { '*name': 'str' }, { 'command': 'query-rx-filter', 'data': { '*name': 'str' },
'returns': ['RxFilterInfo'] } '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' } }

View File

@ -10,6 +10,7 @@
#include "qemu-io.h" #include "qemu-io.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qapi.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#define CMD_NOFILE_OK 0x01 #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) static int info_f(BlockDriverState *bs, int argc, char **argv)
{ {
BlockDriverInfo bdi; BlockDriverInfo bdi;
ImageInfoSpecific *spec_info;
char s1[64], s2[64]; char s1[64], s2[64];
int ret; int ret;
@ -1699,6 +1701,13 @@ static int info_f(BlockDriverState *bs, int argc, char **argv)
printf("cluster size: %s\n", s1); printf("cluster size: %s\n", s1);
printf("vm state offset: %s\n", s2); 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; return 0;
} }

View File

@ -16,6 +16,8 @@
#include "qemu-io.h" #include "qemu-io.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "trace/control.h" #include "trace/control.h"
@ -44,7 +46,7 @@ static const cmdinfo_t close_cmd = {
.oneline = "close the current open file", .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; Error *local_err = NULL;
@ -54,7 +56,7 @@ static int openfile(char *name, int flags, int growable)
} }
if (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, fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
error_get_pretty(local_err)); error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
@ -63,7 +65,7 @@ static int openfile(char *name, int flags, int growable)
} else { } else {
qemuio_bs = bdrv_new("hda"); 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, fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
error_get_pretty(local_err)); error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
@ -89,7 +91,8 @@ static void open_help(void)
" -r, -- open file read-only\n" " -r, -- open file read-only\n"
" -s, -- use snapshot file\n" " -s, -- use snapshot file\n"
" -n, -- disable host cache\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"); "\n");
} }
@ -102,19 +105,30 @@ static const cmdinfo_t open_cmd = {
.argmin = 1, .argmin = 1,
.argmax = -1, .argmax = -1,
.flags = CMD_NOFILE_OK, .flags = CMD_NOFILE_OK,
.args = "[-Crsn] [path]", .args = "[-Crsn] [-o options] [path]",
.oneline = "open the file specified by path", .oneline = "open the file specified by path",
.help = open_help, .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) static int open_f(BlockDriverState *bs, int argc, char **argv)
{ {
int flags = 0; int flags = 0;
int readonly = 0; int readonly = 0;
int growable = 0; int growable = 0;
int c; int c;
QemuOpts *qopts;
QDict *opts = NULL;
while ((c = getopt(argc, argv, "snrg")) != EOF) { while ((c = getopt(argc, argv, "snrgo:")) != EOF) {
switch (c) { switch (c) {
case 's': case 's':
flags |= BDRV_O_SNAPSHOT; flags |= BDRV_O_SNAPSHOT;
@ -128,6 +142,15 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
case 'g': case 'g':
growable = 1; growable = 1;
break; 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: default:
return qemuio_command_usage(&open_cmd); 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 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) static int quit_f(BlockDriverState *bs, int argc, char **argv)
@ -418,7 +441,7 @@ int main(int argc, char **argv)
} }
if ((argc - optind) == 1) { if ((argc - optind) == 1) {
openfile(argv[optind], flags, growable); openfile(argv[optind], flags, growable, NULL);
} }
command_loop(); command_loop();

View File

@ -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 EQMP

View File

@ -71,7 +71,7 @@ def generate_struct_fields(members):
c_name=c_var(argname)) c_name=c_var(argname))
if structured: if structured:
push_indent() push_indent()
ret += generate_struct("", argname, argentry) ret += generate_struct({ "field": argname, "data": argentry})
pop_indent() pop_indent()
else: else:
ret += mcgen(''' ret += mcgen('''
@ -81,13 +81,22 @@ def generate_struct_fields(members):
return ret 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(''' ret = mcgen('''
struct %(name)s struct %(name)s
{ {
''', ''',
name=structname) name=structname)
if base:
ret += generate_struct_fields({'base': base})
ret += generate_struct_fields(members) ret += generate_struct_fields(members)
if len(fieldname): if len(fieldname):
@ -417,7 +426,7 @@ if do_builtins:
for expr in exprs: for expr in exprs:
ret = "\n" ret = "\n"
if expr.has_key('type'): 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") ret += generate_type_cleanup_decl(expr['type'] + "List")
fdef.write(generate_type_cleanup(expr['type'] + "List") + "\n") fdef.write(generate_type_cleanup(expr['type'] + "List") + "\n")
ret += generate_type_cleanup_decl(expr['type']) ret += generate_type_cleanup_decl(expr['type'])

View File

@ -17,7 +17,7 @@ import os
import getopt import getopt
import errno 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 = [] substructs = []
ret = '' ret = ''
full_name = name if not fn_prefix else "%s_%s" % (name, fn_prefix) full_name = name if not fn_prefix else "%s_%s" % (name, fn_prefix)
@ -42,6 +42,19 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error *
name=name, full_name=full_name) name=name, full_name=full_name)
push_indent() 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): for argname, argentry, optional, structured in parse_args(members):
if optional: if optional:
ret += mcgen(''' ret += mcgen('''
@ -120,8 +133,13 @@ if (!err) {
''') ''')
return ret return ret
def generate_visit_struct(name, members): def generate_visit_struct(expr):
ret = generate_visit_struct_fields(name, "", "", members)
name = expr['type']
members = expr['data']
base = expr.get('base')
ret = generate_visit_struct_fields(name, "", "", members, base)
ret += mcgen(''' ret += mcgen('''
@ -472,7 +490,7 @@ if do_builtins:
for expr in exprs: for expr in exprs:
if expr.has_key('type'): 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']) ret += generate_visit_list(expr['type'], expr['data'])
fdef.write(ret) fdef.write(ret)

View File

@ -196,6 +196,7 @@ check-help:
@echo " make check-qapi-schema Run QAPI schema tests" @echo " make check-qapi-schema Run QAPI schema tests"
@echo " make check-block Run block tests" @echo " make check-block Run block tests"
@echo " make check-report.html Generates an HTML test report" @echo " make check-report.html Generates an HTML test report"
@echo " make check-clean Clean the tests"
@echo @echo
@echo "Please note that HTML reports do not regenerate if the unit tests" @echo "Please note that HTML reports do not regenerate if the unit tests"
@echo "has not changed." @echo "has not changed."
@ -252,8 +253,10 @@ check-report.html: check-report.xml
# Other tests # Other tests
QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF)
.PHONY: check-tests/qemu-iotests-quick.sh .PHONY: check-tests/qemu-iotests-quick.sh
check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) 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 .PHONY: check-tests/test-qapi.py
@ -268,12 +271,21 @@ $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json
# Consolidated targets # 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-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y))
check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
check-unit: $(patsubst %,check-%, $(check-unit-y)) check-unit: $(patsubst %,check-%, $(check-unit-y))
check-block: $(patsubst %,check-%, $(check-block-y)) check-block: $(patsubst %,check-%, $(check-block-y))
check: check-qapi-schema check-unit check-qtest 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/*.d)
-include $(wildcard tests/libqos/*.d) -include $(wildcard tests/libqos/*.d)

View File

@ -139,7 +139,10 @@ QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on 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 Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information 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 QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol
Testing: -drive file.filename=foo:bar Testing: -drive file.filename=foo:bar
QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: 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 *** done

View File

@ -47,30 +47,34 @@ capacity_offset=16
granularity_offset=20 granularity_offset=20
grain_table_size_offset=44 grain_table_size_offset=44
echo "=== Testing invalid granularity ==="
echo echo
echo "=== Testing invalid granularity ==="
_make_test_img 64M _make_test_img 64M
poke_file "$TEST_IMG" "$granularity_offset" "\xff\xff\xff\xff\xff\xff\xff\xff" 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 { $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
echo "=== Testing too big L2 table size ==="
echo echo
echo "=== Testing too big L2 table size ==="
_make_test_img 64M _make_test_img 64M
poke_file "$TEST_IMG" "$grain_table_size_offset" "\xff\xff\xff\xff" 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 { $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
echo "=== Testing too big L1 table size ==="
echo echo
echo "=== Testing too big L1 table size ==="
_make_test_img 64M _make_test_img 64M
poke_file "$TEST_IMG" "$capacity_offset" "\xff\xff\xff\xff" poke_file "$TEST_IMG" "$capacity_offset" "\xff\xff\xff\xff"
poke_file "$TEST_IMG" "$grain_table_size_offset" "\x01\x00\x00\x00" 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 { $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
echo "=== Testing monolithicFlat creation and opening ==="
echo echo
echo "=== Testing monolithicFlat creation and opening ==="
IMGOPTS="subformat=monolithicFlat" _make_test_img 2G IMGOPTS="subformat=monolithicFlat" _make_test_img 2G
$QEMU_IMG info $TEST_IMG | _filter_testdir $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 # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full

View File

@ -1,27 +1,29 @@
QA output created by 059 QA output created by 059
=== Testing invalid granularity === === Testing invalid granularity ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 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: 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
no file open, try 'help open' 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 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
L2 table size too big 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 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' no file open, try 'help open'
=== Testing too big L1 table size === === Testing too big L1 table size ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
L1 size too big qemu-io: can't open device TEST_DIR/t.vmdk: L1 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' 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 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648
image: TEST_DIR/t.vmdk image: TEST_DIR/t.vmdk
file format: vmdk file format: vmdk
virtual size: 2.0G (2147483648 bytes) virtual size: 2.0G (2147483648 bytes)
disk size: 4.0K 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 *** done

View File

@ -21,10 +21,10 @@
# creator # creator
owner=mreitz@redhat.com owner=mreitz@redhat.com
seq=`basename $0` seq="$(basename $0)"
echo "QA output created by $seq" echo "QA output created by $seq"
here=`pwd` here="$PWD"
tmp=/tmp/$$ tmp=/tmp/$$
status=1 # failure is the default! 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) rb_offset=131072 # 0x20000 (XXX: just an assumption)
l1_offset=196608 # 0x30000 (XXX: just an assumption) l1_offset=196608 # 0x30000 (XXX: just an assumption)
l2_offset=262144 # 0x40000 (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" 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
echo "=== Testing L2 reference into L1 ===" echo "=== Testing L2 reference into L1 ==="
echo echo
@ -65,16 +71,18 @@ _check_test_img
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Try to write something, thereby forcing the corrupt bit to be set # 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 # The corrupt bit must now be set
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Try to open the image R/W (which should fail) # Try to open the image R/W (which should fail)
$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | _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) # 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 # 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?) # 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" poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00"
_check_test_img _check_test_img
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features ./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 ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Try to fix it # Try to fix it
@ -102,9 +110,34 @@ _check_test_img -r all
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Look if it's really really fixed # 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 ./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 # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full

View File

@ -12,7 +12,6 @@ qcow2: Preventing invalid write on metadata (overlaps with active L1 table); ima
write failed: Input/output error write failed: Input/output error
incompatible_features 0x2 incompatible_features 0x2
qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write qemu-io: can't open device 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 read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@ -40,4 +39,43 @@ incompatible_features 0x0
wrote 512/512 bytes at offset 0 wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
incompatible_features 0x0 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 *** done

62
tests/qemu-iotests/064 Executable file
View File

@ -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

View File

@ -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

125
tests/qemu-iotests/065 Executable file
View File

@ -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'])

View File

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

63
tests/qemu-iotests/066 Executable file
View File

@ -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

View File

@ -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

133
tests/qemu-iotests/067 Executable file
View File

@ -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

View File

@ -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

View File

@ -45,6 +45,7 @@ valgrind=false
rm -f $tmp.list $tmp.tmp $tmp.sed rm -f $tmp.list $tmp.tmp $tmp.sed
export IMGFMT=raw export IMGFMT=raw
export IMGFMT_GENERIC=true
export IMGPROTO=file export IMGPROTO=file
export IMGOPTS="" export IMGOPTS=""
export QEMU_IO_OPTIONS="" export QEMU_IO_OPTIONS=""
@ -133,6 +134,7 @@ check options
-qed test qed -qed test qed
-vdi test vdi -vdi test vdi
-vpc test vpc -vpc test vpc
-vhdx test vhdx
-vmdk test vmdk -vmdk test vmdk
-rbd test rbd -rbd test rbd
-sheepdog test sheepdog -sheepdog test sheepdog
@ -195,6 +197,12 @@ testlist options
xpand=false xpand=false
;; ;;
-vhdx)
IMGFMT=vhdx
xpand=false
IMGFMT_GENERIC=false
;;
-rbd) -rbd)
IMGPROTO=rbd IMGPROTO=rbd
xpand=false xpand=false

View File

@ -159,5 +159,13 @@ _filter_qemu()
-e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' -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 # make sure this script returns success
/bin/true /bin/true

View File

@ -197,12 +197,30 @@ _check_test_img()
_img_info() _img_info()
{ {
discard=0
regex_json_spec_start='^ *"format-specific": \{'
$QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \ $QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$IMGFMT#IMGFMT#g" \ -e "s#$IMGFMT#IMGFMT#g" \
-e "/^disk size:/ D" \ -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() _get_pids_by_name()
@ -321,7 +339,7 @@ _fail()
_supported_fmt() _supported_fmt()
{ {
for f; do for f; do
if [ "$f" = "$IMGFMT" -o "$f" = "generic" ]; then if [ "$f" = "$IMGFMT" -o "$f" = "generic" -a "$IMGFMT_GENERIC" = "true" ]; then
return return
fi fi
done done

View File

@ -69,3 +69,7 @@
061 rw auto 061 rw auto
062 rw auto 062 rw auto
063 rw auto 063 rw auto
064 rw auto
065 rw auto
066 rw auto
067 rw auto

View File

@ -49,6 +49,10 @@ def qemu_img_verbose(*args):
'''Run qemu-img without suppressing its output and return the exit code''' '''Run qemu-img without suppressing its output and return the exit code'''
return subprocess.call(qemu_img_args + list(args)) 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): def qemu_io(*args):
'''Run qemu-io and return the stdout data''' '''Run qemu-io and return the stdout data'''
args = qemu_io_args + list(args) args = qemu_io_args + list(args)

View File

@ -834,6 +834,12 @@ const char *qemu_opts_id(QemuOpts *opts)
return opts->id; 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) void qemu_opts_del(QemuOpts *opts)
{ {
QemuOpt *opt; QemuOpt *opt;