diff options
-rw-r--r-- | block.c | 164 | ||||
-rw-r--r-- | block/backup-top.c | 31 | ||||
-rw-r--r-- | block/file-posix.c | 67 | ||||
-rw-r--r-- | block/iscsi.c | 56 | ||||
-rw-r--r-- | block/nbd.c | 14 | ||||
-rw-r--r-- | block/qapi.c | 15 | ||||
-rw-r--r-- | block/qcow2.c | 11 | ||||
-rw-r--r-- | blockdev.c | 8 | ||||
-rw-r--r-- | docs/interop/qcow2.txt | 64 | ||||
-rw-r--r-- | docs/interop/qemu-img.rst | 9 | ||||
-rw-r--r-- | include/block/block.h | 2 | ||||
-rw-r--r-- | include/block/qapi.h | 4 | ||||
-rw-r--r-- | monitor/hmp-cmds.c | 2 | ||||
-rw-r--r-- | qapi/block-core.json | 7 | ||||
-rw-r--r-- | qemu-img-cmds.hx | 4 | ||||
-rw-r--r-- | qemu-img.c | 28 | ||||
-rwxr-xr-x | tests/qemu-iotests/122 | 14 | ||||
-rw-r--r-- | tests/qemu-iotests/122.out | 5 | ||||
-rwxr-xr-x | tests/qemu-iotests/139 | 3 | ||||
-rwxr-xr-x | tests/qemu-iotests/147 | 2 | ||||
-rwxr-xr-x | tests/qemu-iotests/259 | 62 | ||||
-rw-r--r-- | tests/qemu-iotests/259.out | 14 | ||||
-rwxr-xr-x | tests/qemu-iotests/279 | 7 | ||||
-rwxr-xr-x | tests/qemu-iotests/284 | 97 | ||||
-rw-r--r-- | tests/qemu-iotests/284.out | 62 | ||||
-rwxr-xr-x | tests/qemu-iotests/286 | 76 | ||||
-rw-r--r-- | tests/qemu-iotests/286.out | 8 | ||||
-rw-r--r-- | tests/qemu-iotests/group | 3 |
28 files changed, 659 insertions, 180 deletions
@@ -532,20 +532,139 @@ out: return ret; } -int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) +/** + * Helper function for bdrv_create_file_fallback(): Resize @blk to at + * least the given @minimum_size. + * + * On success, return @blk's actual length. + * Otherwise, return -errno. + */ +static int64_t create_file_fallback_truncate(BlockBackend *blk, + int64_t minimum_size, Error **errp) { - BlockDriver *drv; + Error *local_err = NULL; + int64_t size; + int ret; + + ret = blk_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, &local_err); + if (ret < 0 && ret != -ENOTSUP) { + error_propagate(errp, local_err); + return ret; + } + + size = blk_getlength(blk); + if (size < 0) { + error_free(local_err); + error_setg_errno(errp, -size, + "Failed to inquire the new image file's length"); + return size; + } + + if (size < minimum_size) { + /* Need to grow the image, but we failed to do that */ + error_propagate(errp, local_err); + return -ENOTSUP; + } + + error_free(local_err); + local_err = NULL; + + return size; +} + +/** + * Helper function for bdrv_create_file_fallback(): Zero the first + * sector to remove any potentially pre-existing image header. + */ +static int create_file_fallback_zero_first_sector(BlockBackend *blk, + int64_t current_size, + Error **errp) +{ + int64_t bytes_to_clear; + int ret; + + bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE); + if (bytes_to_clear) { + ret = blk_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to clear the new image's first sector"); + return ret; + } + } + + return 0; +} + +static int bdrv_create_file_fallback(const char *filename, BlockDriver *drv, + QemuOpts *opts, Error **errp) +{ + BlockBackend *blk; + QDict *options = qdict_new(); + int64_t size = 0; + char *buf = NULL; + PreallocMode prealloc; Error *local_err = NULL; int ret; + size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); + prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, + PREALLOC_MODE_OFF, &local_err); + g_free(buf); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + + if (prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, "Unsupported preallocation mode '%s'", + PreallocMode_str(prealloc)); + return -ENOTSUP; + } + + qdict_put_str(options, "driver", drv->format_name); + + blk = blk_new_open(filename, NULL, options, + BDRV_O_RDWR | BDRV_O_RESIZE, errp); + if (!blk) { + error_prepend(errp, "Protocol driver '%s' does not support image " + "creation, and opening the image failed: ", + drv->format_name); + return -EINVAL; + } + + size = create_file_fallback_truncate(blk, size, errp); + if (size < 0) { + ret = size; + goto out; + } + + ret = create_file_fallback_zero_first_sector(blk, size, errp); + if (ret < 0) { + goto out; + } + + ret = 0; +out: + blk_unref(blk); + return ret; +} + +int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) +{ + BlockDriver *drv; + drv = bdrv_find_protocol(filename, true, errp); if (drv == NULL) { return -ENOENT; } - ret = bdrv_create(drv, filename, opts, &local_err); - error_propagate(errp, local_err); - return ret; + if (drv->bdrv_co_create_opts) { + return bdrv_create(drv, filename, opts, errp); + } else { + return bdrv_create_file_fallback(filename, drv, opts, errp); + } } /** @@ -1444,6 +1563,24 @@ QemuOptsList bdrv_runtime_opts = { }, }; +static QemuOptsList fallback_create_opts = { + .name = "fallback-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(fallback_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_PREALLOC, + .type = QEMU_OPT_STRING, + .help = "Preallocation mode (allowed values: off)" + }, + { /* end of list */ } + } +}; + /* * Common part for opening disk images and files * @@ -4807,14 +4944,15 @@ BlockDriverState *bdrv_find_node(const char *node_name) } /* Put this QMP function here so it can access the static graph_bdrv_states. */ -BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp) +BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, + Error **errp) { BlockDeviceInfoList *list, *entry; BlockDriverState *bs; list = NULL; QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { - BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, errp); + BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, flat, errp); if (!info) { qapi_free_BlockDeviceInfoList(list); return NULL; @@ -5771,15 +5909,13 @@ void bdrv_img_create(const char *filename, const char *fmt, return; } - if (!proto_drv->create_opts) { - error_setg(errp, "Protocol driver '%s' does not support image creation", - proto_drv->format_name); - return; - } - /* Create parameter list */ create_opts = qemu_opts_append(create_opts, drv->create_opts); - create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + if (proto_drv->create_opts) { + create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + } else { + create_opts = qemu_opts_append(create_opts, &fallback_create_opts); + } opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); diff --git a/block/backup-top.c b/block/backup-top.c index fa78f3256d..1bfb360bd3 100644 --- a/block/backup-top.c +++ b/block/backup-top.c @@ -48,11 +48,17 @@ static coroutine_fn int backup_top_co_preadv( } static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, - uint64_t bytes) + uint64_t bytes, BdrvRequestFlags flags) { BDRVBackupTopState *s = bs->opaque; - uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size); - uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size); + uint64_t off, end; + + if (flags & BDRV_REQ_WRITE_UNCHANGED) { + return 0; + } + + off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size); + end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size); return block_copy(s->bcs, off, end - off, NULL); } @@ -60,7 +66,7 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - int ret = backup_top_cbw(bs, offset, bytes); + int ret = backup_top_cbw(bs, offset, bytes, 0); if (ret < 0) { return ret; } @@ -71,7 +77,7 @@ static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs, static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - int ret = backup_top_cbw(bs, offset, bytes); + int ret = backup_top_cbw(bs, offset, bytes, flags); if (ret < 0) { return ret; } @@ -84,11 +90,9 @@ static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs, uint64_t bytes, QEMUIOVector *qiov, int flags) { - if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) { - int ret = backup_top_cbw(bs, offset, bytes); - if (ret < 0) { - return ret; - } + int ret = backup_top_cbw(bs, offset, bytes, flags); + if (ret < 0) { + return ret; } return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); @@ -196,8 +200,13 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, return NULL; } - top->total_sectors = source->total_sectors; state = top->opaque; + top->total_sectors = source->total_sectors; + top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & source->supported_write_flags); + top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & + source->supported_zero_flags); bdrv_ref(target); state->target = bdrv_attach_child(top, target, "target", &child_file, errp); diff --git a/block/file-posix.c b/block/file-posix.c index ab82ee1a67..6345477112 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3477,67 +3477,6 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, return raw_do_pwrite_zeroes(bs, offset, bytes, flags, true); } -static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts, - Error **errp) -{ - int fd; - int ret = 0; - struct stat stat_buf; - int64_t total_size = 0; - bool has_prefix; - - /* This function is used by both protocol block drivers and therefore either - * of these prefixes may be given. - * The return value has to be stored somewhere, otherwise this is an error - * due to -Werror=unused-value. */ - has_prefix = - strstart(filename, "host_device:", &filename) || - strstart(filename, "host_cdrom:" , &filename); - - (void)has_prefix; - - ret = raw_normalize_devicepath(&filename, errp); - if (ret < 0) { - return ret; - } - - /* Read out options */ - total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - - fd = qemu_open(filename, O_WRONLY | O_BINARY); - if (fd < 0) { - ret = -errno; - error_setg_errno(errp, -ret, "Could not open device"); - return ret; - } - - if (fstat(fd, &stat_buf) < 0) { - ret = -errno; - error_setg_errno(errp, -ret, "Could not stat device"); - } else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) { - error_setg(errp, - "The given file is neither a block nor a character device"); - ret = -ENODEV; - } else if (lseek(fd, 0, SEEK_END) < total_size) { - error_setg(errp, "Device is too small"); - ret = -ENOSPC; - } - - if (!ret && total_size) { - uint8_t buf[BDRV_SECTOR_SIZE] = { 0 }; - int64_t zero_size = MIN(BDRV_SECTOR_SIZE, total_size); - if (lseek(fd, 0, SEEK_SET) == -1) { - ret = -errno; - } else { - ret = qemu_write_full(fd, buf, zero_size); - ret = ret == zero_size ? 0 : -errno; - } - } - qemu_close(fd); - return ret; -} - static BlockDriver bdrv_host_device = { .format_name = "host_device", .protocol_name = "host_device", @@ -3550,8 +3489,6 @@ static BlockDriver bdrv_host_device = { .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, - .bdrv_co_create_opts = hdev_co_create_opts, - .create_opts = &raw_create_opts, .mutable_opts = mutable_opts, .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, @@ -3678,8 +3615,6 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, - .bdrv_co_create_opts = hdev_co_create_opts, - .create_opts = &raw_create_opts, .mutable_opts = mutable_opts, .bdrv_co_invalidate_cache = raw_co_invalidate_cache, @@ -3812,8 +3747,6 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, - .bdrv_co_create_opts = hdev_co_create_opts, - .create_opts = &raw_create_opts, .mutable_opts = mutable_opts, .bdrv_co_preadv = raw_co_preadv, diff --git a/block/iscsi.c b/block/iscsi.c index c8feaa2f0e..682abd8e09 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2164,58 +2164,6 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opts, - Error **errp) -{ - int ret = 0; - int64_t total_size = 0; - BlockDriverState *bs; - IscsiLun *iscsilun = NULL; - QDict *bs_options; - Error *local_err = NULL; - - bs = bdrv_new(); - - /* Read out options */ - total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - bs->opaque = g_new0(struct IscsiLun, 1); - iscsilun = bs->opaque; - - bs_options = qdict_new(); - iscsi_parse_filename(filename, bs_options, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - } else { - ret = iscsi_open(bs, bs_options, 0, NULL); - } - qobject_unref(bs_options); - - if (ret != 0) { - goto out; - } - iscsi_detach_aio_context(bs); - if (iscsilun->type != TYPE_DISK) { - ret = -ENODEV; - goto out; - } - if (bs->total_sectors < total_size) { - ret = -ENOSPC; - goto out; - } - - ret = 0; -out: - if (iscsilun->iscsi != NULL) { - iscsi_destroy_context(iscsilun->iscsi); - } - g_free(bs->opaque); - bs->opaque = NULL; - bdrv_unref(bs); - return ret; -} - static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { IscsiLun *iscsilun = bs->opaque; @@ -2486,8 +2434,6 @@ static BlockDriver bdrv_iscsi = { .bdrv_parse_filename = iscsi_parse_filename, .bdrv_file_open = iscsi_open, .bdrv_close = iscsi_close, - .bdrv_co_create_opts = iscsi_co_create_opts, - .create_opts = &iscsi_create_opts, .bdrv_reopen_prepare = iscsi_reopen_prepare, .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, @@ -2525,8 +2471,6 @@ static BlockDriver bdrv_iser = { .bdrv_parse_filename = iscsi_parse_filename, .bdrv_file_open = iscsi_open, .bdrv_close = iscsi_close, - .bdrv_co_create_opts = iscsi_co_create_opts, - .create_opts = &iscsi_create_opts, .bdrv_reopen_prepare = iscsi_reopen_prepare, .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, diff --git a/block/nbd.c b/block/nbd.c index d085554f21..6d3b22f844 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -70,6 +70,7 @@ typedef struct BDRVNBDState { CoMutex send_mutex; CoQueue free_sema; Coroutine *connection_co; + Coroutine *teardown_co; QemuCoSleepState *connection_co_sleep_ns_state; bool drained; bool wait_drained_end; @@ -203,7 +204,15 @@ static void nbd_teardown_connection(BlockDriverState *bs) qemu_co_sleep_wake(s->connection_co_sleep_ns_state); } } - BDRV_POLL_WHILE(bs, s->connection_co); + if (qemu_in_coroutine()) { + s->teardown_co = qemu_coroutine_self(); + /* connection_co resumes us when it terminates */ + qemu_coroutine_yield(); + s->teardown_co = NULL; + } else { + BDRV_POLL_WHILE(bs, s->connection_co); + } + assert(!s->connection_co); } static bool nbd_client_connecting(BDRVNBDState *s) @@ -395,6 +404,9 @@ static coroutine_fn void nbd_connection_entry(void *opaque) s->ioc = NULL; } + if (s->teardown_co) { + aio_co_wake(s->teardown_co); + } aio_wait_kick(); } diff --git a/block/qapi.c b/block/qapi.c index 9a5d0c9b27..afd9f3b4a7 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -42,7 +42,9 @@ #include "qemu/cutils.h" BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, - BlockDriverState *bs, Error **errp) + BlockDriverState *bs, + bool flat, + Error **errp) { ImageInfo **p_image_info; BlockDriverState *bs0; @@ -156,6 +158,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, return NULL; } + /* stop gathering data for flat output */ + if (flat) { + break; + } + if (bs0->drv && bs0->backing) { info->backing_file_depth++; bs0 = bs0->backing->bs; @@ -389,7 +396,7 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, if (bs && bs->drv) { info->has_inserted = true; - info->inserted = bdrv_block_device_info(blk, bs, errp); + info->inserted = bdrv_block_device_info(blk, bs, false, errp); if (info->inserted == NULL) { goto err; } @@ -657,7 +664,7 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) char *sizing = NULL; if (!sn) { - qemu_printf("%-10s%-20s%7s%20s%15s", + qemu_printf("%-10s%-20s%11s%20s%15s", "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); } else { ti = sn->date_sec; @@ -672,7 +679,7 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) (int)(secs % 60), (int)((sn->vm_clock_nsec / 1000000) % 1000)); sizing = size_to_str(sn->vm_state_size); - qemu_printf("%-10s%-20s%7s%20s%15s", + qemu_printf("%-10s%-20s%11s%20s%15s", sn->id_str, sn->name, sizing, date_buf, diff --git a/block/qcow2.c b/block/qcow2.c index 8dcee5efec..3c754f616b 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -135,13 +135,16 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, s->crypto_header.length = headerlen; s->crypto_header.offset = ret; - /* Zero fill remaining space in cluster so it has predictable - * content in case of future spec changes */ + /* + * Zero fill all space in cluster so it has predictable + * content, as we may not initialize some regions of the + * header (eg only 1 out of 8 key slots will be initialized) + */ clusterlen = size_to_clusters(s, headerlen) * s->cluster_size; assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0); ret = bdrv_pwrite_zeroes(bs->file, - ret + headerlen, - clusterlen - headerlen, 0); + ret, + clusterlen, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not zero fill encryption header"); return -1; diff --git a/blockdev.c b/blockdev.c index 45de0ba37e..011dcfec27 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3734,9 +3734,13 @@ void qmp_drive_backup(DriveBackup *backup, Error **errp) blockdev_do_action(&action, errp); } -BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) +BlockDeviceInfoList *qmp_query_named_block_nodes(bool has_flat, + bool flat, + Error **errp) { - return bdrv_named_nodes_list(errp); + bool return_flat = has_flat && flat; + + return bdrv_named_nodes_list(return_flat, errp); } XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index af5711e533..5597e24474 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -79,9 +79,9 @@ The first cluster of a qcow2 image contains the file header: Offset into the image file at which the snapshot table starts. Must be aligned to a cluster boundary. -If the version is 3 or higher, the header has the following additional fields. -For version 2, the values are assumed to be zero, unless specified otherwise -in the description of a field. +For version 2, the header is exactly 72 bytes in length, and finishes here. +For version 3 or higher, the header length is at least 104 bytes, including +the next fields through header_length. 72 - 79: incompatible_features Bitmask of incompatible features. An implementation must @@ -109,7 +109,12 @@ in the description of a field. An External Data File Name header extension may be present if this bit is set. - Bits 3-63: Reserved (set to 0) + Bit 3: Compression type bit. If this bit is set, + a non-default compression is used for compressed + clusters. The compression_type field must be + present and not zero. + + Bits 4-63: Reserved (set to 0) 80 - 87: compatible_features Bitmask of compatible features. An implementation can @@ -164,6 +169,57 @@ in the description of a field. 100 - 103: header_length Length of the header structure in bytes. For version 2 images, the length is always assumed to be 72 bytes. + For version 3 it's at least 104 bytes and must be a multiple + of 8. + + +=== Additional fields (version 3 and higher) === + +In general, these fields are optional and may be safely ignored by the software, +as well as filled by zeros (which is equal to field absence), if software needs +to set field B, but does not care about field A which precedes B. More +formally, additional fields have the following compatibility rules: + +1. If the value of the additional field must not be ignored for correct +handling of the file, it will be accompanied by a corresponding incompatible +feature bit. + +2. If there are no unrecognized incompatible feature bits set, an unknown +additional field may be safely ignored other than preserving its value when +rewriting the image header. + +3. An explicit value of 0 will have the same behavior as when the field is not +present*, if not altered by a specific incompatible bit. + +*. A field is considered not present when header_length is less than or equal +to the field's offset. Also, all additional fields are not present for +version 2. + + 104: compression_type + + Defines the compression method used for compressed clusters. + All compressed clusters in an image use the same compression + type. + + If the incompatible bit "Compression type" is set: the field + must be present and non-zero (which means non-zlib + compression type). Otherwise, this field must not be present + or must be zero (which means zlib). + + Available compression type values: + 0: zlib <https://www.zlib.net/> + + +=== Header padding === + +@header_length must be a multiple of 8, which means that if the end of the last +additional field is not aligned, some padding is needed. This padding must be +zeroed, so that if some existing (or future) additional field will fall into +the padding, it will be interpreted accordingly to point [3.] of the previous +paragraph, i.e. in the same manner as when this field is not present. + + +=== Header extensions === Directly after the image header, optional sections called header extensions can be stored. Each extension has a structure like the following: diff --git a/docs/interop/qemu-img.rst b/docs/interop/qemu-img.rst index 42e4451db4..5f40137c10 100644 --- a/docs/interop/qemu-img.rst +++ b/docs/interop/qemu-img.rst @@ -214,6 +214,13 @@ Parameters to convert subcommand: will still be printed. Areas that cannot be read from the source will be treated as containing only zeroes. +.. option:: --target-is-zero + + Assume that reading the destination image will always return + zeros. This parameter is mutually exclusive with a destination image + that has a backing file. It is required to also use the ``-n`` + parameter to skip image creation. + Parameters to dd subcommand: .. program:: qemu-img-dd @@ -366,7 +373,7 @@ Command description: 4 Error on reading data -.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME Convert the disk image *FILENAME* or a snapshot *SNAPSHOT_PARAM* to disk image *OUTPUT_FILENAME* using format *OUTPUT_FMT*. It can diff --git a/include/block/block.h b/include/block/block.h index 314ce63ed9..cd6b5b95aa 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -454,7 +454,7 @@ void bdrv_lock_medium(BlockDriverState *bs, bool locked); void bdrv_eject(BlockDriverState *bs, bool eject_flag); const char *bdrv_get_format_name(BlockDriverState *bs); BlockDriverState *bdrv_find_node(const char *node_name); -BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp); +BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp); BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, diff --git a/include/block/qapi.h b/include/block/qapi.h index cd9410dee3..22c7807c89 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -29,7 +29,9 @@ #include "block/snapshot.h" BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, - BlockDriverState *bs, Error **errp); + BlockDriverState *bs, + bool flat, + Error **errp); int bdrv_query_snapshot_info_list(BlockDriverState *bs, SnapshotInfoList **p_list, Error **errp); diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index b237613e0d..53bc3f76c4 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -620,7 +620,7 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) } /* Print node information */ - blockdev_list = qmp_query_named_block_nodes(NULL); + blockdev_list = qmp_query_named_block_nodes(false, false, NULL); for (blockdev = blockdev_list; blockdev; blockdev = blockdev->next) { assert(blockdev->value->has_node_name); if (device && strcmp(device, blockdev->value->node_name)) { diff --git a/qapi/block-core.json b/qapi/block-core.json index 37d7ea7295..85e27bb61f 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1758,6 +1758,9 @@ # # Get the named block driver list # +# @flat: Omit the nested data about backing image ("backing-image" key) if true. +# Default is false (Since 5.0) +# # Returns: the list of BlockDeviceInfo # # Since: 2.0 @@ -1811,7 +1814,9 @@ # } } ] } # ## -{ 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] } +{ 'command': 'query-named-block-nodes', + 'returns': [ 'BlockDeviceInfo' ], + 'data': { '*flat': 'bool' } } ## # @XDbgBlockGraphNodeType: diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index d7fbc6b1f4..c9c54de1df 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -39,9 +39,9 @@ SRST ERST DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") SRST -.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME ERST DEF("create", img_create, diff --git a/qemu-img.c b/qemu-img.c index 2b4562b9d9..804630a368 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -70,6 +70,7 @@ enum { OPTION_PREALLOCATION = 265, OPTION_SHRINK = 266, OPTION_SALVAGE = 267, + OPTION_TARGET_IS_ZERO = 268, }; typedef enum OutputFormat { @@ -1984,10 +1985,9 @@ static int convert_do_copy(ImgConvertState *s) int64_t sector_num = 0; /* Check whether we have zero initialisation or can get it efficiently */ - if (s->target_is_new && s->min_sparse && !s->target_has_backing) { + if (!s->has_zero_init && s->target_is_new && s->min_sparse && + !s->target_has_backing) { s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target)); - } else { - s->has_zero_init = false; } if (!s->has_zero_init && !s->target_has_backing && @@ -2086,6 +2086,7 @@ static int img_convert(int argc, char **argv) {"force-share", no_argument, 0, 'U'}, {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, {"salvage", no_argument, 0, OPTION_SALVAGE}, + {"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", @@ -2209,6 +2210,14 @@ static int img_convert(int argc, char **argv) case OPTION_TARGET_IMAGE_OPTS: tgt_image_opts = true; break; + case OPTION_TARGET_IS_ZERO: + /* + * The user asserting that the target is blank has the + * same effect as the target driver supporting zero + * initialisation. + */ + s.has_zero_init = true; + break; } } @@ -2247,6 +2256,11 @@ static int img_convert(int argc, char **argv) warn_report("This will become an error in future QEMU versions."); } + if (s.has_zero_init && !skip_create) { + error_report("--target-is-zero requires use of -n flag"); + goto fail_getopt; + } + s.src_num = argc - optind - 1; out_filename = s.src_num >= 1 ? argv[argc - 1] : NULL; @@ -2380,6 +2394,12 @@ static int img_convert(int argc, char **argv) } s.target_has_backing = (bool) out_baseimg; + if (s.has_zero_init && s.target_has_backing) { + error_report("Cannot use --target-is-zero when the destination " + "image has a backing file"); + goto out; + } + if (s.src_num > 1 && out_baseimg) { error_report("Having a backing file for the target makes no sense when " "concatenating multiple input images"); @@ -2503,7 +2523,7 @@ static int img_convert(int argc, char **argv) } } - if (s.target_has_backing) { + if (s.target_has_backing && s.target_is_new) { /* Errors are treated as "backing length unknown" (which means * s.target_backing_sectors has to be negative, which it will * be automatically). The backing file length is used only diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122 index dfa350936f..f7a3ae684a 100755 --- a/tests/qemu-iotests/122 +++ b/tests/qemu-iotests/122 @@ -276,6 +276,20 @@ $QEMU_IMG convert -O $IMGFMT -n "$TEST_IMG" "$TEST_IMG".orig $QEMU_IMG compare "$TEST_IMG" "$TEST_IMG".orig +echo +echo '=== -n -B to an image without a backing file ===' +echo + +# Base for the output +TEST_IMG="$TEST_IMG".base _make_test_img 64M + +# Output that does have $TEST_IMG.base set as its (implicit) backing file +TEST_IMG="$TEST_IMG".orig _make_test_img 64M + +# Convert with -n, which should not confuse -B with "target BDS has a +# backing file" +$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -n "$TEST_IMG" "$TEST_IMG".orig + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out index 849b6cc2ef..1a35951a80 100644 --- a/tests/qemu-iotests/122.out +++ b/tests/qemu-iotests/122.out @@ -228,4 +228,9 @@ Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Images are identical. + +=== -n -B to an image without a backing file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 *** done diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139 index 6b1a444364..7120d3142b 100755 --- a/tests/qemu-iotests/139 +++ b/tests/qemu-iotests/139 @@ -344,9 +344,6 @@ class TestBlockdevDel(iotests.QMPTestCase): @iotests.skip_if_unsupported(['quorum']) def testQuorum(self): - if not iotests.supports_quorum(): - return - self.addQuorum('quorum0', 'node0', 'node1') # We cannot remove the children of a Quorum device self.delBlockDriverState('node0', expect_error = True) diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index f4b0a11dba..d7a9f31089 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -134,7 +134,7 @@ class BuiltinNBD(NBDBlockdevAddBase): self.server.add_drive_raw('if=none,id=nbd-export,' + 'file=%s,' % test_img + 'format=%s,' % imgfmt + - 'cache=%s' % cachemode + + 'cache=%s,' % cachemode + 'aio=%s' % aiomode) self.server.launch() diff --git a/tests/qemu-iotests/259 b/tests/qemu-iotests/259 new file mode 100755 index 0000000000..62e29af05f --- /dev/null +++ b/tests/qemu-iotests/259 @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# +# Test generic image creation fallback (by using NBD) +# +# Copyright (C) 2019 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" + +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 raw +_supported_proto nbd +_supported_os Linux + + +_make_test_img 64M + +echo +echo '--- Testing creation ---' + +$QEMU_IMG create -f qcow2 "$TEST_IMG" 64M | _filter_img_create +$QEMU_IMG info "$TEST_IMG" | _filter_img_info + +echo +echo '--- Testing creation for which the node would need to grow ---' + +# NBD does not support resizing, so this will fail +$QEMU_IMG create -f qcow2 -o preallocation=metadata "$TEST_IMG" 64M 2>&1 \ + | _filter_img_create + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/259.out b/tests/qemu-iotests/259.out new file mode 100644 index 0000000000..ffed19c2a0 --- /dev/null +++ b/tests/qemu-iotests/259.out @@ -0,0 +1,14 @@ +QA output created by 259 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + +--- Testing creation --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=qcow2 size=67108864 +image: TEST_DIR/t.IMGFMT +file format: qcow2 +virtual size: 64 MiB (67108864 bytes) +disk size: unavailable + +--- Testing creation for which the node would need to grow --- +qemu-img: TEST_DIR/t.IMGFMT: Could not resize image: Image format driver does not support resize +Formatting 'TEST_DIR/t.IMGFMT', fmt=qcow2 size=67108864 preallocation=metadata +*** done diff --git a/tests/qemu-iotests/279 b/tests/qemu-iotests/279 index 6682376808..30d29b1cb2 100755 --- a/tests/qemu-iotests/279 +++ b/tests/qemu-iotests/279 @@ -38,6 +38,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow qcow2 vmdk qed _supported_proto file _supported_os Linux +_unsupported_imgopts "subformat=monolithicFlat" \ + "subformat=twoGbMaxExtentFlat" \ TEST_IMG="$TEST_IMG.base" _make_test_img 64M TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" @@ -45,11 +47,12 @@ _make_test_img -b "$TEST_IMG.mid" echo echo '== qemu-img info --backing-chain ==' -_img_info --backing-chain | _filter_img_info +_img_info --backing-chain | _filter_img_info | grep -v 'backing file format' echo echo '== qemu-img info --backing-chain --image-opts ==' -TEST_IMG="driver=qcow2,file.driver=file,file.filename=$TEST_IMG" _img_info --backing-chain --image-opts | _filter_img_info +TEST_IMG="driver=$IMGFMT,file.driver=file,file.filename=$TEST_IMG" _img_info --backing-chain --image-opts \ + | _filter_img_info | grep -v 'backing file format' # success, all done echo "*** done" diff --git a/tests/qemu-iotests/284 b/tests/qemu-iotests/284 new file mode 100755 index 0000000000..071e89b33e --- /dev/null +++ b/tests/qemu-iotests/284 @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# +# Test ref count checks on encrypted images +# +# Copyright (C) 2019 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=berrange@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +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 qcow2 +_supported_proto generic +_supported_os Linux + + +size=1M + +SECRET="secret,id=sec0,data=astrochicken" + +IMGSPEC="driver=$IMGFMT,file.filename=$TEST_IMG,encrypt.key-secret=sec0" +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT + +_run_test() +{ + IMGOPTSSYNTAX=true + OLD_TEST_IMG="$TEST_IMG" + TEST_IMG="driver=$IMGFMT,file.filename=$TEST_IMG,encrypt.key-secret=sec0" + QEMU_IMG_EXTRA_ARGS="--image-opts --object $SECRET" + + echo + echo "== cluster size $csize" + echo "== checking image refcounts ==" + _check_test_img + + echo + echo "== writing some data ==" + $QEMU_IO -c "write -P 0x9 0 1" $QEMU_IMG_EXTRA_ARGS $TEST_IMG | _filter_qemu_io | _filter_testdir + echo + echo "== rechecking image refcounts ==" + _check_test_img + + echo + echo "== writing some more data ==" + $QEMU_IO -c "write -P 0x9 $csize 1" $QEMU_IMG_EXTRA_ARGS $TEST_IMG | _filter_qemu_io | _filter_testdir + echo + echo "== rechecking image refcounts ==" + _check_test_img + + TEST_IMG="$OLD_TEST_IMG" + QEMU_IMG_EXTRA_ARGS= + IMGOPTSSYNTAX= +} + + +echo +echo "testing LUKS qcow2 encryption" +echo + +for csize in 512 2048 32768 +do + _make_test_img --object $SECRET -o "encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10,cluster_size=$csize" $size + _run_test + _cleanup_test_img +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/284.out b/tests/qemu-iotests/284.out new file mode 100644 index 0000000000..48216f5742 --- /dev/null +++ b/tests/qemu-iotests/284.out @@ -0,0 +1,62 @@ +QA output created by 284 + +testing LUKS qcow2 encryption + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 + +== cluster size 512 +== checking image refcounts == +No errors were found on the image. + +== writing some data == +wrote 1/1 bytes at offset 0 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. + +== writing some more data == +wrote 1/1 bytes at offset 512 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 + +== cluster size 2048 +== checking image refcounts == +No errors were found on the image. + +== writing some data == +wrote 1/1 bytes at offset 0 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. + +== writing some more data == +wrote 1/1 bytes at offset 2048 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 + +== cluster size 32768 +== checking image refcounts == +No errors were found on the image. + +== writing some data == +wrote 1/1 bytes at offset 0 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. + +== writing some more data == +wrote 1/1 bytes at offset 32768 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/286 b/tests/qemu-iotests/286 new file mode 100755 index 0000000000..f14445ba4a --- /dev/null +++ b/tests/qemu-iotests/286 @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# +# Test qemu-img snapshot -l +# +# Copyright (C) 2019 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/>. +# + +seq=$(basename "$0") +echo "QA output created by $seq" + +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 +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file + +_make_test_img 64M + +# Should be so long as to take up the whole field width +sn_name=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + +# More memory will give us a larger VM state, i.e. one above 1 MB. +# This way, we get a number with a decimal point. +qemu_comm_method=monitor _launch_qemu -m 512 "$TEST_IMG" + +_send_qemu_cmd $QEMU_HANDLE "savevm $sn_name" '(qemu)' +_send_qemu_cmd $QEMU_HANDLE 'quit' '(qemu)' +wait=yes _cleanup_qemu + +# Check that all fields are separated by spaces. +# We first collapse all space sequences into one space each; +# then we turn every space-separated field into a '.'; +# and finally, we name the '.'s so the output is not just a confusing +# sequence of dots. + +echo 'Output structure:' +$QEMU_IMG snapshot -l "$TEST_IMG" | tail -n 1 | tr -s ' ' \ + | sed -e 's/\S\+/./g' \ + | sed -e 's/\./(snapshot ID)/' \ + -e 's/\./(snapshot name)/' \ + -e 's/\./(VM state size value)/' \ + -e 's/\./(VM state size unit)/' \ + -e 's/\./(snapshot date)/' \ + -e 's/\./(snapshot time)/' \ + -e 's/\./(VM clock)/' + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/286.out b/tests/qemu-iotests/286.out new file mode 100644 index 0000000000..39ff07e12c --- /dev/null +++ b/tests/qemu-iotests/286.out @@ -0,0 +1,8 @@ +QA output created by 286 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) savevm abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz +(qemu) quit +Output structure: +(snapshot ID) (snapshot name) (VM state size value) (VM state size unit) (snapshot date) (snapshot time) (VM clock) +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 1904223020..0317667695 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -273,6 +273,7 @@ 256 rw auto quick 257 rw 258 rw quick +259 rw auto quick 260 rw quick 261 rw 262 rw quick migration @@ -290,3 +291,5 @@ 280 rw migration quick 281 rw quick 283 auto quick +284 rw +286 rw quick |