From c2eb918e3299f930fd0d0ae48d002cf2599de250 Mon Sep 17 00:00:00 2001 From: Hu Tao Date: Wed, 10 Sep 2014 17:05:45 +0800 Subject: [PATCH] block: round up file size to nearest sector Currently the file size requested by user is rounded down to nearest sector, causing the actual file size could be a bit less than the size user requested. Since some formats (like qcow2) record virtual disk size in bytes, this can make the last few bytes cannot be accessed. This patch fixes it by rounding up file size to nearest sector so that the actual file size is no less than the requested file size. Signed-off-by: Hu Tao Reviewed-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz Signed-off-by: Kevin Wolf --- block/archipelago.c | 3 +- block/cow.c | 3 +- block/gluster.c | 4 +-- block/iscsi.c | 4 +-- block/nfs.c | 3 +- block/qcow.c | 3 +- block/qcow2.c | 3 +- block/qed.c | 3 +- block/raw-posix.c | 8 ++--- block/raw-win32.c | 4 +-- block/rbd.c | 3 +- block/sheepdog.c | 3 +- block/ssh.c | 3 +- block/vdi.c | 3 +- block/vhdx.c | 3 +- block/vmdk.c | 3 +- block/vpc.c | 3 +- tests/qemu-iotests/104 | 57 ++++++++++++++++++++++++++++++++ tests/qemu-iotests/104.out | 12 +++++++ tests/qemu-iotests/common.filter | 21 ++++++++++++ tests/qemu-iotests/group | 1 + 21 files changed, 127 insertions(+), 23 deletions(-) create mode 100755 tests/qemu-iotests/104 create mode 100644 tests/qemu-iotests/104.out diff --git a/block/archipelago.c b/block/archipelago.c index 40e5f76ae66..93fb7c06341 100644 --- a/block/archipelago.c +++ b/block/archipelago.c @@ -708,7 +708,8 @@ static int qemu_archipelago_create(const char *filename, parse_filename_opts(filename, errp, &volname, &segment_name, &mport, &vport); - total_size = qemu_opt_get_size_del(options, BLOCK_OPT_SIZE, 0); + total_size = ROUND_UP(qemu_opt_get_size_del(options, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); if (segment_name == NULL) { segment_name = g_strdup("archipelago"); diff --git a/block/cow.c b/block/cow.c index 6ee483327f3..c3769fe03b0 100644 --- a/block/cow.c +++ b/block/cow.c @@ -335,7 +335,8 @@ static int cow_create(const char *filename, QemuOpts *opts, Error **errp) BlockDriverState *cow_bs = NULL; /* Read out options */ - image_sectors = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512; + image_sectors = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); image_filename = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); ret = bdrv_create_file(filename, opts, &local_err); diff --git a/block/gluster.c b/block/gluster.c index 1912cf9d073..65c7a58062a 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -494,8 +494,8 @@ static int qemu_gluster_create(const char *filename, goto out; } - total_size = - qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / BDRV_SECTOR_SIZE; + total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); if (!tmp || !strcmp(tmp, "off")) { diff --git a/block/iscsi.c b/block/iscsi.c index 3e192024882..84bcae89fab 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1531,8 +1531,8 @@ static int iscsi_create(const char *filename, QemuOpts *opts, Error **errp) bs = bdrv_new("", &error_abort); /* Read out options */ - total_size = - qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / BDRV_SECTOR_SIZE; + 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; diff --git a/block/nfs.c b/block/nfs.c index 194f3015016..c76e368b952 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -418,7 +418,8 @@ static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp) client->aio_context = qemu_get_aio_context(); /* Read out options */ - total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); ret = nfs_client_open(client, url, O_CREAT, errp); if (ret < 0) { diff --git a/block/qcow.c b/block/qcow.c index 67c237fe7d0..041af26ce34 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -725,7 +725,8 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) BlockDriverState *qcow_bs; /* Read out options */ - total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512; + total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) { flags |= BLOCK_FLAG_ENCRYPT; diff --git a/block/qcow2.c b/block/qcow2.c index f9e045ff2be..c8050e527c6 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1921,7 +1921,8 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) int ret; /* Read out options */ - sectors = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512; + sectors = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT); if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) { diff --git a/block/qed.c b/block/qed.c index ba395af76af..f8d9e12263b 100644 --- a/block/qed.c +++ b/block/qed.c @@ -648,7 +648,8 @@ static int bdrv_qed_create(const char *filename, QemuOpts *opts, Error **errp) char *backing_fmt = NULL; int ret; - image_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT); cluster_size = qemu_opt_get_size_del(opts, diff --git a/block/raw-posix.c b/block/raw-posix.c index d737f3a0c5f..9c22e3f45d5 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -1369,8 +1369,8 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) strstart(filename, "file:", &filename); /* Read out options */ - total_size = - qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / BDRV_SECTOR_SIZE; + total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false); fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, @@ -1966,8 +1966,8 @@ static int hdev_create(const char *filename, QemuOpts *opts, (void)has_prefix; /* Read out options */ - total_size = - qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / BDRV_SECTOR_SIZE; + total_size = DIV_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) { diff --git a/block/raw-win32.c b/block/raw-win32.c index 902eab6100a..1e1880d528e 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -511,8 +511,8 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) strstart(filename, "file:", &filename); /* Read out options */ - total_size = - qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512; + total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); diff --git a/block/rbd.c b/block/rbd.c index ea969e7bebf..b7f7d5ff303 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -314,7 +314,8 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) } /* Read out options */ - bytes = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0); if (objsize) { if ((objsize - 1) & objsize) { /* not a power of 2? */ diff --git a/block/sheepdog.c b/block/sheepdog.c index f91afc3a5bc..7da36e1f9a0 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1702,7 +1702,8 @@ static int sd_create(const char *filename, QemuOpts *opts, goto out; } - s->inode.vdi_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); if (!buf || !strcmp(buf, "off")) { diff --git a/block/ssh.c b/block/ssh.c index cd2fd751fea..cf43bc0f894 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -700,7 +700,8 @@ static int ssh_create(const char *filename, QemuOpts *opts, Error **errp) ssh_state_init(&s); /* Get desired file size. */ - total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); DPRINTF("total_size=%" PRIi64, total_size); uri_options = qdict_new(); diff --git a/block/vdi.c b/block/vdi.c index 4b10aacc3b0..cfa08b0b93e 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -700,7 +700,8 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) logout("\n"); /* Read out options. */ - bytes = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); #if defined(CONFIG_VDI_BLOCK_SIZE) /* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */ block_size = qemu_opt_get_size_del(opts, diff --git a/block/vhdx.c b/block/vhdx.c index 87c99fc2608..796b7bd8845 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1766,7 +1766,8 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) VHDXImageType image_type; Error *local_err = NULL; - image_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0); block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0); type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); diff --git a/block/vmdk.c b/block/vmdk.c index a1cb91131e2..afdea1a8b69 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1807,7 +1807,8 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) goto exit; } /* Read out options */ - total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE); backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); if (qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false)) { diff --git a/block/vpc.c b/block/vpc.c index c024b4cf3fd..4947369d48e 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -757,7 +757,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) BlockDriverState *bs = NULL; /* Read out options */ - total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); if (disk_type_param) { if (!strcmp(disk_type_param, "dynamic")) { diff --git a/tests/qemu-iotests/104 b/tests/qemu-iotests/104 new file mode 100755 index 00000000000..b471aa5bb15 --- /dev/null +++ b/tests/qemu-iotests/104 @@ -0,0 +1,57 @@ +#!/bin/bash +# +# Test image creation with aligned and unaligned sizes +# +# Copyright (C) 2014 Fujitsu. +# +# 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=hutao@cn.fujitsu.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 generic +_supported_proto generic +_supported_os Linux + +echo "=== Check qemu-img info output ===" +echo +image_sizes="1024 1234" + +for s in $image_sizes; do + _make_test_img $s | _filter_img_create + _img_info | _filter_img_info +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/104.out b/tests/qemu-iotests/104.out new file mode 100644 index 00000000000..de27852e351 --- /dev/null +++ b/tests/qemu-iotests/104.out @@ -0,0 +1,12 @@ +QA output created by 104 +=== Check qemu-img info output === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 1.0K (1024 bytes) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1234 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 1.5K (1536 bytes) +***done diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 51192c85117..f69cb6b916f 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -192,5 +192,26 @@ _filter_img_create() -e "s/archipelago:a/TEST_DIR\//g" } +_filter_img_info() +{ + sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + -e "s#$TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGFMT#IMGFMT#g" \ + -e "/encrypted: yes/d" \ + -e "/cluster_size: [0-9]\\+/d" \ + -e "/table_size: [0-9]\\+/d" \ + -e "/compat: '[^']*'/d" \ + -e "/compat6: \\(on\\|off\\)/d" \ + -e "/static: \\(on\\|off\\)/d" \ + -e "/zeroed_grain: \\(on\\|off\\)/d" \ + -e "/subformat: '[^']*'/d" \ + -e "/adapter_type: '[^']*'/d" \ + -e "/lazy_refcounts: \\(on\\|off\\)/d" \ + -e "/block_size: [0-9]\\+/d" \ + -e "/block_state_zero: \\(on\\|off\\)/d" \ + -e "/log_size: [0-9]\\+/d" \ + -e "s/archipelago:a/TEST_DIR\//g" +} + # make sure this script returns success /bin/true diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 0920b28db4e..622685e94ce 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -104,3 +104,4 @@ 100 rw auto quick 101 rw auto quick 103 rw auto quick +104 rw auto -- 2.39.5