Explorar o código

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>
Anthony Liguori %!s(int64=12) %!d(string=hai) anos
pai
achega
33c6cae44e
Modificáronse 66 ficheiros con 2109 adicións e 516 borrados
  1. 0 1
      Makefile
  2. 48 5
      block.c
  3. 3 3
      block/backup.c
  4. 4 4
      block/blkdebug.c
  5. 7 6
      block/blkverify.c
  6. 3 3
      block/commit.c
  7. 3 3
      block/mirror.c
  8. 124 0
      block/qapi.c
  9. 3 5
      block/qcow2-cache.c
  10. 7 9
      block/qcow2-cluster.c
  11. 18 19
      block/qcow2-refcount.c
  12. 18 12
      block/qcow2-snapshot.c
  13. 117 12
      block/qcow2.c
  14. 23 7
      block/qcow2.h
  15. 57 15
      block/raw-posix.c
  16. 11 5
      block/raw-win32.c
  17. 4 3
      block/raw_bsd.c
  18. 3 3
      block/stream.c
  19. 70 57
      block/vmdk.c
  20. 394 283
      blockdev.c
  21. 11 11
      blockjob.c
  22. 17 0
      docs/qapi-code-gen.txt
  23. 3 0
      docs/specs/qcow2.txt
  24. 5 0
      hw/block/m25p80.c
  25. 5 0
      hw/block/xen_disk.c
  26. 9 1
      hw/ide/ahci.c
  27. 4 0
      hw/sd/milkymist-memcard.c
  28. 6 0
      hw/sd/omap_mmc.c
  29. 4 0
      hw/sd/pl181.c
  30. 3 0
      hw/sd/pxa2xx_mmci.c
  31. 5 0
      hw/sd/sd.c
  32. 3 0
      hw/sd/sdhci.c
  33. 3 0
      hw/sd/ssi-sd.c
  34. 19 0
      include/block/block.h
  35. 7 0
      include/block/block_int.h
  36. 7 7
      include/block/blockjob.h
  37. 2 0
      include/block/qapi.h
  38. 1 0
      include/qemu/option.h
  39. 1 0
      include/sysemu/blockdev.h
  40. 287 1
      qapi-schema.json
  41. 9 0
      qemu-io-cmds.c
  42. 31 8
      qemu-io.c
  43. 55 0
      qmp-commands.hx
  44. 12 3
      scripts/qapi-types.py
  45. 22 4
      scripts/qapi-visit.py
  46. 14 2
      tests/Makefile
  47. 5 2
      tests/qemu-iotests/051.out
  48. 8 4
      tests/qemu-iotests/059
  49. 10 8
      tests/qemu-iotests/059.out
  50. 40 7
      tests/qemu-iotests/060
  51. 39 1
      tests/qemu-iotests/060.out
  52. 62 0
      tests/qemu-iotests/064
  53. 14 0
      tests/qemu-iotests/064.out
  54. 125 0
      tests/qemu-iotests/065
  55. 5 0
      tests/qemu-iotests/065.out
  56. 63 0
      tests/qemu-iotests/066
  57. 13 0
      tests/qemu-iotests/066.out
  58. 133 0
      tests/qemu-iotests/067
  59. 80 0
      tests/qemu-iotests/067.out
  60. 8 0
      tests/qemu-iotests/common
  61. 8 0
      tests/qemu-iotests/common.filter
  62. 20 2
      tests/qemu-iotests/common.rc
  63. 4 0
      tests/qemu-iotests/group
  64. 4 0
      tests/qemu-iotests/iotests.py
  65. BIN=BIN
      tests/qemu-iotests/sample_images/iotest-dynamic-1G.vhdx.bz2
  66. 6 0
      util/qemu-option.c

+ 0 - 1
Makefile

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

+ 48 - 5
block.c

@@ -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)) {
-        bdrv_enable_copy_on_read(bs);
+    if (flags & BDRV_O_COPY_ON_READ) {
+        if (!bs->read_only) {
+            bdrv_enable_copy_on_read(bs);
+        } else {
+            error_setg(errp, "Can't use copy-on-read on read-only device");
+            return -EINVAL;
+        }
     }
     }
 
 
     if (filename != NULL) {
     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;
+}

+ 3 - 3
block/backup.c

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

+ 4 - 4
block/blkdebug.c

@@ -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_free(local_err);
+        error_propagate(errp, 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_free(local_err);
+        error_propagate(errp, local_err);
         goto fail;
         goto fail;
     }
     }
 
 

