From 4af4fa7a094fc9b2b6a0d8c6be3121b4a7a51a7d Mon Sep 17 00:00:00 2001 From: Kevin Wolf <kwolf@redhat.com> Date: Tue, 12 Jan 2010 12:35:36 -0200 Subject: [PATCH 4/5] block: Add bdrv_change_backing_file RH-Author: Kevin Wolf <kwolf@redhat.com> Message-id: <1263299736-12361-1-git-send-email-kwolf@redhat.com> Patchwork-id: 6197 O-Subject: [RHEL-5.5 KVM PATCH v4 2/3] block: Add bdrv_change_backing_file Bugzilla: 530134 RH-Acked-by: Marcelo Tosatti <mtosatti@redhat.com> RH-Acked-by: Juan Quintela <quintela@redhat.com> RH-Acked-by: Gleb Natapov <gleb@redhat.com> Bugzilla: 530134 Upstream status: Submitted Introduce the functions needed to change the backing file of an image. The function is implemented for qcow2. Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- qemu/block-qcow2.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu/block.c | 20 ++++++++++ qemu/block.h | 2 + qemu/block_int.h | 3 ++ 4 files changed, 126 insertions(+), 0 deletions(-) Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> --- qemu/block-qcow2.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu/block.c | 20 ++++++++++ qemu/block.h | 2 + qemu/block_int.h | 3 ++ 4 files changed, 126 insertions(+), 0 deletions(-) diff --git a/qemu/block-qcow2.c b/qemu/block-qcow2.c index 45d53bf..ddc2e49 100644 --- a/qemu/block-qcow2.c +++ b/qemu/block-qcow2.c @@ -1601,6 +1601,105 @@ static void qcow_close(BlockDriverState *bs) bdrv_delete(s->hd); } +/* + * Updates the variable length parts of the qcow2 header, i.e. the backing file + * name and all extensions. qcow2 was not designed to allow such changes, so if + * we run out of space (we can only use the first cluster) this function may + * fail. + * + * Returns 0 on success, -errno in error cases. + */ +static int qcow2_update_ext_header(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt) +{ + size_t backing_file_len = 0; + size_t backing_fmt_len = 0; + BDRVQcowState *s = bs->opaque; + QCowExtension ext_backing_fmt = {0, 0}; + int ret; + + /* Backing file format doesn't make sense without a backing file */ + if (backing_fmt && !backing_file) { + return -EINVAL; + } + + /* Prepare the backing file format extension if needed */ + if (backing_fmt) { + ext_backing_fmt.len = cpu_to_be32(strlen(backing_fmt)); + ext_backing_fmt.magic = cpu_to_be32(QCOW_EXT_MAGIC_BACKING_FORMAT); + backing_fmt_len = ((sizeof(ext_backing_fmt) + + strlen(backing_fmt) + 7) & ~7); + } + + /* Check if we can fit the new header into the first cluster */ + if (backing_file) { + backing_file_len = strlen(backing_file); + } + + size_t header_size = sizeof(QCowHeader) + backing_file_len + + backing_fmt_len; + + if (header_size > s->cluster_size) { + return -ENOSPC; + } + + /* Rewrite backing file name and qcow2 extensions */ + size_t ext_size = header_size - sizeof(QCowHeader); + uint8_t buf[ext_size]; + size_t offset = 0; + size_t backing_file_offset = 0; + + if (backing_file) { + if (backing_fmt) { + int padding = backing_fmt_len - + (sizeof(ext_backing_fmt) + strlen(backing_fmt)); + + memcpy(buf + offset, &ext_backing_fmt, sizeof(ext_backing_fmt)); + offset += sizeof(ext_backing_fmt); + + memcpy(buf + offset, backing_fmt, strlen(backing_fmt)); + offset += strlen(backing_fmt); + + memset(buf + offset, 0, padding); + offset += padding; + } + + memcpy(buf + offset, backing_file, backing_file_len); + backing_file_offset = sizeof(QCowHeader) + offset; + } + + ret = bdrv_pwrite(s->hd, sizeof(QCowHeader), buf, ext_size); + if (ret < 0) { + goto fail; + } + + /* Update header fields */ + uint64_t be_backing_file_offset = cpu_to_be64(backing_file_offset); + uint32_t be_backing_file_size = cpu_to_be32(backing_file_len); + + ret = bdrv_pwrite(s->hd, offsetof(QCowHeader, backing_file_offset), + &be_backing_file_offset, sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + ret = bdrv_pwrite(s->hd, offsetof(QCowHeader, backing_file_size), + &be_backing_file_size, sizeof(uint32_t)); + if (ret < 0) { + goto fail; + } + + ret = 0; +fail: + return ret; +} + +static int qcow2_change_backing_file(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt) +{ + return qcow2_update_ext_header(bs, backing_file, backing_fmt); +} + /* XXX: use std qcow open function ? */ typedef struct QCowCreateState { int cluster_size; @@ -3033,6 +3132,8 @@ BlockDriver bdrv_qcow2 = { .bdrv_put_buffer = qcow_put_buffer, .bdrv_get_buffer = qcow_get_buffer, + .bdrv_change_backing_file = qcow2_change_backing_file, + .bdrv_create2 = qcow_create2, .bdrv_check = qcow_check, }; diff --git a/qemu/block.c b/qemu/block.c index 3255f39..d596941 100644 --- a/qemu/block.c +++ b/qemu/block.c @@ -612,6 +612,26 @@ int bdrv_commit(BlockDriverState *bs) return 0; } +/* + * Return values: + * 0 - success + * -EINVAL - backing format specified, but no file + * -ENOSPC - can't update the backing file because no space is left in the + * image file header + * -ENOTSUP - format driver doesn't support changing the backing file + */ +int bdrv_change_backing_file(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt) +{ + BlockDriver *drv = bs->drv; + + if (drv->bdrv_change_backing_file != NULL) { + return drv->bdrv_change_backing_file(bs, backing_file, backing_fmt); + } else { + return -ENOTSUP; + } +} + static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, size_t size) { diff --git a/qemu/block.h b/qemu/block.h index 1132d6d..753b356 100644 --- a/qemu/block.h +++ b/qemu/block.h @@ -86,6 +86,8 @@ int64_t bdrv_getlength(BlockDriverState *bs); void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); int bdrv_commit(BlockDriverState *bs); +int bdrv_change_backing_file(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt); /* async block I/O */ typedef struct BlockDriverAIOCB BlockDriverAIOCB; typedef void BlockDriverCompletionFunc(void *opaque, int ret); diff --git a/qemu/block_int.h b/qemu/block_int.h index 2df84c0..2f8093c 100644 --- a/qemu/block_int.h +++ b/qemu/block_int.h @@ -83,6 +83,9 @@ struct BlockDriver { int (*bdrv_get_buffer)(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size); + int (*bdrv_change_backing_file)(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt); + /* removable device specific */ int (*bdrv_is_inserted)(BlockDriverState *bs); int (*bdrv_media_changed)(BlockDriverState *bs); -- 1.6.3.rc4.29.g8146