Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 340e01248478ba8b78a6d4d1809b1eff > files > 278

kvm-83-270.el5_11.src.rpm

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