From e4603fe139e2161464d7e75faa3a650e31f057fc Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 7 Apr 2015 15:03:16 +0200 Subject: qcow2: Fix header update with overridden backing file In recent qemu versions, it is possible to override the backing file name and format that is stored in the image file with values given at runtime. In such cases, the temporary override could end up in the image header if the qcow2 header was updated, while obviously correct behaviour would be to leave the on-disk backing file path/format unchanged. Fix this and add a test case for it. Reported-by: Michael Tokarev Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Message-id: 1428411796-2852-1-git-send-email-kwolf@redhat.com Signed-off-by: Stefan Hajnoczi --- block/qcow2.c | 29 ++++++++++++++++++++++------- block/qcow2.h | 6 ++++++ 2 files changed, 28 insertions(+), 7 deletions(-) (limited to 'block') diff --git a/block/qcow2.c b/block/qcow2.c index 32bdf75..316a8db 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -140,6 +140,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, return 3; } bs->backing_format[ext.len] = '\0'; + s->image_backing_format = g_strdup(bs->backing_format); #ifdef DEBUG_EXT printf("Qcow2: Got format extension %s\n", bs->backing_format); #endif @@ -884,6 +885,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } bs->backing_file[len] = '\0'; + s->image_backing_file = g_strdup(bs->backing_file); } /* Internal snapshots */ @@ -1457,6 +1459,9 @@ static void qcow2_close(BlockDriverState *bs) g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); + g_free(s->image_backing_file); + g_free(s->image_backing_format); + g_free(s->cluster_cache); qemu_vfree(s->cluster_data); qcow2_refcount_close(bs); @@ -1622,9 +1627,10 @@ int qcow2_update_header(BlockDriverState *bs) } /* Backing file format header extension */ - if (*bs->backing_format) { + if (s->image_backing_format) { ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT, - bs->backing_format, strlen(bs->backing_format), + s->image_backing_format, + strlen(s->image_backing_format), buflen); if (ret < 0) { goto fail; @@ -1682,8 +1688,8 @@ int qcow2_update_header(BlockDriverState *bs) buflen -= ret; /* Backing file name */ - if (*bs->backing_file) { - size_t backing_file_len = strlen(bs->backing_file); + if (s->image_backing_file) { + size_t backing_file_len = strlen(s->image_backing_file); if (buflen < backing_file_len) { ret = -ENOSPC; @@ -1691,7 +1697,7 @@ int qcow2_update_header(BlockDriverState *bs) } /* Using strncpy is ok here, since buf is not NUL-terminated. */ - strncpy(buf, bs->backing_file, buflen); + strncpy(buf, s->image_backing_file, buflen); header->backing_file_offset = cpu_to_be64(buf - ((char*) header)); header->backing_file_size = cpu_to_be32(backing_file_len); @@ -1712,9 +1718,17 @@ fail: static int qcow2_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt) { + BDRVQcowState *s = bs->opaque; + pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); + g_free(s->image_backing_file); + g_free(s->image_backing_format); + + s->image_backing_file = backing_file ? g_strdup(bs->backing_file) : NULL; + s->image_backing_format = backing_fmt ? g_strdup(bs->backing_format) : NULL; + return qcow2_update_header(bs); } @@ -2751,8 +2765,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } if (backing_file || backing_format) { - ret = qcow2_change_backing_file(bs, backing_file ?: bs->backing_file, - backing_format ?: bs->backing_format); + ret = qcow2_change_backing_file(bs, + backing_file ?: s->image_backing_file, + backing_format ?: s->image_backing_format); if (ret < 0) { return ret; } diff --git a/block/qcow2.h b/block/qcow2.h index aa6d367..422b825 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -283,6 +283,12 @@ typedef struct BDRVQcowState { QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; QTAILQ_HEAD (, Qcow2DiscardRegion) discards; bool cache_discards; + + /* Backing file path and format as stored in the image (this is not the + * effective path/format, which may be the result of a runtime option + * override) */ + char *image_backing_file; + char *image_backing_format; } BDRVQcowState; struct QCowAIOCB; -- cgit v1.1