+ 7 - 6
block/blkverify.c

@@ -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_free(local_err);
+        error_propagate(errp, 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_free(local_err);
+        error_propagate(errp, 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_free(local_err);
+        error_propagate(errp, 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)

+ 3 - 3
block/commit.c

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

+ 3 - 3
block/mirror.c

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

+ 124 - 0
block/qapi.c

@@ -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);
+    }
 }
 }

+ 3 - 5
block/qcow2-cache.c

@@ -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,
-                QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_BLOCK,
+        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
                 c->entries[i].offset, s->cluster_size);
                 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,
-                QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2,
+        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
                 c->entries[i].offset, s->cluster_size);
                 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);
     }
     }
 
 

+ 7 - 9
block/qcow2-cluster.c

@@ -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,
-                                        new_l1_table_offset, new_l1_size2);
+    ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset,
+                                        new_l1_size2);
     if (ret < 0) {
     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,
-            QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
+    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
             s->l1_table_offset + 8 * l1_start_index, sizeof(buf));
             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,
-                                                offset, s->cluster_size);
+            ret = qcow2_pre_write_overlap_check(bs, 0, 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 &
-                        ~(QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2), l2_offset,
+                ret = qcow2_pre_write_overlap_check(bs,
+                        QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, l2_offset,
                         s->cluster_size);
                         s->cluster_size);
                 if (ret < 0) {
                 if (ret < 0) {
                     goto fail;
                     goto fail;

+ 18 - 19
block/qcow2-refcount.c

@@ -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,
-                            nb_clusters << s->cluster_bits, type);
+    case QCOW2_CLUSTER_ZERO:
+        if (l2_entry & L2E_OFFSET_MASK) {
+            qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
+                                nb_clusters << s->cluster_bits, type);
+        }
         break;
         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,
-                    QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2, l2_offset,
-                    s->cluster_size);
+            ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
+                                                l2_offset, s->cluster_size);
             if (ret < 0) {
             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,
-            QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_TABLE,
+    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_TABLE,
             s->refcount_table_offset + rt_start_index * sizeof(uint64_t),
             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,
-            s->cluster_size);
+    ret = qcow2_pre_write_overlap_check(bs, 0, new_offset, 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
- * of QCow2MetadataOverlap values).
+ * The ign parameter specifies what checks not to perform (being a bitmask of
+ * QCow2MetadataOverlap values), i.e., what sections to ignore.
  *
  *
  * Returns:
  * 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,
-                            l1_sz * sizeof(uint64_t) / BDRV_SECTOR_SIZE);
-
+            ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2);
             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;

+ 18 - 12
block/qcow2-snapshot.c

@@ -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,
-                                        snapshots_size);
+    ret = qcow2_pre_write_overlap_check(bs, 0, offset, 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,
-            sn->l1_table_offset, s->l1_size * sizeof(uint64_t));
+    ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset,
+                                        s->l1_size * sizeof(uint64_t));
     if (ret < 0) {
     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,
-            QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
-            s->l1_table_offset, cur_l1_bytes);
+    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
+                                        s->l1_table_offset, cur_l1_bytes);
     if (ret < 0) {
     if (ret < 0) {
         goto fail;
         goto fail;
     }
     }

+ 117 - 12
block/qcow2.c

@@ -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,
-                cluster_offset, out_len);
+        ret = qcow2_pre_write_overlap_check(bs, 0, 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,

+ 23 - 7
block/qcow2.h

@@ -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_REFCOUNT_TABLE | QCOW2_OL_REFCOUNT_BLOCK | \
-     QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_INACTIVE_L1)
+    (QCOW2_OL_CONSTANT | QCOW2_OL_ACTIVE_L2 | QCOW2_OL_REFCOUNT_BLOCK | \
+     QCOW2_OL_INACTIVE_L1)
 
 
-/* The default checks to perform */
-#define QCOW2_OL_DEFAULT QCOW2_OL_CACHED
+/* Perform all overlap checks */
+#define QCOW2_OL_ALL \
+    (QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2)
 
 
 #define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
 #define 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 */

+ 57 - 15
block/raw-posix.c

@@ -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_free(local_err);
+        error_propagate(errp, 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)
-        return -errno;
+    if (fd < 0) {
+        ret = -errno;
+        error_setg_errno(errp, -ret, "Could not open device");
+        return ret;
+    }
 
 
-    if (fstat(fd, &stat_buf) < 0)
+    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 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);
-    if (ret)
+    ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
+    if (ret) {
+        if (error_is_set(&local_err)) {
+            error_propagate(errp, local_err);
+        }
         return ret;
         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);
