diff --git a/block/blkreplay.c b/block/blkreplay.c index 61e44a1949ed4ad34d3329c042310f9bcdc00866..fe5a9b4a984da6a94b56a99391d4950363855748 100755 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -129,10 +129,9 @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs) static BlockDriver bdrv_blkreplay = { .format_name = "blkreplay", - .protocol_name = "blkreplay", .instance_size = 0, - .bdrv_file_open = blkreplay_open, + .bdrv_open = blkreplay_open, .bdrv_close = blkreplay_close, .bdrv_child_perm = bdrv_filter_default_perms, .bdrv_getlength = blkreplay_getlength, diff --git a/block/crypto.c b/block/crypto.c index e0b8856f74357c7103b242e522e4927f16c22166..bc6c7e3795f515d47c9f1806d27a1db6df5263dc 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -357,7 +357,11 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, BlockCrypto *crypto = bs->opaque; uint64_t payload_offset = qcrypto_block_get_payload_offset(crypto->block); - assert(payload_offset < (INT64_MAX - offset)); + + if (payload_offset > INT64_MAX - offset) { + error_setg(errp, "The requested file size is too large"); + return -EFBIG; + } offset += payload_offset; diff --git a/block/parallels.c b/block/parallels.c index e2515dec812cccf9ec792b3f01b91815f6e5fd70..799215e079fe982d0e2fa59f47001594de796237 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -526,6 +526,11 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, cl_size = DEFAULT_CLUSTER_SIZE; } + /* XXX What is the real limit here? This is an insanely large maximum. */ + if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) { + error_setg(errp, "Cluster size is too large"); + return -EINVAL; + } if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) { error_setg(errp, "Image size is too large for this cluster size"); return -E2BIG; diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 362deaf303aff69a0d15a791d2e2e02ef0150ba5..6b8b63514afc45108a727bfdcb99595abdc945a4 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -839,6 +839,13 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, qcow2_cache_put(s->refcount_block_cache, &refcount_block); } ret = alloc_refcount_block(bs, cluster_index, &refcount_block); + /* If the caller needs to restart the search for free clusters, + * try the same ones first to see if they're still free. */ + if (ret == -EAGAIN) { + if (s->free_cluster_index > (start >> s->cluster_bits)) { + s->free_cluster_index = (start >> s->cluster_bits); + } + } if (ret < 0) { goto fail; } diff --git a/block/quorum.c b/block/quorum.c index 14333c18aaca6125bba54f1b056af0db384371e2..cfe484a945ccf2d29bcc092176846f9054f896c3 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1098,11 +1098,10 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) static BlockDriver bdrv_quorum = { .format_name = "quorum", - .protocol_name = "quorum", .instance_size = sizeof(BDRVQuorumState), - .bdrv_file_open = quorum_open, + .bdrv_open = quorum_open, .bdrv_close = quorum_close, .bdrv_refresh_filename = quorum_refresh_filename, diff --git a/block/replication.c b/block/replication.c index f98ef094b9da59d5c578696ee693a4a45fca2cc3..6c0c7186d9a2c0c1c30e534e0437bcc7eb42d378 100644 --- a/block/replication.c +++ b/block/replication.c @@ -703,7 +703,6 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) BlockDriver bdrv_replication = { .format_name = "replication", - .protocol_name = "replication", .instance_size = sizeof(BDRVReplicationState), .bdrv_open = replication_open, diff --git a/block/throttle.c b/block/throttle.c index 5f4d43d0fc57b84515bfb3790c28e9e9ec4ec603..95ed06acd8ddc61281d3d07c9eebfae568689b34 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -215,10 +215,9 @@ static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs) static BlockDriver bdrv_throttle = { .format_name = "throttle", - .protocol_name = "throttle", .instance_size = sizeof(ThrottleGroupMember), - .bdrv_file_open = throttle_open, + .bdrv_open = throttle_open, .bdrv_close = throttle_close, .bdrv_co_flush = throttle_co_flush, diff --git a/block/vdi.c b/block/vdi.c index d939b034c4e34a95debb869243774af9dd4c97c7..4a2d1ff88d4a477beffb8b674f10aa3044744db1 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -235,7 +235,6 @@ static void vdi_header_to_le(VdiHeader *header) qemu_uuid_bswap(&header->uuid_parent); } -#if defined(CONFIG_VDI_DEBUG) static void vdi_header_print(VdiHeader *header) { char uuid[37]; @@ -257,16 +256,15 @@ static void vdi_header_print(VdiHeader *header) logout("block extra 0x%04x\n", header->block_extra); logout("blocks tot. 0x%04x\n", header->blocks_in_image); logout("blocks all. 0x%04x\n", header->blocks_allocated); - uuid_unparse(header->uuid_image, uuid); + qemu_uuid_unparse(&header->uuid_image, uuid); logout("uuid image %s\n", uuid); - uuid_unparse(header->uuid_last_snap, uuid); + qemu_uuid_unparse(&header->uuid_last_snap, uuid); logout("uuid snap %s\n", uuid); - uuid_unparse(header->uuid_link, uuid); + qemu_uuid_unparse(&header->uuid_link, uuid); logout("uuid link %s\n", uuid); - uuid_unparse(header->uuid_parent, uuid); + qemu_uuid_unparse(&header->uuid_parent, uuid); logout("uuid parent %s\n", uuid); } -#endif static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) @@ -387,9 +385,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, } vdi_header_to_cpu(&header); -#if defined(CONFIG_VDI_DEBUG) - vdi_header_print(&header); -#endif + if (VDI_DEBUG) { + vdi_header_print(&header); + } if (header.disk_size > VDI_DISK_SIZE_MAX) { error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64 @@ -728,7 +726,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, int ret = 0; uint64_t bytes = 0; uint32_t blocks; - uint32_t image_type = VDI_TYPE_DYNAMIC; + uint32_t image_type; VdiHeader header; size_t i; size_t bmap_size; @@ -744,9 +742,22 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, /* Validate options and set default values */ bytes = vdi_opts->size; - if (vdi_opts->q_static) { + + if (!vdi_opts->has_preallocation) { + vdi_opts->preallocation = PREALLOC_MODE_OFF; + } + switch (vdi_opts->preallocation) { + case PREALLOC_MODE_OFF: + image_type = VDI_TYPE_DYNAMIC; + break; + case PREALLOC_MODE_METADATA: image_type = VDI_TYPE_STATIC; + break; + default: + error_setg(errp, "Preallocation mode not supported for vdi"); + return -EINVAL; } + #ifndef CONFIG_VDI_STATIC_IMAGE if (image_type == VDI_TYPE_STATIC) { ret = -ENOTSUP; @@ -812,9 +823,9 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, qemu_uuid_generate(&header.uuid_image); qemu_uuid_generate(&header.uuid_last_snap); /* There is no need to set header.uuid_link or header.uuid_parent here. */ -#if defined(CONFIG_VDI_DEBUG) - vdi_header_print(&header); -#endif + if (VDI_DEBUG) { + vdi_header_print(&header); + } vdi_header_to_le(&header); ret = blk_pwrite(blk, offset, &header, sizeof(header), 0); if (ret < 0) { @@ -874,6 +885,7 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, BlockdevCreateOptions *create_options = NULL; BlockDriverState *bs_file = NULL; uint64_t block_size = DEFAULT_CLUSTER_SIZE; + bool is_static = false; Visitor *v; Error *local_err = NULL; int ret; @@ -895,6 +907,9 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, goto done; } #endif + if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) { + is_static = true; + } qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true); @@ -913,6 +928,9 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, qdict_put_str(qdict, "driver", "vdi"); qdict_put_str(qdict, "file", bs_file->node_name); + if (is_static) { + qdict_put_str(qdict, "preallocation", "metadata"); + } /* Get the QAPI object */ v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); diff --git a/block/vhdx.c b/block/vhdx.c index d2c54b789191224e27b000c63aa163263c085f49..6ac0424f61aff8d04506d5ea6e411f39c3528fa2 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1822,17 +1822,21 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, /* Validate options and set default values */ image_size = vhdx_opts->size; if (image_size > VHDX_MAX_IMAGE_SIZE) { - error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB"); + error_setg(errp, "Image size too large; max of 64TB"); return -EINVAL; } if (!vhdx_opts->has_log_size) { log_size = DEFAULT_LOG_SIZE; } else { + if (vhdx_opts->log_size > UINT32_MAX) { + error_setg(errp, "Log size must be smaller than 4 GB"); + return -EINVAL; + } log_size = vhdx_opts->log_size; } if (log_size < MiB || (log_size % MiB) != 0) { - error_setg_errno(errp, EINVAL, "Log size must be a multiple of 1 MB"); + error_setg(errp, "Log size must be a multiple of 1 MB"); return -EINVAL; } @@ -1874,12 +1878,15 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, } if (block_size < MiB || (block_size % MiB) != 0) { - error_setg_errno(errp, EINVAL, "Block size must be a multiple of 1 MB"); + error_setg(errp, "Block size must be a multiple of 1 MB"); + return -EINVAL; + } + if (!is_power_of_2(block_size)) { + error_setg(errp, "Block size must be a power of two"); return -EINVAL; } if (block_size > VHDX_BLOCK_SIZE_MAX) { - error_setg_errno(errp, EINVAL, "Block size must not exceed %d", - VHDX_BLOCK_SIZE_MAX); + error_setg(errp, "Block size must not exceed %d", VHDX_BLOCK_SIZE_MAX); return -EINVAL; } diff --git a/include/block/block_int.h b/include/block/block_int.h index 27e17addba95cf4ae908ff02bb2bbbbecde7962d..c4dd1d4bb8ac644cd5279bedd9688742b1704b17 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -126,6 +126,8 @@ struct BlockDriver { int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags, Error **errp); + + /* Protocol drivers should implement this instead of bdrv_open */ int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, Error **errp); void (*bdrv_close)(BlockDriverState *bs); @@ -251,6 +253,12 @@ struct BlockDriver { */ int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs); + /* + * Drivers setting this field must be able to work with just a plain + * filename with ':' as a prefix, and no other options. + * Options may be extracted from the filename by implementing + * bdrv_parse_filename. + */ const char *protocol_name; int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset, PreallocMode prealloc, Error **errp); diff --git a/qapi/block-core.json b/qapi/block-core.json index 1088ab0c78564d156aea28ed1f76ad30bab5ae39..c50517bff31c6ff474aa0b0b30c94fa2b5e8ebaa 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3943,16 +3943,15 @@ # # @file Node to create the image format on # @size Size of the virtual disk in bytes -# @static Whether to create a statically (true) or -# dynamically (false) allocated image -# (default: false, i.e. dynamic) +# @preallocation Preallocation mode for the new image (allowed values: off, +# metadata; default: off) # # Since: 2.12 ## { 'struct': 'BlockdevCreateOptionsVdi', 'data': { 'file': 'BlockdevRef', 'size': 'size', - '*static': 'bool' } } + '*preallocation': 'PreallocMode' } } ## # @BlockdevVhdxSubformat: diff --git a/replication.h b/replication.h index 8faefe005f83584c39e66e5a80cbb2aa1bc0c1ca..4c8354de23ca2c9c1255ecd881f62de1a384fed7 100644 --- a/replication.h +++ b/replication.h @@ -67,7 +67,6 @@ typedef struct ReplicationState ReplicationState; * * BlockDriver bdrv_replication = { * .format_name = "replication", - * .protocol_name = "replication", * .instance_size = sizeof(BDRVReplicationState), * * .bdrv_open = replication_open, diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025 index f5e672e6b3b51233d797167257dac922f369c2df..70dd5f10aa27d86364bed353d6601367d0951b41 100755 --- a/tests/qemu-iotests/025 +++ b/tests/qemu-iotests/025 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter . ./common.pattern -_supported_fmt raw qcow2 qed +_supported_fmt raw qcow2 qed luks _supported_proto file sheepdog rbd nfs _supported_os Linux @@ -62,6 +62,13 @@ length EOF _check_test_img +# bdrv_truncate() doesn't zero the new space, so we need to do that explicitly. +# We still want to test automatic zeroing for other formats even though +# bdrv_truncate() doesn't guarantee it. +if [ "$IMGFMT" == "luks" ]; then + $QEMU_IO -c "write -z $small_size $((big_size - small_size))" "$TEST_IMG" > /dev/null +fi + echo echo "=== Verifying image size after reopen" $QEMU_IO -c "length" "$TEST_IMG" diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index 86a50a2e13d4c8f7364ec0b37a3e2a430fd27d77..8e89416a8639a39718f5dd0772c9115c82c4a731 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -533,7 +533,7 @@ Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device -11 leaked clusters were found on the image. +10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -561,7 +561,7 @@ Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device -11 leaked clusters were found on the image. +10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -589,7 +589,7 @@ Failed to flush the L2 table cache: No space left on device Failed to flush the refcount block cache: No space left on device write failed: No space left on device -11 leaked clusters were found on the image. +10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 diff --git a/tests/qemu-iotests/121 b/tests/qemu-iotests/121 index 1307b4e3279d8d47203b6e5ffeeeafce9501b647..6d6f55a5dcf47d1506acd4adb47d2aee25a4cee1 100755 --- a/tests/qemu-iotests/121 +++ b/tests/qemu-iotests/121 @@ -93,6 +93,26 @@ $QEMU_IO -c 'write 63M 130K' "$TEST_IMG" | _filter_qemu_io _check_test_img +echo +echo '=== Allocating a new refcount block must not leave holes in the image ===' +echo + +IMGOPTS='cluster_size=512,refcount_bits=16' _make_test_img 1M + +# This results in an image with 256 used clusters: the qcow2 header, +# the refcount table, one refcount block, the L1 table, four L2 tables +# and 248 data clusters +$QEMU_IO -c 'write 0 124k' "$TEST_IMG" | _filter_qemu_io + +# 256 clusters of 512 bytes each give us a 128K image +stat -c "size=%s (expected 131072)" $TEST_IMG + +# All 256 entries of the refcount block are used, so writing a new +# data cluster also allocates a new refcount block +$QEMU_IO -c 'write 124k 512' "$TEST_IMG" | _filter_qemu_io + +# Two more clusters, the image size should be 129K now +stat -c "size=%s (expected 132096)" $TEST_IMG # success, all done echo diff --git a/tests/qemu-iotests/121.out b/tests/qemu-iotests/121.out index 5961a44cd93945bfbb344ea06fe8d36acb7ec1a0..613d56185ecba996b76a87881811743c02d4a4cb 100644 --- a/tests/qemu-iotests/121.out +++ b/tests/qemu-iotests/121.out @@ -20,4 +20,14 @@ wrote 133120/133120 bytes at offset 66060288 130 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. +=== Allocating a new refcount block must not leave holes in the image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +wrote 126976/126976 bytes at offset 0 +124 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +size=131072 (expected 131072) +wrote 512/512 bytes at offset 126976 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +size=132096 (expected 132096) + *** done diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 index 96a5213e77f808df6817fcf93770e0e1c8639b6b..e607c0d296d351f1df898ca49d28f2970e730724 100755 --- a/tests/qemu-iotests/210 +++ b/tests/qemu-iotests/210 @@ -204,6 +204,43 @@ run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ { "execute": "quit" } EOF +echo +echo "=== Resize image with invalid sizes ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ + -blockdev driver=luks,file=node0,key-secret=keysec0,node-name=node1 \ + -object secret,id=keysec0,data="foo" <0 size"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +file format: IMGFMT +virtual size: 0 (0 bytes) *** done diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 new file mode 100755 index 0000000000000000000000000000000000000000..1edec26517158bce319cc9b2b8b134f23a28d3cf --- /dev/null +++ b/tests/qemu-iotests/211 @@ -0,0 +1,246 @@ +#!/bin/bash +# +# Test VDI and file image creation +# +# Copyright (C) 2018 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 . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt vdi +_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 \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Successful image creation (defaults) ===" +echo + +size=$((128 * 1024 * 1024)) + +run_qemu <. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt parallels +_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 \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Successful image creation (defaults) ===" +echo + +size=$((128 * 1024 * 1024)) + +run_qemu <. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt vhdx +_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 \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Successful image creation (defaults) ===" +echo + +size=$((128 * 1024 * 1024)) + +run_qemu <