aboutsummaryrefslogtreecommitdiff
path: root/block
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2017-11-17 19:08:07 +0000
committerPeter Maydell <peter.maydell@linaro.org>2017-11-17 19:08:07 +0000
commit2e02083438962d26ef9dcc7100f3b378104183db (patch)
treecff9297aa3887b7d70c3250914b76277b65ee1e9 /block
parent085ee6d282d38b430c850900c051e6b9e8c1681f (diff)
parentd5a49c6e7d9e42059450674ec845b7bc0d62cb7e (diff)
downloadqemu-2e02083438962d26ef9dcc7100f3b378104183db.zip
qemu-2e02083438962d26ef9dcc7100f3b378104183db.tar.gz
qemu-2e02083438962d26ef9dcc7100f3b378104183db.tar.bz2
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.c48
-rw-r--r--block/bochs.c13
-rw-r--r--block/cloop.c13
-rw-r--r--block/dmg.c12
-rw-r--r--block/io.c36
-rw-r--r--block/qapi.c8
-rw-r--r--block/qcow2-cache.c23
-rw-r--r--block/qcow2-cluster.c13
-rw-r--r--block/qcow2-refcount.c26
-rw-r--r--block/qcow2.c31
-rw-r--r--block/qcow2.h6
-rw-r--r--block/rbd.c14
-rw-r--r--block/replication.c26
-rw-r--r--block/snapshot.c6
-rw-r--r--block/vvfat.c8
15 files changed, 244 insertions, 39 deletions
diff --git a/block/block-backend.c b/block/block-backend.c
index f10b1db..5836cb3 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 a759b6e..50c6300 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 d6597fc..2be6898 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 6c0711f..c9b3c51 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 3d5ef2c..4fdf93a 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 7fa2437..fc10f0a 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 75746a7..c48ffeb 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 2e072ed..a3fec27 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 60b8eef..3de1ab5 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 b3d66a0..1914a94 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 782a206..6f0ff15 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 144f350..a76a5e8 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 3a4e682..e41e293 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 1d5ab5f..be0743a 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 a0f2335..a690595 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);
}