-    if (ret)
+    ret = raw_open_common(bs, options, flags, 0, &local_err);
+    if (ret) {
+        if (error_is_set(&local_err)) {
+            error_propagate(errp, local_err);
+        }
         return ret;
         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);

+ 11 - 5
block/raw-win32.c

@@ -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_free(local_err);
+        error_propagate(errp, 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_free(local_err);
+        error_propagate(errp, 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;

+ 4 - 3
block/raw_bsd.c

@@ -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_free(local_err);
+        error_propagate(errp, local_err);
     }
     }
     return ret;
     return ret;
 }
 }

+ 3 - 3
block/stream.c

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

+ 70 - 57
block/vmdk.c

@@ -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,
-                 "No reopen queue for VMDK extents");
+        error_setg(errp, "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");
-        return -EINVAL;
+        error_setg(errp, "Invalid granularity, image may be corrupt");
+        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,
-                    l1_size);
+                     extent->l1_table_offset,
+                     extent->l1_table,
+                     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,
-                        l1_size);
+                         extent->l1_backup_table_offset,
+                         extent->l1_backup_table,
+                         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,
-                "VMDK: Not supported extent type \"%s\""".\n", type);
+            error_setg(errp, "Unsupported extent type '%s'", 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,
-                "VMDK: Not supported image type \"%s\""".\n", ct);
+        error_setg(errp, "Unsupported image type '%s'", 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,
-                "(VMDK) Wrong offset: sector_num=0x%" PRIx64
+        error_report("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,
-                        "VMDK: can't write to allocated cluster"
-                        " for streamOptimized\n");
+                error_report("Could not write to allocated cluster"
+                              " for streamOptimized");
                 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;
         }
         }

+ 394 - 283
blockdev.c

@@ -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",
-                     buf, is_read ? "read" : "write");
+        error_setg(errp, "'%s' invalid %s error action",
+                   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,
-                                BlockInterfaceType block_default_type)
+typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
+
+/* Takes the ownership of bs_opts */
+static DriveInfo *blockdev_init(QDict *bs_opts,
+                                BlockInterfaceType type,
+                                Error **errp)
 {
 {
     const char *buf;
     const char *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;
-    media = MEDIA_DISK;
-
-    /* Check common options by copying from all_opts to opts, all other options
-     * are stored in bs_opts. */
-    id = qemu_opts_id(all_opts);
+    /* Check common options by copying from bs_opts to opts, all other options
+     * stay in bs_opts for processing by bdrv_open(). */
+    id = qdict_get_try_str(bs_opts, "id");
     opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
     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_free(error);
+        error_propagate(errp, 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_free(error);
+        error_propagate(errp, 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_report("'%s' can be only used as read-only device.", buf);
-            } else {
-                error_report("'%s' invalid format", buf);
-            }
+            error_setg(errp, "'%s' invalid format", buf);
             return NULL;
             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_free(error);
+        error_propagate(errp, 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);
-        if (on_write_error < 0) {
+        on_write_error = parse_block_error_action(buf, 0, &error);
+        if (error_is_set(&error)) {
+            error_propagate(errp, error);
             return NULL;
             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);
-        if (on_read_error < 0) {
-            return NULL;
-        }
-    }
-
-    if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
-        if (type != IF_VIRTIO) {
-            error_report("addr is not supported by this bus type");
+        on_read_error = parse_block_error_action(buf, 1, &error);
+        if (error_is_set(&error)) {
+            error_propagate(errp, error);
             return NULL;
             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(buf);
-    } else {
-        /* no id supplied -> create one */
-        dinfo->id = g_malloc0(32);
-        if (type == IF_IDE || type == IF_SCSI)
-            mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
-        if (max_devs)
-            snprintf(dinfo->id, 32, "%s%i%s%i",
-                     if_name[type], bus_id, mediastr, unit_id);
-        else
-            snprintf(dinfo->id, 32, "%s%s%i",
-                     if_name[type], mediastr, unit_id);
-    }
+    dinfo->id = g_strdup(qemu_opts_id(opts));
     dinfo->bdrv = bdrv_new(dinfo->id);
     dinfo->bdrv = 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",
-                     file ?: dinfo->id, error_get_pretty(error));
+        error_setg(errp, "could not open disk image %s: %s",
+                   file ?: dinfo->id, error_get_pretty(error));
+        error_free(error);
         goto err;
         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 */ }
     },
     },

