Patchwork [1/2] qcow2: Simplify image creation

login
register
mail settings
Submitter Kevin Wolf
Date June 14, 2010, 2:43 p.m.
Message ID <1276526595-32658-2-git-send-email-kwolf@redhat.com>
Download mbox | patch
Permalink /patch/55541/
State New
Headers show

Comments

Kevin Wolf - June 14, 2010, 2:43 p.m.
Instead of doing lots of magic for setting up initial refcount blocks and stuff
create a minimal (inconsistent) image, open it and initialize the rest with
regular qcow2 functions.

This is a complete rewrite of the image creation function. The old
implementating is #ifdef'd out and will be removed by the next patch (removing
it here would have made the diff unreadable because diff tries to find
similarities when it's really a rewrite)

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/qcow2.c |  126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 125 insertions(+), 1 deletions(-)
Stefan Hajnoczi - June 15, 2010, 10:14 a.m.
On Mon, Jun 14, 2010 at 3:43 PM, Kevin Wolf <kwolf@redhat.com> wrote:
> Instead of doing lots of magic for setting up initial refcount blocks and stuff
> create a minimal (inconsistent) image, open it and initialize the rest with
> regular qcow2 functions.

Nice idea.

> +    ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
> +    if (ret < 0) {
> +        return ret;
> +    }

The bs is not closed on error.  Also, this function will leave a
partially created file on disk if it fails.

> +    /* And if we're supposed to preallocate metadata, do that now */
> +    if (prealloc) {
> +        preallocate(bs);
> +    }
> +
> +    return 0;
> +}

The bs is leaked when the function succeeds.

Stefan
Kevin Wolf - June 15, 2010, 10:31 a.m.
Am 15.06.2010 12:14, schrieb Stefan Hajnoczi:
> On Mon, Jun 14, 2010 at 3:43 PM, Kevin Wolf <kwolf@redhat.com> wrote:
>> Instead of doing lots of magic for setting up initial refcount blocks and stuff
>> create a minimal (inconsistent) image, open it and initialize the rest with
>> regular qcow2 functions.
> 
> Nice idea.
> 
>> +    ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
>> +    if (ret < 0) {
>> +        return ret;
>> +    }
> 
> The bs is not closed on error.  

Right, will fix the missing bdrv_delete here and in other cases.

> Also, this function will leave a
> partially created file on disk if it fails.

As did the old one, and the bdrv_create functions of most other formats
behave the same. We could implement a bdrv_remove to remove that file
again, but it would be ununsed except for these very unlikely error
cases. If you can't access the disk, usually the bdrv_create_file would
fail already.

Kevin

Patch

diff --git a/block/qcow2.c b/block/qcow2.c
index 33fa9a9..acb850c 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -27,6 +27,7 @@ 
 #include <zlib.h>
 #include "aes.h"
 #include "block/qcow2.h"
+#include "qemu-error.h"
 
 /*
   Differences with QCOW:
@@ -767,6 +768,7 @@  static int qcow2_change_backing_file(BlockDriverState *bs,
     return qcow2_update_ext_header(bs, backing_file, backing_fmt);
 }
 
+#if 0
 static int get_bits_from_size(size_t size)
 {
     int res = 0;
@@ -787,6 +789,7 @@  static int get_bits_from_size(size_t size)
 
     return res;
 }
+#endif
 
 
 static int preallocate(BlockDriverState *bs)
@@ -839,6 +842,7 @@  static int preallocate(BlockDriverState *bs)
     return 0;
 }
 
+#if 0
 static int qcow_create2(const char *filename, int64_t total_size,
                         const char *backing_file, const char *backing_format,
                         int flags, size_t cluster_size, int prealloc)
@@ -1036,6 +1040,126 @@  exit:
 
     return ret;
 }
+#else
+static int qcow_create2(const char *filename, int64_t total_size,
+                        const char *backing_file, const char *backing_format,
+                        int flags, size_t cluster_size, int prealloc,
+                        QEMUOptionParameter *options)
+{
+    /* Calulate cluster_bits */
+    int cluster_bits;
+    cluster_bits = ffs(cluster_size) - 1;
+    if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
+        (1 << cluster_bits) != cluster_size)
+    {
+        error_report(
+            "Cluster size must be a power of two between %d and %dk\n",
+            1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
+        return -EINVAL;
+    }
+
+    /*
+     * Open the image file and write a minimal qcow2 header.
+     *
+     * We keep things simple and start with a zero-sized image. We also
+     * do without refcount blocks or a L1 table for now. We'll fix the
+     * inconsistency later.
+     *
+     * We do need a refcount table because growing the refcount table means
+     * allocating two new refcount blocks - the seconds of which would be at
+     * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
+     * size for any qcow2 image.
+     */
+    BlockDriverState* bs;
+    QCowHeader header;
+    uint8_t* refcount_table;
+    int ret;
+
+    ret = bdrv_create_file(filename, options);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR);
+    if (ret < 0) {
+        return ret;
+    }
+
+    /* Write the header */
+    memset(&header, 0, sizeof(header));
+    header.magic = cpu_to_be32(QCOW_MAGIC);
+    header.version = cpu_to_be32(QCOW_VERSION);
+    header.cluster_bits = cpu_to_be32(cluster_bits);
+    header.size = cpu_to_be64(0);
+    header.l1_table_offset = cpu_to_be64(0);
+    header.l1_size = cpu_to_be32(0);
+    header.refcount_table_offset = cpu_to_be64(cluster_size);
+    header.refcount_table_clusters = cpu_to_be32(1);
+
+    if (flags & BLOCK_FLAG_ENCRYPT) {
+        header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
+    } else {
+        header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+    }
+
+    ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
+    if (ret < 0) {
+        return ret;
+    }
+
+    /* Write an empty refcount table */
+    refcount_table = qemu_mallocz(cluster_size);
+    ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size);
+    qemu_free(refcount_table);
+
+    if (ret < 0) {
+        return ret;
+    }
+
+    bdrv_close(bs);
+
+    /*
+     * And now open the image and make it consistent first (i.e. increase the
+     * refcount of the cluster that is occupied by the header and the refcount
+     * table)
+     */
+    BlockDriver* drv = bdrv_find_format("qcow2");
+    assert(drv != NULL);
+    ret = bdrv_open(bs, filename, BDRV_O_RDWR | BDRV_O_NO_FLUSH, drv);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
+    if (ret < 0) {
+        return ret;
+    } else if (ret != 0) {
+        error_report("Huh, first cluster in empty image is already in use?");
+        abort();
+    }
+
+    /* Okay, now that we have a valid image, let's give it the right size */
+    ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE);
+    if (ret < 0) {
+        return ret;
+    }
+
+    /* Want a backing file? There you go.*/
+    if (backing_file) {
+        ret = bdrv_change_backing_file(bs, backing_file, backing_format);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    /* And if we're supposed to preallocate metadata, do that now */
+    if (prealloc) {
+        preallocate(bs);
+    }
+
+    return 0;
+}
+#endif
 
 static int qcow_create(const char *filename, QEMUOptionParameter *options)
 {
@@ -1081,7 +1205,7 @@  static int qcow_create(const char *filename, QEMUOptionParameter *options)
     }
 
     return qcow_create2(filename, sectors, backing_file, backing_fmt, flags,
-        cluster_size, prealloc);
+        cluster_size, prealloc, options);
 }
 
 static int qcow_make_empty(BlockDriverState *bs)