After the top image has been committed into an image in its backing chain, all images above that base image should be emptied to restore the old qemu-img commit behavior.
Signed-off-by: Max Reitz <mre...@redhat.com> --- qemu-img.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 7ee791f..42616da 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -49,6 +49,11 @@ typedef enum OutputFormat { OFORMAT_HUMAN, } OutputFormat; +typedef struct BackingList { + BlockDriverState *bs; + QSIMPLEQ_ENTRY(BackingList) list; +} BackingList; + /* Default to cache=writeback as data integrity is not important for qemu-tcg. */ #define BDRV_O_FLAGS BDRV_O_CACHE_WB #define BDRV_DEFAULT_CACHE "writeback" @@ -717,10 +722,13 @@ static int img_commit(int argc, char **argv) { int c, ret, flags; const char *filename, *fmt, *cache; - BlockDriverState *bs, *base_bs; + BlockDriverState *bs, *base_bs, *backing_bs; bool quiet = false; Error *local_err = NULL; CommonBlockJobCBInfo cbi; + BackingList *bl_element, *bl_next; + QSIMPLEQ_HEAD(, BackingList) backing_list = + QSIMPLEQ_HEAD_INITIALIZER(backing_list); fmt = NULL; cache = BDRV_DEFAULT_CACHE; @@ -750,7 +758,7 @@ static int img_commit(int argc, char **argv) } filename = argv[optind++]; - flags = BDRV_O_RDWR; + flags = BDRV_O_RDWR | BDRV_O_UNMAP; ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); @@ -771,6 +779,32 @@ static int img_commit(int argc, char **argv) goto done; } + /* Build a list of the intermediate backing images in order to be able to + * empty them later; note that base_bs is included in this list although we + * actually want to empty bs instead. As bdrv_swap() will be called on both + * by the commit block job, it is however correct to use the pointer to + * base_bs in order to clear bs. */ + backing_bs = bs; + + do { + backing_bs = backing_bs->backing_hd; + assert(backing_bs); + + bl_element = g_new(BackingList, 1); + bl_element->bs = backing_bs; + + if (backing_bs != base_bs) { + /* Generally, the images should be emptied from top to bottom in + * order to keep them consistent even if one make_empty operation + * fails because it could empty an image only partially */ + QSIMPLEQ_INSERT_TAIL(&backing_list, bl_element, list); + } else { + /* The final pointer in the backing chain will however later point + * to the image at the very top, so put it at the front instead */ + QSIMPLEQ_INSERT_HEAD(&backing_list, bl_element, list); + } + } while (backing_bs != base_bs); + cbi = (CommonBlockJobCBInfo){ .errp = &local_err, .bs = bs, @@ -779,10 +813,57 @@ static int img_commit(int argc, char **argv) commit_active_start(bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT, common_block_job_cb, &cbi, &local_err); if (local_err) { - goto done; + goto free_backing_list; + } + + /* The block job will swap base_bs and bs (which is not what we really want + * here, but okay) and unref bs (and subsequently all intermediate block + * devices). In order to be able to empty these images afterwards, increment + * the reference counter here preemptively. */ + QSIMPLEQ_FOREACH(bl_element, &backing_list, list) { + bdrv_ref(bl_element->bs); } run_block_job(bs->job, &local_err); + if (local_err) { + goto unref_backing; + } + + QSIMPLEQ_FOREACH(bl_element, &backing_list, list) { + if (bl_element->bs->drv->bdrv_make_empty) { + if (bl_element->bs->read_only) { + /* If this is an intermediate file in the backing chain between + * the top and the bottom image, it has been opened implicitly + * read-only; reopen it R/W for emptying */ + ret = bdrv_reopen(bl_element->bs, flags, &local_err); + if (ret < 0 || local_err) { + if (!local_err) { + error_setg_errno(&local_err, -ret, + "Could not write to %s", + bl_element->bs->filename); + } + goto unref_backing; + } + } + + ret = bl_element->bs->drv->bdrv_make_empty(bl_element->bs); + if (ret) { + error_setg_errno(&local_err, -ret, "Could not empty %s", + bl_element->bs->filename); + goto unref_backing; + } + } + } + +unref_backing: + QSIMPLEQ_FOREACH(bl_element, &backing_list, list) { + bdrv_unref(bl_element->bs); + } + +free_backing_list: + QSIMPLEQ_FOREACH_SAFE(bl_element, &backing_list, list, bl_next) { + g_free(bl_element); + } done: bdrv_unref(bs); -- 1.9.2