+ 11 - 11
blockjob.c

@@ -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->job_type      = job_type;
+    job = g_malloc0(driver->instance_size);
+    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) {
-        job->job_type->iostatus_reset(job);
+    if (job->driver->iostatus_reset) {
+        job->driver->iostatus_reset(job);
     }
     }
 }
 }
 
 
@@ -209,7 +209,7 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
 BlockJobInfo *block_job_query(BlockJob *job)
 BlockJobInfo *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,

+ 17 - 0
docs/qapi-code-gen.txt

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

+ 3 - 0
docs/specs/qcow2.txt

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

+ 5 - 0
hw/block/m25p80.c

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

+ 5 - 0
hw/block/xen_disk.c

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

+ 9 - 1
hw/ide/ahci.c

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

+ 4 - 0
hw/sd/milkymist-memcard.c

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

+ 6 - 0
hw/sd/omap_mmc.c

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

+ 4 - 0
hw/sd/pl181.c

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

+ 3 - 0
hw/sd/pxa2xx_mmci.c

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

+ 5 - 0
hw/sd/sd.c

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

+ 3 - 0
hw/sd/sdhci.c

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

+ 3 - 0
hw/sd/ssi-sd.c

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

+ 19 - 0
include/block/block.h

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

+ 7 - 0
include/block/block_int.h

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

+ 7 - 7
include/block/blockjob.h

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

+ 2 - 0
include/block/qapi.h

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

+ 1 - 0
include/qemu/option.h

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

+ 1 - 0
include/sysemu/blockdev.h

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

+ 287 - 1
qapi-schema.json

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

+ 9 - 0
qemu-io-cmds.c

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

+ 31 - 8
qemu-io.c

@@ -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();
 
 

+ 55 - 0
qmp-commands.hx

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

+ 12 - 3
scripts/qapi-types.py

@@ -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 @@ def maybe_open(really, name, opt):
 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'])

+ 22 - 4
scripts/qapi-visit.py

@@ -17,7 +17,7 @@
 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 @@ def generate_visit_struct_fields(name, field_prefix, fn_prefix, members):
         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 @@ def generate_visit_struct_body(field_prefix, name, members):
 ''')
 ''')
     return ret
     return ret
 
 
-def generate_visit_struct(name, members):
-    ret = generate_visit_struct_fields(name, "", "", members)
+def generate_visit_struct(expr):
+
+    name = expr['type']
+    members = expr['data']
+    base = expr.get('base')
+
+    ret = generate_visit_struct_fields(name, "", "", members, base)
 
 
     ret += mcgen('''
     ret += mcgen('''
 
 
@@ -472,7 +490,7 @@ def maybe_open(really, name, opt):
 
 
 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)
 
 

+ 14 - 2
tests/Makefile

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

+ 5 - 2
tests/qemu-iotests/051.out

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

+ 8 - 4
tests/qemu-iotests/059

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

+ 10 - 8
tests/qemu-iotests/059.out

@@ -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: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
+qemu-io: can't open device TEST_DIR/t.vmdk: Invalid granularity, image may be corrupt
 no file open, try 'help open'
 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: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
+qemu-io: can't open device TEST_DIR/t.vmdk: L1 size too big
 no file open, try 'help open'
 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

+ 40 - 7
tests/qemu-iotests/060

@@ -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,8 +110,33 @@ _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
+
+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
 ./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"

+ 39 - 1
tests/qemu-iotests/060.out

@@ -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 - 0
tests/qemu-iotests/064

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

+ 14 - 0
tests/qemu-iotests/064.out

@@ -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 - 0
tests/qemu-iotests/065

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

+ 5 - 0
tests/qemu-iotests/065.out

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

+ 63 - 0
tests/qemu-iotests/066

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

+ 13 - 0
tests/qemu-iotests/066.out

@@ -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 - 0
tests/qemu-iotests/067

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

+ 80 - 0
tests/qemu-iotests/067.out

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

+ 8 - 0
tests/qemu-iotests/common

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

+ 8 - 0
tests/qemu-iotests/common.filter

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

+ 20 - 2
tests/qemu-iotests/common.rc

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

+ 4 - 0
tests/qemu-iotests/group

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

+ 4 - 0
tests/qemu-iotests/iotests.py

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

BIN=BIN
tests/qemu-iotests/sample_images/iotest-dynamic-1G.vhdx.bz2


+ 6 - 0
util/qemu-option.c

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