aboutsummaryrefslogtreecommitdiff
path: root/block.c
diff options
context:
space:
mode:
Diffstat (limited to 'block.c')
-rw-r--r--block.c41
1 files changed, 38 insertions, 3 deletions
diff --git a/block.c b/block.c
index 3feac08535..5ba3435f8f 100644
--- a/block.c
+++ b/block.c
@@ -1137,7 +1137,7 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
static void update_flags_from_options(int *flags, QemuOpts *opts)
{
- *flags &= ~BDRV_O_CACHE_MASK;
+ *flags &= ~(BDRV_O_CACHE_MASK | BDRV_O_RDWR | BDRV_O_AUTO_RDONLY);
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH));
if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
@@ -1149,8 +1149,6 @@ static void update_flags_from_options(int *flags, QemuOpts *opts)
*flags |= BDRV_O_NOCACHE;
}
- *flags &= ~BDRV_O_RDWR;
-
assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY));
if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) {
*flags |= BDRV_O_RDWR;
@@ -2262,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().
@@ -2269,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);
}
@@ -2284,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);
}
@@ -3836,6 +3855,8 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
const char *backing_file_str)
{
+ BlockDriverState *explicit_top = top;
+ bool update_inherits_from;
BdrvChild *c, *next;
Error *local_err = NULL;
int ret = -EIO;
@@ -3851,6 +3872,16 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
goto exit;
}
+ /* If 'base' recursively inherits from 'top' then we should set
+ * base->inherits_from to top->inherits_from after 'top' and all
+ * other intermediate nodes have been dropped.
+ * If 'top' is an implicit node (e.g. "commit_top") we should skip
+ * it because no one inherits from it. We use explicit_top for that. */
+ while (explicit_top && explicit_top->implicit) {
+ explicit_top = backing_bs(explicit_top);
+ }
+ update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top);
+
/* success - we can delete the intermediate states, and link top->base */
/* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
* we've figured out how they should work. */
@@ -3886,6 +3917,10 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
bdrv_unref(top);
}
+ if (update_inherits_from) {
+ base->inherits_from = explicit_top->inherits_from;
+ }
+
ret = 0;
exit:
bdrv_unref(top);