summaryrefslogtreecommitdiff
path: root/block.c
diff options
context:
space:
mode:
Diffstat (limited to 'block.c')
-rw-r--r--block.c68
1 files changed, 40 insertions, 28 deletions
diff --git a/block.c b/block.c
index ab354ba82a..1b098d4d09 100644
--- a/block.c
+++ b/block.c
@@ -985,14 +985,26 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base,
const char *filename, Error **errp)
{
BlockDriverState *parent = c->opaque;
+ int orig_flags = bdrv_get_flags(parent);
int ret;
+ if (!(orig_flags & BDRV_O_RDWR)) {
+ ret = bdrv_reopen(parent, orig_flags | BDRV_O_RDWR, errp);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
ret = bdrv_change_backing_file(parent, filename,
base->drv ? base->drv->format_name : "");
if (ret < 0) {
error_setg_errno(errp, ret, "Could not update backing file link");
}
+ if (!(orig_flags & BDRV_O_RDWR)) {
+ bdrv_reopen(parent, orig_flags, NULL);
+ }
+
return ret;
}
@@ -3482,7 +3494,7 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
BlockDriverState *base, const char *backing_file_str)
{
- BlockDriverState *new_top_bs = NULL;
+ BdrvChild *c, *next;
Error *local_err = NULL;
int ret = -EIO;
@@ -3492,42 +3504,42 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
goto exit;
}
- new_top_bs = bdrv_find_overlay(active, top);
-
- if (new_top_bs == NULL) {
- /* we could not find the image above 'top', this is an error */
- goto exit;
- }
-
- /* special case of new_top_bs->backing->bs already pointing to base - nothing
- * to do, no intermediate images */
- if (backing_bs(new_top_bs) == base) {
- ret = 0;
- goto exit;
- }
-
/* Make sure that base is in the backing chain of top */
if (!bdrv_chain_contains(top, base)) {
goto exit;
}
/* success - we can delete the intermediate states, and link top->base */
- if (new_top_bs->backing->role->update_filename) {
- backing_file_str = backing_file_str ? backing_file_str : base->filename;
- ret = new_top_bs->backing->role->update_filename(new_top_bs->backing,
- base, backing_file_str,
- &local_err);
- if (ret < 0) {
- bdrv_set_backing_hd(new_top_bs, top, &error_abort);
+ backing_file_str = backing_file_str ? backing_file_str : base->filename;
+
+ QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
+ /* Check whether we are allowed to switch c from top to base */
+ GSList *ignore_children = g_slist_prepend(NULL, c);
+ bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
+ ignore_children, &local_err);
+ if (local_err) {
+ ret = -EPERM;
+ error_report_err(local_err);
goto exit;
}
- }
+ g_slist_free(ignore_children);
- bdrv_set_backing_hd(new_top_bs, base, &local_err);
- if (local_err) {
- ret = -EPERM;
- error_report_err(local_err);
- goto exit;
+ /* If so, update the backing file path in the image file */
+ if (c->role->update_filename) {
+ ret = c->role->update_filename(c, base, backing_file_str,
+ &local_err);
+ if (ret < 0) {
+ bdrv_abort_perm_update(base);
+ error_report_err(local_err);
+ goto exit;
+ }
+ }
+
+ /* Do the actual switch in the in-memory graph.
+ * Completes bdrv_check_update_perm() transaction internally. */
+ bdrv_ref(base);
+ bdrv_replace_child(c, base);
+ bdrv_unref(top);
}
ret = 0;