aboutsummaryrefslogtreecommitdiff
path: root/block.c
diff options
context:
space:
mode:
Diffstat (limited to 'block.c')
-rw-r--r--block.c21
1 files changed, 21 insertions, 0 deletions
diff --git a/block.c b/block.c
index 9cd6f4a50d..fe0cdc7d99 100644
--- a/block.c
+++ b/block.c
@@ -2260,6 +2260,18 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load)
}
}
+/* Return true if you can reach parent going through child->inherits_from
+ * recursively. If parent or child are NULL, return false */
+static bool bdrv_inherits_from_recursive(BlockDriverState *child,
+ BlockDriverState *parent)
+{
+ while (child && child != parent) {
+ child = child->inherits_from;
+ }
+
+ return child != NULL;
+}
+
/*
* Sets the backing file link of a BDS. A new reference is created; callers
* which don't need their own reference any more must call bdrv_unref().
@@ -2267,6 +2279,9 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load)
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
Error **errp)
{
+ bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
+ bdrv_inherits_from_recursive(backing_hd, bs);
+
if (backing_hd) {
bdrv_ref(backing_hd);
}
@@ -2282,6 +2297,12 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
errp);
+ /* If backing_hd was already part of bs's backing chain, and
+ * inherits_from pointed recursively to bs then let's update it to
+ * point directly to bs (else it will become NULL). */
+ if (update_inherits_from) {
+ backing_hd->inherits_from = bs;
+ }
if (!bs->backing) {
bdrv_unref(backing_hd);
}