From 40365552c2fceacc9800ec9a87b9ead516a9a6ce Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 28 Oct 2015 13:24:26 +0100 Subject: [PATCH] mirror: Error out when a BDS would get two BBs bdrv_replace_in_backing_chain() asserts that not both old and new BlockDdriverState have a BlockBackend attached to them because both would have to end up pointing to the new BDS and we don't support more than one BB per BDS yet. Before we can safely allow references to existing nodes as backing files, we need to make sure that even if a backing file has a BB on it, this doesn't crash qemu. There are probably also some cases with the 'replaces' option set where drive-mirror could fail this assertion today. They are fixed with this error check as well. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/mirror.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/block/mirror.c b/block/mirror.c index 0e8f556..8e3f524 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -18,6 +18,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" #include "qemu/bitmap.h" +#include "qemu/error-report.h" #define SLICE_TIME 100000000ULL /* ns */ #define MAX_IN_FLIGHT 16 @@ -370,11 +371,22 @@ static void mirror_exit(BlockJob *job, void *opaque) if (s->to_replace) { to_replace = s->to_replace; } + + /* This was checked in mirror_start_job(), but meanwhile one of the + * nodes could have been newly attached to a BlockBackend. */ + if (to_replace->blk && s->target->blk) { + error_report("block job: Can't create node with two BlockBackends"); + data->ret = -EINVAL; + goto out; + } + if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); } bdrv_replace_in_backing_chain(to_replace, s->target); } + +out: if (s->to_replace) { bdrv_op_unblock_all(s->to_replace, s->replace_blocker); error_free(s->replace_blocker); @@ -705,6 +717,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, bool is_none_mode, BlockDriverState *base) { MirrorBlockJob *s; + BlockDriverState *replaced_bs; if (granularity == 0) { granularity = bdrv_get_default_bitmap_granularity(target); @@ -728,6 +741,21 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, buf_size = DEFAULT_MIRROR_BUF_SIZE; } + /* We can't support this case as long as the block layer can't handle + * multiple BlockBackends per BlockDriverState. */ + if (replaces) { + replaced_bs = bdrv_lookup_bs(replaces, replaces, errp); + if (replaced_bs == NULL) { + return; + } + } else { + replaced_bs = bs; + } + if (replaced_bs->blk && target->blk) { + error_setg(errp, "Can't create node with two BlockBackends"); + return; + } + s = block_job_create(driver, bs, speed, cb, opaque, errp); if (!s) { return; -- 2.7.4