diff options
Diffstat (limited to 'block')
-rw-r--r-- | block/backup-top.c | 10 | ||||
-rw-r--r-- | block/backup.c | 1 | ||||
-rw-r--r-- | block/blkdebug.c | 6 | ||||
-rw-r--r-- | block/blklogwrites.c | 10 | ||||
-rw-r--r-- | block/blkreplay.c | 6 | ||||
-rw-r--r-- | block/blkverify.c | 11 | ||||
-rw-r--r-- | block/dirty-bitmap.c | 13 | ||||
-rw-r--r-- | block/export/vhost-user-blk-server.c | 150 | ||||
-rw-r--r-- | block/meson.build | 3 | ||||
-rw-r--r-- | block/mirror.c | 12 | ||||
-rw-r--r-- | block/parallels-ext.c | 300 | ||||
-rw-r--r-- | block/parallels.c | 26 | ||||
-rw-r--r-- | block/parallels.h | 7 | ||||
-rw-r--r-- | block/qcow2-bitmap.c | 81 | ||||
-rw-r--r-- | block/qcow2.c | 65 | ||||
-rw-r--r-- | block/qcow2.h | 9 | ||||
-rw-r--r-- | block/qed.c | 24 | ||||
-rw-r--r-- | block/quorum.c | 6 |
18 files changed, 580 insertions, 160 deletions
diff --git a/block/backup-top.c b/block/backup-top.c index d1253e1aa6..589e8b651d 100644 --- a/block/backup-top.c +++ b/block/backup-top.c @@ -45,6 +45,12 @@ static coroutine_fn int backup_top_co_preadv( BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { + BDRVBackupTopState *s = bs->opaque; + + if (!s->active) { + return -EIO; + } + return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } @@ -54,6 +60,10 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, BDRVBackupTopState *s = bs->opaque; uint64_t off, end; + if (!s->active) { + return -EIO; + } + if (flags & BDRV_REQ_WRITE_UNCHANGED) { return 0; } diff --git a/block/backup.c b/block/backup.c index 94e6dcd72e..6cf2f974aa 100644 --- a/block/backup.c +++ b/block/backup.c @@ -103,6 +103,7 @@ static void backup_abort(Job *job) static void backup_clean(Job *job) { BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); + block_job_remove_all_bdrv(&s->common); bdrv_backup_top_drop(s->backup_top); } diff --git a/block/blkdebug.c b/block/blkdebug.c index 7eaa8a28bf..2c0b9b0ee8 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -464,7 +464,6 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, { BDRVBlkdebugState *s = bs->opaque; QemuOpts *opts; - Error *local_err = NULL; int ret; uint64_t align; @@ -494,10 +493,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto out; } diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 13ae63983b..b7579370a3 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -157,19 +157,17 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, /* Open the file */ bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false, - &local_err); - if (local_err) { + errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } /* Open the log file */ s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_of_bds, - BDRV_CHILD_METADATA, false, &local_err); - if (local_err) { + BDRV_CHILD_METADATA, false, errp); + if (!s->log_file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } diff --git a/block/blkreplay.c b/block/blkreplay.c index 30a0f5d57a..4a247752fd 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -23,16 +23,14 @@ typedef struct Request { static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - Error *local_err = NULL; int ret; /* Open the image file */ bs->file = bdrv_open_child(NULL, options, "image", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } diff --git a/block/blkverify.c b/block/blkverify.c index 943e62be9c..188d7632fa 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -112,7 +112,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, { BDRVBlkverifyState *s = bs->opaque; QemuOpts *opts; - Error *local_err = NULL; int ret; opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); @@ -125,20 +124,18 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } /* Open the test file */ s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "test", bs, &child_of_bds, BDRV_CHILD_DATA, - false, &local_err); - if (local_err) { + false, errp); + if (!s->test_file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 9b9cd71238..a0eaa28785 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -726,6 +726,19 @@ uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap) return hbitmap_serialization_align(bitmap->bitmap); } +/* Return the disk size covered by a chunk of serialized bitmap data. */ +uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size, + const BdrvDirtyBitmap *bitmap) +{ + uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); + uint64_t limit = granularity * (serialized_chunk_size << 3); + + assert(QEMU_IS_ALIGNED(limit, + bdrv_dirty_bitmap_serialization_align(bitmap))); + return limit; +} + + void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, uint8_t *buf, uint64_t offset, uint64_t bytes) diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c index ab2c4d44c4..cb5d896b7b 100644 --- a/block/export/vhost-user-blk-server.c +++ b/block/export/vhost-user-blk-server.c @@ -20,8 +20,17 @@ #include "sysemu/block-backend.h" #include "util/block-helpers.h" +/* + * Sector units are 512 bytes regardless of the + * virtio_blk_config->blk_size value. + */ +#define VIRTIO_BLK_SECTOR_BITS 9 +#define VIRTIO_BLK_SECTOR_SIZE (1ull << VIRTIO_BLK_SECTOR_BITS) + enum { VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1, + VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768, + VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768, }; struct virtio_blk_inhdr { unsigned char status; @@ -58,30 +67,102 @@ static void vu_blk_req_complete(VuBlkReq *req) free(req); } +static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector, + size_t size) +{ + uint64_t nb_sectors = size >> BDRV_SECTOR_BITS; + uint64_t total_sectors; + + if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return false; + } + if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) { + return false; + } + blk_get_geometry(vexp->export.blk, &total_sectors); + if (sector > total_sectors || nb_sectors > total_sectors - sector) { + return false; + } + return true; +} + static int coroutine_fn -vu_blk_discard_write_zeroes(BlockBackend *blk, struct iovec *iov, +vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov, uint32_t iovcnt, uint32_t type) { + BlockBackend *blk = vexp->export.blk; struct virtio_blk_discard_write_zeroes desc; - ssize_t size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); + ssize_t size; + uint64_t sector; + uint32_t num_sectors; + uint32_t max_sectors; + uint32_t flags; + int bytes; + + /* Only one desc is currently supported */ + if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) { + return VIRTIO_BLK_S_UNSUPP; + } + + size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); if (unlikely(size != sizeof(desc))) { - error_report("Invalid size %zd, expect %zu", size, sizeof(desc)); - return -EINVAL; + error_report("Invalid size %zd, expected %zu", size, sizeof(desc)); + return VIRTIO_BLK_S_IOERR; + } + + sector = le64_to_cpu(desc.sector); + num_sectors = le32_to_cpu(desc.num_sectors); + flags = le32_to_cpu(desc.flags); + max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ? + VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS : + VHOST_USER_BLK_MAX_DISCARD_SECTORS; + + /* This check ensures that 'bytes' fits in an int */ + if (unlikely(num_sectors > max_sectors)) { + return VIRTIO_BLK_S_IOERR; } - uint64_t range[2] = { le64_to_cpu(desc.sector) << 9, - le32_to_cpu(desc.num_sectors) << 9 }; - if (type == VIRTIO_BLK_T_DISCARD) { - if (blk_co_pdiscard(blk, range[0], range[1]) == 0) { - return 0; + bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS; + + if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) { + return VIRTIO_BLK_S_IOERR; + } + + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard + * and write zeroes commands if any unknown flag is set. + */ + if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + return VIRTIO_BLK_S_UNSUPP; + } + + if (type == VIRTIO_BLK_T_WRITE_ZEROES) { + int blk_flags = 0; + + if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { + blk_flags |= BDRV_REQ_MAY_UNMAP; } - } else if (type == VIRTIO_BLK_T_WRITE_ZEROES) { - if (blk_co_pwrite_zeroes(blk, range[0], range[1], 0) == 0) { - return 0; + + if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS, + bytes, blk_flags) == 0) { + return VIRTIO_BLK_S_OK; + } + } else if (type == VIRTIO_BLK_T_DISCARD) { + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for + * discard commands if the unmap flag is set. + */ + if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + return VIRTIO_BLK_S_UNSUPP; + } + + if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS, + bytes) == 0) { + return VIRTIO_BLK_S_OK; } } - return -EINVAL; + return VIRTIO_BLK_S_IOERR; } static void coroutine_fn vu_blk_virtio_process_req(void *opaque) @@ -128,6 +209,8 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque) switch (type & ~VIRTIO_BLK_T_BARRIER) { case VIRTIO_BLK_T_IN: case VIRTIO_BLK_T_OUT: { + QEMUIOVector qiov; + int64_t offset; ssize_t ret = 0; bool is_write = type & VIRTIO_BLK_T_OUT; req->sector_num = le64_to_cpu(req->out.sector); @@ -137,13 +220,24 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque) break; } - int64_t offset = req->sector_num * vexp->blk_size; - QEMUIOVector qiov; if (is_write) { qemu_iovec_init_external(&qiov, out_iov, out_num); - ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); } else { qemu_iovec_init_external(&qiov, in_iov, in_num); + } + + if (unlikely(!vu_blk_sect_range_ok(vexp, + req->sector_num, + qiov.size))) { + req->in->status = VIRTIO_BLK_S_IOERR; + break; + } + + offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS; + + if (is_write) { + ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); + } else { ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0); } if (ret >= 0) { @@ -170,19 +264,13 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque) } case VIRTIO_BLK_T_DISCARD: case VIRTIO_BLK_T_WRITE_ZEROES: { - int rc; - if (!vexp->writable) { req->in->status = VIRTIO_BLK_S_IOERR; break; } - rc = vu_blk_discard_write_zeroes(blk, &elem->out_sg[1], out_num, type); - if (rc == 0) { - req->in->status = VIRTIO_BLK_S_OK; - } else { - req->in->status = VIRTIO_BLK_S_IOERR; - } + req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num, + type); break; } default: @@ -347,17 +435,21 @@ vu_blk_initialize_config(BlockDriverState *bs, uint32_t blk_size, uint16_t num_queues) { - config->capacity = cpu_to_le64(bdrv_getlength(bs) >> BDRV_SECTOR_BITS); + config->capacity = + cpu_to_le64(bdrv_getlength(bs) >> VIRTIO_BLK_SECTOR_BITS); config->blk_size = cpu_to_le32(blk_size); config->size_max = cpu_to_le32(0); config->seg_max = cpu_to_le32(128 - 2); config->min_io_size = cpu_to_le16(1); config->opt_io_size = cpu_to_le32(1); config->num_queues = cpu_to_le16(num_queues); - config->max_discard_sectors = cpu_to_le32(32768); + config->max_discard_sectors = + cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS); config->max_discard_seg = cpu_to_le32(1); - config->discard_sector_alignment = cpu_to_le32(config->blk_size >> 9); - config->max_write_zeroes_sectors = cpu_to_le32(32768); + config->discard_sector_alignment = + cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS); + config->max_write_zeroes_sectors + = cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS); config->max_write_zeroes_seg = cpu_to_le32(1); } @@ -383,7 +475,7 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, if (vu_opts->has_logical_block_size) { logical_block_size = vu_opts->logical_block_size; } else { - logical_block_size = BDRV_SECTOR_SIZE; + logical_block_size = VIRTIO_BLK_SECTOR_SIZE; } check_block_size(exp->id, "logical-block-size", logical_block_size, &local_err); diff --git a/block/meson.build b/block/meson.build index eeaefe5809..d21990ec95 100644 --- a/block/meson.build +++ b/block/meson.build @@ -57,7 +57,8 @@ block_ss.add(when: 'CONFIG_QED', if_true: files( 'qed-table.c', 'qed.c', )) -block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'], if_true: files('parallels.c')) +block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'], + if_true: files('parallels.c', 'parallels-ext.c')) block_ss.add(when: 'CONFIG_WIN32', if_true: files('file-win32.c', 'win32-aio.c')) block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit]) block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c')) diff --git a/block/mirror.c b/block/mirror.c index 1803c6988b..6af02a57c4 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1860,8 +1860,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, bool auto_complete, Error **errp) { bool base_read_only; - Error *local_err = NULL; - BlockJob *ret; + BlockJob *job; base_read_only = bdrv_is_read_only(base); @@ -1871,19 +1870,18 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, } } - ret = mirror_start_job( + job = mirror_start_job( job_id, bs, creation_flags, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN, false, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + errp); + if (!job) { goto error_restore_flags; } - return ret; + return job; error_restore_flags: /* ignore error and errp for bdrv_reopen, because we want to propagate diff --git a/block/parallels-ext.c b/block/parallels-ext.c new file mode 100644 index 0000000000..e0dd0975c6 --- /dev/null +++ b/block/parallels-ext.c @@ -0,0 +1,300 @@ +/* + * Support of Parallels Format Extension. It's a part of Parallels format + * driver. + * + * Copyright (c) 2021 Virtuozzo International GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "block/block_int.h" +#include "parallels.h" +#include "crypto/hash.h" +#include "qemu/uuid.h" + +#define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL + +#define PARALLELS_END_OF_FEATURES_MAGIC 0x0ULL +#define PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC 0x20385FAE252CB34AULL + +typedef struct ParallelsFormatExtensionHeader { + uint64_t magic; /* PARALLELS_FORMAT_EXTENSION_MAGIC */ + uint8_t check_sum[16]; +} QEMU_PACKED ParallelsFormatExtensionHeader; + +typedef struct ParallelsFeatureHeader { + uint64_t magic; + uint64_t flags; + uint32_t data_size; + uint32_t _unused; +} QEMU_PACKED ParallelsFeatureHeader; + +typedef struct ParallelsDirtyBitmapFeature { + uint64_t size; + uint8_t id[16]; + uint32_t granularity; + uint32_t l1_size; + /* L1 table follows */ +} QEMU_PACKED ParallelsDirtyBitmapFeature; + +/* Given L1 table read bitmap data from the image and populate @bitmap */ +static int parallels_load_bitmap_data(BlockDriverState *bs, + const uint64_t *l1_table, + uint32_t l1_size, + BdrvDirtyBitmap *bitmap, + Error **errp) +{ + BDRVParallelsState *s = bs->opaque; + int ret = 0; + uint64_t offset, limit; + uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); + uint8_t *buf = NULL; + uint64_t i, tab_size = + DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size), + s->cluster_size); + + if (tab_size != l1_size) { + error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond " + "to bitmap size and cluster size. Expected %" PRIu64, + l1_size, tab_size); + return -EINVAL; + } + + buf = qemu_blockalign(bs, s->cluster_size); + limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); + for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { + uint64_t count = MIN(bm_size - offset, limit); + uint64_t entry = l1_table[i]; + + if (entry == 0) { + /* No need to deserialize zeros because @bitmap is cleared. */ + continue; + } + + if (entry == 1) { + bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false); + } else { + ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf, + s->cluster_size); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to read bitmap data cluster"); + goto finish; + } + bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count, + false); + } + } + ret = 0; + + bdrv_dirty_bitmap_deserialize_finish(bitmap); + +finish: + qemu_vfree(buf); + + return ret; +} + +/* + * @data buffer (of @data_size size) is the Dirty bitmaps feature which + * consists of ParallelsDirtyBitmapFeature followed by L1 table. + */ +static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs, + uint8_t *data, + size_t data_size, + Error **errp) +{ + int ret; + ParallelsDirtyBitmapFeature bf; + g_autofree uint64_t *l1_table = NULL; + BdrvDirtyBitmap *bitmap; + QemuUUID uuid; + char uuidstr[UUID_FMT_LEN + 1]; + int i; + + if (data_size < sizeof(bf)) { + error_setg(errp, "Too small Bitmap Feature area in Parallels Format " + "Extension: %zu bytes, expected at least %zu bytes", + data_size, sizeof(bf)); + return NULL; + } + memcpy(&bf, data, sizeof(bf)); + bf.size = le64_to_cpu(bf.size); + bf.granularity = le32_to_cpu(bf.granularity) << BDRV_SECTOR_BITS; + bf.l1_size = le32_to_cpu(bf.l1_size); + data += sizeof(bf); + data_size -= sizeof(bf); + + if (bf.size != bs->total_sectors) { + error_setg(errp, "Bitmap size (in sectors) %" PRId64 " differs from " + "disk size in sectors %" PRId64, bf.size, bs->total_sectors); + return NULL; + } + + if (bf.l1_size * sizeof(uint64_t) > data_size) { + error_setg(errp, "Bitmaps feature corrupted: l1 table exceeds " + "extension data_size"); + return NULL; + } + + memcpy(&uuid, bf.id, sizeof(uuid)); + qemu_uuid_unparse(&uuid, uuidstr); + bitmap = bdrv_create_dirty_bitmap(bs, bf.granularity, uuidstr, errp); + if (!bitmap) { + return NULL; + } + + l1_table = g_new(uint64_t, bf.l1_size); + for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) { + l1_table[i] = ldq_le_p(data); + } + + ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp); + if (ret < 0) { + bdrv_release_dirty_bitmap(bitmap); + return NULL; + } + + /* We support format extension only for RO parallels images. */ + assert(!(bs->open_flags & BDRV_O_RDWR)); + bdrv_dirty_bitmap_set_readonly(bitmap, true); + + return bitmap; +} + +static int parallels_parse_format_extension(BlockDriverState *bs, + uint8_t *ext_cluster, Error **errp) +{ + BDRVParallelsState *s = bs->opaque; + int ret; + int remaining = s->cluster_size; + uint8_t *pos = ext_cluster; + ParallelsFormatExtensionHeader eh; + g_autofree uint8_t *hash = NULL; + size_t hash_len = 0; + GSList *bitmaps = NULL, *el; + + memcpy(&eh, pos, sizeof(eh)); + eh.magic = le64_to_cpu(eh.magic); + pos += sizeof(eh); + remaining -= sizeof(eh); + + if (eh.magic != PARALLELS_FORMAT_EXTENSION_MAGIC) { + error_setg(errp, "Wrong parallels Format Extension magic: 0x%" PRIx64 + ", expected: 0x%llx", eh.magic, + PARALLELS_FORMAT_EXTENSION_MAGIC); + goto fail; + } + + ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining, + &hash, &hash_len, errp); + if (ret < 0) { + goto fail; + } + + if (hash_len != sizeof(eh.check_sum) || + memcmp(hash, eh.check_sum, sizeof(eh.check_sum)) != 0) { + error_setg(errp, "Wrong checksum in Format Extension header. Format " + "extension is corrupted."); + goto fail; + } + + while (true) { + ParallelsFeatureHeader fh; + BdrvDirtyBitmap *bitmap; + + if (remaining < sizeof(fh)) { + error_setg(errp, "Can not read feature header, as remaining bytes " + "(%d) in Format Extension is less than Feature header " + "size (%zu)", remaining, sizeof(fh)); + goto fail; + } + + memcpy(&fh, pos, sizeof(fh)); + pos += sizeof(fh); + remaining -= sizeof(fh); + + fh.magic = le64_to_cpu(fh.magic); + fh.flags = le64_to_cpu(fh.flags); + fh.data_size = le32_to_cpu(fh.data_size); + + if (fh.flags) { + error_setg(errp, "Flags for extension feature are unsupported"); + goto fail; + } + + if (fh.data_size > remaining) { + error_setg(errp, "Feature data_size exceedes Format Extension " + "cluster"); + goto fail; + } + + switch (fh.magic) { + case PARALLELS_END_OF_FEATURES_MAGIC: + return 0; + + case PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC: + bitmap = parallels_load_bitmap(bs, pos, fh.data_size, errp); + if (!bitmap) { + goto fail; + } + bitmaps = g_slist_append(bitmaps, bitmap); + break; + + default: + error_setg(errp, "Unknown feature: 0x%" PRIu64, fh.magic); + goto fail; + } + + pos = ext_cluster + QEMU_ALIGN_UP(pos + fh.data_size - ext_cluster, 8); + } + +fail: + for (el = bitmaps; el; el = el->next) { + bdrv_release_dirty_bitmap(el->data); + } + g_slist_free(bitmaps); + + return -EINVAL; +} + +int parallels_read_format_extension(BlockDriverState *bs, + int64_t ext_off, Error **errp) +{ + BDRVParallelsState *s = bs->opaque; + int ret; + uint8_t *ext_cluster = qemu_blockalign(bs, s->cluster_size); + + assert(ext_off > 0); + + ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to read Format Extension cluster"); + goto out; + } + + ret = parallels_parse_format_extension(bs, ext_cluster, errp); + +out: + qemu_vfree(ext_cluster); + + return ret; +} diff --git a/block/parallels.c b/block/parallels.c index 3c22dfdc9d..6ebad2a2bb 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "block/block_int.h" #include "block/qdict.h" @@ -421,7 +422,6 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, int ret; uint32_t i; bool flush_bat = false; - int cluster_size = s->tracks << BDRV_SECTOR_BITS; size = bdrv_getlength(bs->file->bs); if (size < 0) { @@ -472,7 +472,7 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, high_off = off; } - if (prev_off != 0 && (prev_off + cluster_size) != off) { + if (prev_off != 0 && (prev_off + s->cluster_size) != off) { res->bfi.fragmented_clusters++; } prev_off = off; @@ -487,10 +487,10 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, } } - res->image_end_offset = high_off + cluster_size; + res->image_end_offset = high_off + s->cluster_size; if (size > res->image_end_offset) { int64_t count; - count = DIV_ROUND_UP(size - res->image_end_offset, cluster_size); + count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size); fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n", fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR", size - res->image_end_offset); @@ -771,6 +771,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, ret = -EFBIG; goto fail; } + s->cluster_size = s->tracks << BDRV_SECTOR_BITS; s->bat_size = le32_to_cpu(ph.bat_entries); if (s->bat_size > INT_MAX / sizeof(uint32_t)) { @@ -843,6 +844,23 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, goto fail_options; } + if (ph.ext_off) { + if (flags & BDRV_O_RDWR) { + /* + * It's unsafe to open image RW if there is an extension (as we + * don't support it). But parallels driver in QEMU historically + * ignores the extension, so print warning and don't care. + */ + warn_report("Format Extension ignored in RW mode"); + } else { + ret = parallels_read_format_extension( + bs, le64_to_cpu(ph.ext_off) << BDRV_SECTOR_BITS, errp); + if (ret < 0) { + goto fail; + } + } + } + if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) { s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC); ret = parallels_update_header(bs); diff --git a/block/parallels.h b/block/parallels.h index 5aa101cfc8..f22f43f988 100644 --- a/block/parallels.h +++ b/block/parallels.h @@ -48,7 +48,8 @@ typedef struct ParallelsHeader { uint64_t nb_sectors; uint32_t inuse; uint32_t data_off; - char padding[12]; + uint32_t flags; + uint64_t ext_off; } QEMU_PACKED ParallelsHeader; typedef enum ParallelsPreallocMode { @@ -79,9 +80,13 @@ typedef struct BDRVParallelsState { ParallelsPreallocMode prealloc_mode; unsigned int tracks; + unsigned int cluster_size; unsigned int off_multiplier; Error *migration_blocker; } BDRVParallelsState; +int parallels_read_format_extension(BlockDriverState *bs, + int64_t ext_off, Error **errp); + #endif diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 5eef82fa55..8fb4731551 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -278,18 +278,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) return 0; } -/* Return the disk size covered by a single qcow2 cluster of bitmap data. */ -static uint64_t bytes_covered_by_bitmap_cluster(const BDRVQcow2State *s, - const BdrvDirtyBitmap *bitmap) -{ - uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); - uint64_t limit = granularity * (s->cluster_size << 3); - - assert(QEMU_IS_ALIGNED(limit, - bdrv_dirty_bitmap_serialization_align(bitmap))); - return limit; -} - /* load_bitmap_data * @bitmap_table entries must satisfy specification constraints. * @bitmap must be cleared */ @@ -312,7 +300,7 @@ static int load_bitmap_data(BlockDriverState *bs, } buf = g_malloc(s->cluster_size); - limit = bytes_covered_by_bitmap_cluster(s, bitmap); + limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { uint64_t count = MIN(bm_size - offset, limit); uint64_t entry = bitmap_table[i]; @@ -962,25 +950,27 @@ static void set_readonly_helper(gpointer bitmap, gpointer value) bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value); } -/* qcow2_load_dirty_bitmaps() - * Return value is a hint for caller: true means that the Qcow2 header was - * updated. (false doesn't mean that the header should be updated by the - * caller, it just means that updating was not needed or the image cannot be - * written to). - * On failure the function returns false. +/* + * Return true on success, false on failure. + * If header_updated is not NULL then it is set appropriately regardless of + * the return value. */ -bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, + Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; Qcow2Bitmap *bm; GSList *created_dirty_bitmaps = NULL; - bool header_updated = false; bool needs_update = false; + if (header_updated) { + *header_updated = false; + } + if (s->nb_bitmaps == 0) { /* No bitmaps - nothing to do */ - return false; + return true; } bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, @@ -1036,7 +1026,9 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) error_setg_errno(errp, -ret, "Can't update bitmap directory"); goto fail; } - header_updated = true; + if (header_updated) { + *header_updated = true; + } } if (!can_write(bs)) { @@ -1047,7 +1039,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) g_slist_free(created_dirty_bitmaps); bitmap_list_free(bm_list); - return header_updated; + return true; fail: g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs); @@ -1089,30 +1081,32 @@ static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) /* * qcow2_get_bitmap_info_list() * Returns a list of QCOW2 bitmap details. - * In case of no bitmaps, the function returns NULL and - * the @errp parameter is not set. - * When bitmap information can not be obtained, the function returns - * NULL and the @errp parameter is set. + * On success return true with info_list set (note, that if there are no + * bitmaps, info_list is set to NULL). + * On failure return false with errp set. */ -Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, - Error **errp) +bool qcow2_get_bitmap_info_list(BlockDriverState *bs, + Qcow2BitmapInfoList **info_list, Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; Qcow2Bitmap *bm; - Qcow2BitmapInfoList *list = NULL; - Qcow2BitmapInfoList **tail = &list; + Qcow2BitmapInfoList **tail; if (s->nb_bitmaps == 0) { - return NULL; + *info_list = NULL; + return true; } bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, s->bitmap_directory_size, errp); - if (bm_list == NULL) { - return NULL; + if (!bm_list) { + return false; } + *info_list = NULL; + tail = info_list; + QSIMPLEQ_FOREACH(bm, bm_list, entry) { Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1); info->granularity = 1U << bm->granularity_bits; @@ -1123,7 +1117,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, bitmap_list_free(bm_list); - return list; + return true; } int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) @@ -1303,7 +1297,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, } buf = g_malloc(s->cluster_size); - limit = bytes_covered_by_bitmap_cluster(s, bitmap); + limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); assert(DIV_ROUND_UP(bm_size, limit) == tb_size); offset = 0; @@ -1525,9 +1519,10 @@ out: * readonly to begin with, and whether we opened directly or reopened to that * state shouldn't matter for the state we get afterward. */ -void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, +bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, Error **errp) { + ERRP_GUARD(); BdrvDirtyBitmap *bitmap; BDRVQcow2State *s = bs->opaque; uint32_t new_nb_bitmaps = s->nb_bitmaps; @@ -1547,7 +1542,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, s->bitmap_directory_size, errp); if (bm_list == NULL) { - return; + return false; } } @@ -1662,7 +1657,7 @@ success: } bitmap_list_free(bm_list); - return; + return true; fail: QSIMPLEQ_FOREACH(bm, bm_list, entry) { @@ -1680,16 +1675,14 @@ fail: } bitmap_list_free(bm_list); + return false; } int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp) { BdrvDirtyBitmap *bitmap; - Error *local_err = NULL; - qcow2_store_persistent_dirty_bitmaps(bs, false, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + if (!qcow2_store_persistent_dirty_bitmaps(bs, false, errp)) { return -EINVAL; } diff --git a/block/qcow2.c b/block/qcow2.c index d9f49a52e7..0db1227ac9 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -868,7 +868,7 @@ static void qcow2_attach_aio_context(BlockDriverState *bs, cache_clean_timer_init(bs, new_context); } -static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, +static bool read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, uint64_t *l2_cache_size, uint64_t *l2_cache_entry_size, uint64_t *refcount_cache_size, Error **errp) @@ -906,16 +906,16 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " "at the same time"); - return; + return false; } else if (l2_cache_size_set && (l2_cache_max_setting > combined_cache_size)) { error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " QCOW2_OPT_CACHE_SIZE); - return; + return false; } else if (*refcount_cache_size > combined_cache_size) { error_setg(errp, QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not exceed " QCOW2_OPT_CACHE_SIZE); - return; + return false; } if (l2_cache_size_set) { @@ -954,8 +954,10 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, error_setg(errp, "L2 cache entry size must be a power of two " "between %d and the cluster size (%d)", 1 << MIN_CLUSTER_BITS, s->cluster_size); - return; + return false; } + + return true; } typedef struct Qcow2ReopenState { @@ -982,7 +984,6 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, int i; const char *encryptfmt; QDict *encryptopts = NULL; - Error *local_err = NULL; int ret; qdict_extract_subqdict(options, &encryptopts, "encrypt."); @@ -995,10 +996,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } /* get L2 table/refcount block cache size from command line options */ - read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, - &refcount_cache_size, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, + &refcount_cache_size, errp)) { ret = -EINVAL; goto fail; } @@ -1159,6 +1158,10 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } qdict_put_str(encryptopts, "format", "qcow"); r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); + if (!r->crypto_opts) { + ret = -EINVAL; + goto fail; + } break; case QCOW_CRYPT_LUKS: @@ -1171,14 +1174,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } qdict_put_str(encryptopts, "format", "luks"); r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); + if (!r->crypto_opts) { + ret = -EINVAL; + goto fail; + } break; default: error_setg(errp, "Unsupported encryption method %d", s->crypt_method_header); - break; - } - if (s->crypt_method_header != QCOW_CRYPT_NONE && !r->crypto_opts) { ret = -EINVAL; goto fail; } @@ -1292,11 +1296,11 @@ static int validate_compression_type(BDRVQcow2State *s, Error **errp) static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; unsigned int len, i; int ret = 0; QCowHeader header; - Error *local_err = NULL; uint64_t ext_end; uint64_t l1_vm_state_index; bool update_header = false; @@ -1611,9 +1615,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, /* Open external data file */ s->data_file = bdrv_open_child(NULL, options, "data-file", bs, &child_of_bds, BDRV_CHILD_DATA, - true, &local_err); - if (local_err) { - error_propagate(errp, local_err); + true, errp); + if (*errp) { ret = -EINVAL; goto fail; } @@ -1785,9 +1788,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) { /* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */ - bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + bool header_updated; + if (!qcow2_load_dirty_bitmaps(bs, &header_updated, errp)) { ret = -EINVAL; goto fail; } @@ -2719,11 +2721,11 @@ static void qcow2_close(BlockDriverState *bs) static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) { + ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; int flags = s->flags; QCryptoBlock *crypto = NULL; QDict *options; - Error *local_err = NULL; int ret; /* @@ -2741,16 +2743,11 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, flags &= ~BDRV_O_INACTIVE; qemu_co_mutex_lock(&s->lock); - ret = qcow2_do_open(bs, options, flags, &local_err); + ret = qcow2_do_open(bs, options, flags, errp); qemu_co_mutex_unlock(&s->lock); qobject_unref(options); - if (local_err) { - error_propagate_prepend(errp, local_err, - "Could not reopen qcow2 layer: "); - bs->drv = NULL; - return; - } else if (ret < 0) { - error_setg_errno(errp, -ret, "Could not reopen qcow2 layer"); + if (ret < 0) { + error_prepend(errp, "Could not reopen qcow2 layer: "); bs->drv = NULL; return; } @@ -5066,12 +5063,10 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, BDRVQcow2State *s = bs->opaque; ImageInfoSpecific *spec_info; QCryptoBlockInfo *encrypt_info = NULL; - Error *local_err = NULL; if (s->crypto != NULL) { - encrypt_info = qcrypto_block_get_info(s->crypto, &local_err); - if (local_err) { - error_propagate(errp, local_err); + encrypt_info = qcrypto_block_get_info(s->crypto, errp); + if (!encrypt_info) { return NULL; } } @@ -5088,9 +5083,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, }; } else if (s->qcow_version == 3) { Qcow2BitmapInfoList *bitmaps; - bitmaps = qcow2_get_bitmap_info_list(bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!qcow2_get_bitmap_info_list(bs, &bitmaps, errp)) { qapi_free_ImageInfoSpecific(spec_info); qapi_free_QCryptoBlockInfo(encrypt_info); return NULL; diff --git a/block/qcow2.h b/block/qcow2.h index 0678073b74..0fe5f74ed3 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -978,12 +978,13 @@ void qcow2_cache_discard(Qcow2Cache *c, void *table); int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size); -bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); -Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, - Error **errp); +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, + Error **errp); +bool qcow2_get_bitmap_info_list(BlockDriverState *bs, + Qcow2BitmapInfoList **info_list, Error **errp); int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); -void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, +bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, Error **errp); int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, diff --git a/block/qed.c b/block/qed.c index b27e7546ca..f45c640513 100644 --- a/block/qed.c +++ b/block/qed.c @@ -393,6 +393,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); if (ret < 0) { + error_setg(errp, "Failed to read QED header"); return ret; } qed_header_le_to_cpu(&le_header, &s->header); @@ -408,25 +409,30 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, return -ENOTSUP; } if (!qed_is_cluster_size_valid(s->header.cluster_size)) { + error_setg(errp, "QED cluster size is invalid"); return -EINVAL; } /* Round down file size to the last cluster */ file_size = bdrv_getlength(bs->file->bs); if (file_size < 0) { + error_setg(errp, "Failed to get file length"); return file_size; } s->file_size = qed_start_of_cluster(s, file_size); if (!qed_is_table_size_valid(s->header.table_size)) { + error_setg(errp, "QED table size is invalid"); return -EINVAL; } if (!qed_is_image_size_valid(s->header.image_size, s->header.cluster_size, s->header.table_size)) { + error_setg(errp, "QED image size is invalid"); return -EINVAL; } if (!qed_check_table_offset(s, s->header.l1_table_offset)) { + error_setg(errp, "QED table offset is invalid"); return -EINVAL; } @@ -438,6 +444,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, /* Header size calculation must not overflow uint32_t */ if (s->header.header_size > UINT32_MAX / s->header.cluster_size) { + error_setg(errp, "QED header size is too large"); return -EINVAL; } @@ -445,6 +452,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, if ((uint64_t)s->header.backing_filename_offset + s->header.backing_filename_size > s->header.cluster_size * s->header.header_size) { + error_setg(errp, "QED backing filename offset is invalid"); return -EINVAL; } @@ -453,6 +461,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, bs->auto_backing_file, sizeof(bs->auto_backing_file)); if (ret < 0) { + error_setg(errp, "Failed to read backing filename"); return ret; } pstrcpy(bs->backing_file, sizeof(bs->backing_file), @@ -475,6 +484,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_write_header_sync(s); if (ret) { + error_setg(errp, "Failed to update header"); return ret; } @@ -487,6 +497,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_read_l1_table_sync(s); if (ret) { + error_setg(errp, "Failed to read L1 table"); goto out; } @@ -503,6 +514,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_check(s, &result, true); if (ret) { + error_setg(errp, "Image corrupted"); goto out; } } @@ -1537,22 +1549,16 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, Error **errp) { BDRVQEDState *s = bs->opaque; - Error *local_err = NULL; int ret; bdrv_qed_close(bs); bdrv_qed_init_state(bs); qemu_co_mutex_lock(&s->table_lock); - ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); + ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, errp); qemu_co_mutex_unlock(&s->table_lock); - if (local_err) { - error_propagate_prepend(errp, local_err, - "Could not reopen qed layer: "); - return; - } else if (ret < 0) { - error_setg_errno(errp, -ret, "Could not reopen qed layer"); - return; + if (ret < 0) { + error_prepend(errp, "Could not reopen qed layer: "); } } diff --git a/block/quorum.c b/block/quorum.c index 0bd75450de..cfc1436abb 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -929,7 +929,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVQuorumState *s = bs->opaque; - Error *local_err = NULL; QemuOpts *opts = NULL; const char *pattern_str; bool *opened; @@ -1007,9 +1006,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, s->children[i] = bdrv_open_child(NULL, options, indexstr, bs, &child_of_bds, BDRV_CHILD_DATA, false, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + errp); + if (!s->children[i]) { ret = -EINVAL; goto close_exit; } |