From patchwork Thu Dec 10 15:30:55 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 40829 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 78D72B6F0C for ; Fri, 11 Dec 2009 02:47:43 +1100 (EST) Received: from localhost ([127.0.0.1]:35373 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NIlEy-0006LX-KG for incoming@patchwork.ozlabs.org; Thu, 10 Dec 2009 10:47:40 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1NIkzy-0006eM-Jw for qemu-devel@nongnu.org; Thu, 10 Dec 2009 10:32:10 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1NIkzt-0006Xy-Jd for qemu-devel@nongnu.org; Thu, 10 Dec 2009 10:32:10 -0500 Received: from [199.232.76.173] (port=49965 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NIkzt-0006Xe-EN for qemu-devel@nongnu.org; Thu, 10 Dec 2009 10:32:05 -0500 Received: from mx1.redhat.com ([209.132.183.28]:46674) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1NIkzt-0001pL-Eq for qemu-devel@nongnu.org; Thu, 10 Dec 2009 10:32:05 -0500 Received: from int-mx04.intmail.prod.int.phx2.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.17]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id nBAFW4CW026911 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 10 Dec 2009 10:32:04 -0500 Received: from localhost.localdomain (dhcp-5-175.str.redhat.com [10.32.5.175]) by int-mx04.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id nBAFW0dT011236; Thu, 10 Dec 2009 10:32:03 -0500 From: Kevin Wolf To: qemu-devel@nongnu.org Date: Thu, 10 Dec 2009 16:30:55 +0100 Message-Id: <1260459056-20520-3-git-send-email-kwolf@redhat.com> In-Reply-To: <1260459056-20520-1-git-send-email-kwolf@redhat.com> References: <1260459056-20520-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.17 X-detected-operating-system: by monty-python.gnu.org: Genre and OS details not recognized. Cc: Kevin Wolf Subject: [Qemu-devel] [PATCH 2/3] block: Add bdrv_change_backing_file X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Introduce the functions needed to change the backing file of an image. The function is implemented for qcow2. Signed-off-by: Kevin Wolf --- block.c | 20 ++++++++++++++ block.h | 2 + block/qcow2.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ block_int.h | 3 ++ 4 files changed, 107 insertions(+), 0 deletions(-) diff --git a/block.c b/block.c index a794875..02d7e7e 100644 --- a/block.c +++ b/block.c @@ -596,6 +596,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/block.h b/block.h index 880d450..564f0fe 100644 --- a/block.h +++ b/block.h @@ -80,6 +80,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); void bdrv_register(BlockDriver *bdrv); /* async block I/O */ diff --git a/block/qcow2.c b/block/qcow2.c index 984264b..9f87b57 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -658,6 +658,86 @@ 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}; + + /* 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 = (strlen(backing_fmt) + 7) & ~7; + ext_backing_fmt.magic = QCOW_EXT_MAGIC_BACKING_FORMAT; + backing_fmt_len = ((sizeof(ext_backing_fmt) + + ext_backing_fmt.len + 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; + + if (backing_file) { + if (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, ext_backing_fmt.len - strlen(backing_fmt)); + offset += ext_backing_fmt.len - strlen(backing_fmt); + } + memcpy(buf + offset, backing_file, backing_file_len); + } + + bdrv_pwrite(s->hd, sizeof(QCowHeader), buf, ext_size); + + /* Update header fields */ + uint64_t backing_file_offset = cpu_to_be64(header_size - ext_size); + uint32_t backing_file_size = cpu_to_be32(backing_file_len); + + bdrv_pwrite(s->hd, offsetof(QCowHeader, backing_file_offset), + &backing_file_offset, sizeof(uint64_t)); + bdrv_pwrite(s->hd, offsetof(QCowHeader, backing_file_size), + &backing_file_size, sizeof(uint32_t)); + + return 0; +} + +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); +} + static int get_bits_from_size(size_t size) { int res = 0; @@ -1137,6 +1217,8 @@ static BlockDriver bdrv_qcow2 = { .bdrv_save_vmstate = qcow_save_vmstate, .bdrv_load_vmstate = qcow_load_vmstate, + .bdrv_change_backing_file = qcow2_change_backing_file, + .create_options = qcow_create_options, .bdrv_check = qcow_check, }; diff --git a/block_int.h b/block_int.h index 9a3b2e0..a0ebd90 100644 --- a/block_int.h +++ b/block_int.h @@ -98,6 +98,9 @@ struct BlockDriver { int (*bdrv_load_vmstate)(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);