diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2017-11-17 19:08:07 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2017-11-17 19:08:07 +0000 |
commit | 2e02083438962d26ef9dcc7100f3b378104183db (patch) | |
tree | cff9297aa3887b7d70c3250914b76277b65ee1e9 /block | |
parent | 085ee6d282d38b430c850900c051e6b9e8c1681f (diff) | |
parent | d5a49c6e7d9e42059450674ec845b7bc0d62cb7e (diff) |
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block layer patches for 2.11.0-rc2
# gpg: Signature made Fri 17 Nov 2017 17:58:36 GMT
# gpg: using RSA key 0x7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6
* remotes/kevin/tags/for-upstream: (25 commits)
iotests: Make 087 pass without AIO enabled
block: Make bdrv_next() keep strong references
qcow2: Fix overly broad madvise()
qcow2: Refuse to get unaligned offsets from cache
qcow2: Add bounds check to get_refblock_offset()
block: Guard against NULL bs->drv
qcow2: Unaligned zero cluster in handle_alloc()
qcow2: check_errors are fatal
qcow2: reject unaligned offsets in write compressed
iotests: Add test for failing qemu-img commit
tests: Add check-qobject for equality tests
iotests: Add test for non-string option reopening
block: qobject_is_equal() in bdrv_reopen_prepare()
qapi: Add qobject_is_equal()
qapi/qlist: Add qlist_append_null() macro
qapi/qnull: Add own header
qcow2: fix image corruption on commit with persistent bitmap
iotests: test clearing unknown autoclear_features by qcow2
block: Fix permissions in image activation
qcow2: fix image corruption after committing qcow2 image into base
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'block')
-rw-r--r-- | block/block-backend.c | 48 | ||||
-rw-r--r-- | block/bochs.c | 13 | ||||
-rw-r--r-- | block/cloop.c | 13 | ||||
-rw-r--r-- | block/dmg.c | 12 | ||||
-rw-r--r-- | block/io.c | 36 | ||||
-rw-r--r-- | block/qapi.c | 8 | ||||
-rw-r--r-- | block/qcow2-cache.c | 23 | ||||
-rw-r--r-- | block/qcow2-cluster.c | 13 | ||||
-rw-r--r-- | block/qcow2-refcount.c | 26 | ||||
-rw-r--r-- | block/qcow2.c | 31 | ||||
-rw-r--r-- | block/qcow2.h | 6 | ||||
-rw-r--r-- | block/rbd.c | 14 | ||||
-rw-r--r-- | block/replication.c | 26 | ||||
-rw-r--r-- | block/snapshot.c | 6 | ||||
-rw-r--r-- | block/vvfat.c | 8 |
15 files changed, 244 insertions, 39 deletions
diff --git a/block/block-backend.c b/block/block-backend.c index f10b1db612..5836cb3087 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -442,21 +442,37 @@ BlockBackend *blk_next(BlockBackend *blk) * the monitor or attached to a BlockBackend */ BlockDriverState *bdrv_next(BdrvNextIterator *it) { - BlockDriverState *bs; + BlockDriverState *bs, *old_bs; + + /* Must be called from the main loop */ + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); /* First, return all root nodes of BlockBackends. In order to avoid * returning a BDS twice when multiple BBs refer to it, we only return it * if the BB is the first one in the parent list of the BDS. */ if (it->phase == BDRV_NEXT_BACKEND_ROOTS) { + BlockBackend *old_blk = it->blk; + + old_bs = old_blk ? blk_bs(old_blk) : NULL; + do { it->blk = blk_all_next(it->blk); bs = it->blk ? blk_bs(it->blk) : NULL; } while (it->blk && (bs == NULL || bdrv_first_blk(bs) != it->blk)); + if (it->blk) { + blk_ref(it->blk); + } + blk_unref(old_blk); + if (bs) { + bdrv_ref(bs); + bdrv_unref(old_bs); return bs; } it->phase = BDRV_NEXT_MONITOR_OWNED; + } else { + old_bs = it->bs; } /* Then return the monitor-owned BDSes without a BB attached. Ignore all @@ -467,18 +483,46 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it) bs = it->bs; } while (bs && bdrv_has_blk(bs)); + if (bs) { + bdrv_ref(bs); + } + bdrv_unref(old_bs); + return bs; } -BlockDriverState *bdrv_first(BdrvNextIterator *it) +static void bdrv_next_reset(BdrvNextIterator *it) { *it = (BdrvNextIterator) { .phase = BDRV_NEXT_BACKEND_ROOTS, }; +} +BlockDriverState *bdrv_first(BdrvNextIterator *it) +{ + bdrv_next_reset(it); return bdrv_next(it); } +/* Must be called when aborting a bdrv_next() iteration before + * bdrv_next() returns NULL */ +void bdrv_next_cleanup(BdrvNextIterator *it) +{ + /* Must be called from the main loop */ + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + + if (it->phase == BDRV_NEXT_BACKEND_ROOTS) { + if (it->blk) { + bdrv_unref(blk_bs(it->blk)); + blk_unref(it->blk); + } + } else { + bdrv_unref(it->bs); + } + + bdrv_next_reset(it); +} + /* * Add a BlockBackend into the list of backends referenced by the monitor, with * the given @name acting as the handle for the monitor. diff --git a/block/bochs.c b/block/bochs.c index a759b6eff0..50c630047b 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -28,6 +28,7 @@ #include "block/block_int.h" #include "qemu/module.h" #include "qemu/bswap.h" +#include "qemu/error-report.h" /**************************************************************/ @@ -110,9 +111,15 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ - if (ret < 0) { - return ret; + if (!bdrv_is_read_only(bs)) { + error_report("Opening bochs images without an explicit read-only=on " + "option is deprecated. Future versions will refuse to " + "open the image instead of automatically marking the " + "image read-only."); + ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ + if (ret < 0) { + return ret; + } } ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); diff --git a/block/cloop.c b/block/cloop.c index d6597fcf78..2be68987bd 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu-common.h" #include "block/block_int.h" #include "qemu/module.h" @@ -72,9 +73,15 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_set_read_only(bs, true, errp); - if (ret < 0) { - return ret; + if (!bdrv_is_read_only(bs)) { + error_report("Opening cloop images without an explicit read-only=on " + "option is deprecated. Future versions will refuse to " + "open the image instead of automatically marking the " + "image read-only."); + ret = bdrv_set_read_only(bs, true, errp); + if (ret < 0) { + return ret; + } } /* read header */ diff --git a/block/dmg.c b/block/dmg.c index 6c0711f563..c9b3c519c4 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -419,9 +419,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_set_read_only(bs, true, errp); - if (ret < 0) { - return ret; + if (!bdrv_is_read_only(bs)) { + error_report("Opening dmg images without an explicit read-only=on " + "option is deprecated. Future versions will refuse to " + "open the image instead of automatically marking the " + "image read-only."); + ret = bdrv_set_read_only(bs, true, errp); + if (ret < 0) { + return ret; + } } block_module_load_one("dmg-bz2"); diff --git a/block/io.c b/block/io.c index 3d5ef2cabe..4fdf93a014 100644 --- a/block/io.c +++ b/block/io.c @@ -853,6 +853,10 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, assert(!(flags & ~BDRV_REQ_MASK)); + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_co_preadv) { return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags); } @@ -894,6 +898,10 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, assert(!(flags & ~BDRV_REQ_MASK)); + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_co_pwritev) { ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov, flags & bs->supported_write_flags); @@ -945,6 +953,10 @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset, { BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (!drv->bdrv_co_pwritev_compressed) { return -ENOTSUP; } @@ -975,6 +987,10 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, BDRV_REQUEST_MAX_BYTES); unsigned int progress = 0; + if (!drv) { + return -ENOMEDIUM; + } + /* FIXME We cannot require callers to have write permissions when all they * are doing is a read request. If we did things right, write permissions * would be obtained anyway, but internally by the copy-on-read code. As @@ -1291,6 +1307,10 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, bs->bl.request_alignment); int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); + if (!drv) { + return -ENOMEDIUM; + } + assert(alignment % bs->bl.request_alignment == 0); head = offset % alignment; tail = (offset + bytes) % alignment; @@ -1397,6 +1417,10 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, uint64_t bytes_remaining = bytes; int max_transfer; + if (!drv) { + return -ENOMEDIUM; + } + if (bdrv_has_readonly_bitmaps(bs)) { return -EPERM; } @@ -1863,6 +1887,8 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, bytes = n; } + /* Must be non-NULL or bdrv_getlength() would have failed */ + assert(bs->drv); if (!bs->drv->bdrv_co_get_block_status) { *pnum = bytes; ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; @@ -2373,6 +2399,12 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) } BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK); + if (!bs->drv) { + /* bs->drv->bdrv_co_flush() might have ejected the BDS + * (even in case of apparent success) */ + ret = -ENOMEDIUM; + goto out; + } if (bs->drv->bdrv_co_flush_to_disk) { ret = bs->drv->bdrv_co_flush_to_disk(bs); } else if (bs->drv->bdrv_aio_flush) { @@ -2542,6 +2574,10 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, num = max_pdiscard; } + if (!bs->drv) { + ret = -ENOMEDIUM; + goto out; + } if (bs->drv->bdrv_co_pdiscard) { ret = bs->drv->bdrv_co_pdiscard(bs, offset, num); } else { diff --git a/block/qapi.c b/block/qapi.c index 7fa2437923..fc10f0a565 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -39,8 +39,14 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, { ImageInfo **p_image_info; BlockDriverState *bs0; - BlockDeviceInfo *info = g_malloc0(sizeof(*info)); + BlockDeviceInfo *info; + if (!bs->drv) { + error_setg(errp, "Block device %s is ejected", bs->node_name); + return NULL; + } + + info = g_malloc0(sizeof(*info)); info->file = g_strdup(bs->filename); info->ro = bs->read_only; info->drv = g_strdup(bs->drv->format_name); diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 75746a7f43..c48ffebd8f 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -62,6 +62,18 @@ static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, return idx; } +static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c) +{ + if (c == s->refcount_block_cache) { + return "refcount block"; + } else if (c == s->l2_table_cache) { + return "L2 table"; + } else { + /* Do not abort, because this is not critical */ + return "unknown"; + } +} + static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, int i, int num_tables) { @@ -73,7 +85,7 @@ static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, size_t mem_size = (size_t) s->cluster_size * num_tables; size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); - if (length > 0) { + if (mem_size > offset && length > 0) { madvise((uint8_t *) t + offset, length, MADV_DONTNEED); } #endif @@ -314,9 +326,18 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t min_lru_counter = UINT64_MAX; int min_lru_index = -1; + assert(offset != 0); + trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, offset, read_from_disk); + if (offset_into_cluster(s, offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s " + "cache: Offset %#" PRIx64 " is unaligned", + qcow2_cache_get_name(s, c), offset); + return -EIO; + } + /* Check if the table is already cached */ i = lookup_index = (offset / s->cluster_size * 4) % c->size; do { diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 2e072ed155..a3fec27bf9 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1308,10 +1308,21 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, (!*host_offset || start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK))) { + int preallocated_nb_clusters; + + if (offset_into_cluster(s, entry & L2E_OFFSET_MASK)) { + qcow2_signal_corruption(bs, true, -1, -1, "Preallocated zero " + "cluster offset %#llx unaligned (guest " + "offset: %#" PRIx64 ")", + entry & L2E_OFFSET_MASK, guest_offset); + ret = -EIO; + goto fail; + } + /* Try to reuse preallocated zero clusters; contiguous normal clusters * would be fine, too, but count_cow_clusters() above has limited * nb_clusters already to a range of COW clusters */ - int preallocated_nb_clusters = + preallocated_nb_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, &l2_table[l2_index], QCOW_OFLAG_COPIED); assert(preallocated_nb_clusters > 0); diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 60b8eef3e8..3de1ab51ba 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -3077,16 +3077,40 @@ done: return ret; } +static int64_t get_refblock_offset(BlockDriverState *bs, uint64_t offset) +{ + BDRVQcow2State *s = bs->opaque; + uint32_t index = offset_to_reftable_index(s, offset); + int64_t covering_refblock_offset = 0; + + if (index < s->refcount_table_size) { + covering_refblock_offset = s->refcount_table[index] & REFT_OFFSET_MASK; + } + if (!covering_refblock_offset) { + qcow2_signal_corruption(bs, true, -1, -1, "Refblock at %#" PRIx64 " is " + "not covered by the refcount structures", + offset); + return -EIO; + } + + return covering_refblock_offset; +} + static int qcow2_discard_refcount_block(BlockDriverState *bs, uint64_t discard_block_offs) { BDRVQcow2State *s = bs->opaque; - uint64_t refblock_offs = get_refblock_offset(s, discard_block_offs); + int64_t refblock_offs; uint64_t cluster_index = discard_block_offs >> s->cluster_bits; uint32_t block_index = cluster_index & (s->refcount_block_size - 1); void *refblock; int ret; + refblock_offs = get_refblock_offset(bs, discard_block_offs); + if (refblock_offs < 0) { + return refblock_offs; + } + assert(discard_block_offs != 0); ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs, diff --git a/block/qcow2.c b/block/qcow2.c index b3d66a0e88..1914a940e5 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -376,6 +376,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, default: /* unknown magic - save it in case we need to rewrite the header */ + /* If you add a new feature, make sure to also update the fast + * path of qcow2_make_empty() to deal with it. */ { Qcow2UnknownHeaderExtension *uext; @@ -1475,7 +1477,10 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, BdrvCheckResult result = {0}; ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS); - if (ret < 0) { + if (ret < 0 || result.check_errors) { + if (ret >= 0) { + ret = -EIO; + } error_setg_errno(errp, -ret, "Could not repair dirty image"); goto fail; } @@ -3356,6 +3361,10 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL); } + if (offset_into_cluster(s, offset)) { + return -EINVAL; + } + buf = qemu_blockalign(bs, s->cluster_size); if (bytes != s->cluster_size) { if (bytes > s->cluster_size || @@ -3600,13 +3609,16 @@ static int qcow2_make_empty(BlockDriverState *bs) l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); - if (s->qcow_version >= 3 && !s->snapshots && - 3 + l1_clusters <= s->refcount_block_size) { - /* The following function only works for qcow2 v3 images (it requires - * the dirty flag) and only as long as there are no snapshots (because - * it completely empties the image). Furthermore, the L1 table and three - * additional clusters (image header, refcount table, one refcount - * block) have to fit inside one refcount block. */ + if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps && + 3 + l1_clusters <= s->refcount_block_size && + s->crypt_method_header != QCOW_CRYPT_LUKS) { + /* The following function only works for qcow2 v3 images (it + * requires the dirty flag) and only as long as there are no + * features that reserve extra clusters (such as snapshots, + * LUKS header, or persistent bitmaps), because it completely + * empties the image. Furthermore, the L1 table and three + * additional clusters (image header, refcount table, one + * refcount block) have to fit inside one refcount block. */ return make_completely_empty(bs); } @@ -4069,6 +4081,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, error_report("Changing the encryption format is not supported"); return -ENOTSUP; } + } else if (g_str_has_prefix(desc->name, "encrypt.")) { + error_report("Changing the encryption parameters is not supported"); + return -ENOTSUP; } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size); diff --git a/block/qcow2.h b/block/qcow2.h index 782a206ecb..6f0ff15dd0 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -527,12 +527,6 @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset) return offset >> (s->refcount_block_bits + s->cluster_bits); } -static inline uint64_t get_refblock_offset(BDRVQcow2State *s, uint64_t offset) -{ - uint32_t index = offset_to_reftable_index(s, offset); - return s->refcount_table[index] & REFT_OFFSET_MASK; -} - /* qcow2.c functions */ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, int64_t sector_num, int nb_sectors); diff --git a/block/rbd.c b/block/rbd.c index 144f350e1f..a76a5e8755 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -665,10 +665,16 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, /* If we are using an rbd snapshot, we must be r/o, otherwise * leave as-is */ if (s->snap != NULL) { - r = bdrv_set_read_only(bs, true, &local_err); - if (r < 0) { - error_propagate(errp, local_err); - goto failed_open; + if (!bdrv_is_read_only(bs)) { + error_report("Opening rbd snapshots without an explicit " + "read-only=on option is deprecated. Future versions " + "will refuse to open the image instead of " + "automatically marking the image read-only."); + r = bdrv_set_read_only(bs, true, &local_err); + if (r < 0) { + error_propagate(errp, local_err); + goto failed_open; + } } } diff --git a/block/replication.c b/block/replication.c index 3a4e6822e4..e41e293d2b 100644 --- a/block/replication.c +++ b/block/replication.c @@ -161,10 +161,13 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { - *nperm = *nshared = BLK_PERM_CONSISTENT_READ \ - | BLK_PERM_WRITE \ - | BLK_PERM_WRITE_UNCHANGED; - + *nperm = BLK_PERM_CONSISTENT_READ; + if ((bs->open_flags & (BDRV_O_INACTIVE | BDRV_O_RDWR)) == BDRV_O_RDWR) { + *nperm |= BLK_PERM_WRITE; + } + *nshared = BLK_PERM_CONSISTENT_READ \ + | BLK_PERM_WRITE \ + | BLK_PERM_WRITE_UNCHANGED; return; } @@ -339,12 +342,24 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp) return; } + if (!s->active_disk->bs->drv) { + error_setg(errp, "Active disk %s is ejected", + s->active_disk->bs->node_name); + return; + } + ret = s->active_disk->bs->drv->bdrv_make_empty(s->active_disk->bs); if (ret < 0) { error_setg(errp, "Cannot make active disk empty"); return; } + if (!s->hidden_disk->bs->drv) { + error_setg(errp, "Hidden disk %s is ejected", + s->hidden_disk->bs->node_name); + return; + } + ret = s->hidden_disk->bs->drv->bdrv_make_empty(s->hidden_disk->bs); if (ret < 0) { error_setg(errp, "Cannot make hidden disk empty"); @@ -508,6 +523,9 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, return; } + /* Must be true, or the bdrv_getlength() calls would have failed */ + assert(s->active_disk->bs->drv && s->hidden_disk->bs->drv); + if (!s->active_disk->bs->drv->bdrv_make_empty || !s->hidden_disk->bs->drv->bdrv_make_empty) { error_setg(errp, diff --git a/block/snapshot.c b/block/snapshot.c index 1d5ab5f90f..be0743abac 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -417,6 +417,7 @@ bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs) } aio_context_release(ctx); if (!ok) { + bdrv_next_cleanup(&it); goto fail; } } @@ -444,6 +445,7 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs, } aio_context_release(ctx); if (ret < 0) { + bdrv_next_cleanup(&it); goto fail; } } @@ -469,6 +471,7 @@ int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs) } aio_context_release(ctx); if (err < 0) { + bdrv_next_cleanup(&it); goto fail; } } @@ -494,6 +497,7 @@ int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs) } aio_context_release(ctx); if (err < 0) { + bdrv_next_cleanup(&it); goto fail; } } @@ -525,6 +529,7 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, } aio_context_release(ctx); if (err < 0) { + bdrv_next_cleanup(&it); goto fail; } } @@ -548,6 +553,7 @@ BlockDriverState *bdrv_all_find_vmstate_bs(void) aio_context_release(ctx); if (found) { + bdrv_next_cleanup(&it); break; } } diff --git a/block/vvfat.c b/block/vvfat.c index a0f2335894..a690595f2c 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1259,7 +1259,11 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, "Unable to set VVFAT to 'rw' when drive is read-only"); goto fail; } - } else { + } else if (!bdrv_is_read_only(bs)) { + error_report("Opening non-rw vvfat images without an explicit " + "read-only=on option is deprecated. Future versions " + "will refuse to open the image instead of " + "automatically marking the image read-only."); /* read only is the default for safety */ ret = bdrv_set_read_only(bs, true, &local_err); if (ret < 0) { @@ -2943,7 +2947,7 @@ static int do_commit(BDRVVVFATState* s) return ret; } - if (s->qcow->bs->drv->bdrv_make_empty) { + if (s->qcow->bs->drv && s->qcow->bs->drv->bdrv_make_empty) { s->qcow->bs->drv->bdrv_make_empty(s->qcow->bs); } |