From 2f51b8136807041d1585dd5a740190c15dfbadd1 Mon Sep 17 00:00:00 2001
From: Stefan Weil <sw@weilnetz.de>
Date: Wed, 30 Jan 2013 16:24:34 +0100
Subject: [PATCH 2/2] block/vpc: Improve code

Signed-off-by: Stefan Weil <sw@weilnetz.de>
---
 block/vpc.c |   98 +++++++++++++++++++++++++++++-----------------------------
 1 files changed, 49 insertions(+), 49 deletions(-)

diff --git a/block/vpc.c b/block/vpc.c
index 7948609..5e43934 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -21,6 +21,9 @@
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
+ *
+ * See http://en.wikipedia.org/wiki/VHD_(file_format) for more information
+ * and links to the VHD specification.
  */
 #include "qemu-common.h"
 #include "block/block_int.h"
@@ -32,10 +35,13 @@
 
 /**************************************************************/
 
-#define HEADER_SIZE 512
+#define FOOTER_SIZE 512
 
 //#define CACHE
 
+/* By default, VHD uses a block size of 2 MiB for dynamic disk images. */
+#define VHD_DEFAULT_BLOCK_SIZE 0x200000
+
 enum vhd_type {
     VHD_FIXED           = 2,
     VHD_DYNAMIC         = 3,
@@ -51,7 +57,7 @@ struct vhd_footer {
     uint32_t    features;
     uint32_t    version;
 
-    // Offset of next header structure, 0xFFFFFFFF if none
+    // Offset of first dyndisk header structure, 0xFFFFFFFF if none
     uint64_t    data_offset;
 
     // Seconds since Jan 1, 2000 0:00:00 (UTC)
@@ -115,7 +121,7 @@ struct vhd_dyndisk_header {
 
 typedef struct BDRVVPCState {
     CoMutex lock;
-    uint8_t footer_buf[HEADER_SIZE];
+    uint8_t footer_buf[FOOTER_SIZE];
     uint64_t free_data_block_offset;
     int max_table_entries;
     uint32_t *pagetable;
@@ -141,8 +147,9 @@ static uint32_t vpc_checksum(uint8_t* buf, size_t size)
     uint32_t res = 0;
     int i;
 
-    for (i = 0; i < size; i++)
+    for (i = 0; i < size; i++) {
         res += buf[i];
+    }
 
     return ~res;
 }
@@ -150,8 +157,9 @@ static uint32_t vpc_checksum(uint8_t* buf, size_t size)
 
 static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
 {
-    if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
-	return 100;
+    if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) {
+        return 100;
+    }
     return 0;
 }
 
@@ -159,47 +167,47 @@ static int vpc_open(BlockDriverState *bs, int flags)
 {
     BDRVVPCState *s = bs->opaque;
     int i;
-    struct vhd_footer* footer;
+    struct vhd_footer* footer = s->footer_buf;
     struct vhd_dyndisk_header* dyndisk_header;
-    uint8_t buf[HEADER_SIZE];
+    uint8_t buf[FOOTER_SIZE];
     uint32_t checksum;
     int err = -1;
     int disk_type = VHD_DYNAMIC;
+    int64_t offset = bdrv_getlength(bs->file);
 
-    if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
+    if (offset < FOOTER_SIZE) {
         goto fail;
+    }
+
+    /* The footer is always found at the end of the file. */
+    if (bdrv_pread(bs->file, offset-FOOTER_SIZE, s->footer_buf, FOOTER_SIZE)
+                != FOOTER_SIZE) {
+        goto fail;
+    }
 
-    footer = (struct vhd_footer*) s->footer_buf;
     if (strncmp(footer->creator, "conectix", 8)) {
-        int64_t offset = bdrv_getlength(bs->file);
-        if (offset < HEADER_SIZE) {
-            goto fail;
-        }
-        /* If a fixed disk, the footer is found only at the end of the file */
-        if (bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE)
-                != HEADER_SIZE) {
-            goto fail;
-        }
+        /* Some VHD versions use a footer with only 511 byte. Try it, too. */
+        memmove(s->footer_buf, s->footer_buf + 1, FOOTER_SIZE - 1);
         if (strncmp(footer->creator, "conectix", 8)) {
             goto fail;
         }
-        disk_type = VHD_FIXED;
     }
 
+    disk_type = be32_to_cpu(footer->type);
     checksum = be32_to_cpu(footer->checksum);
     footer->checksum = 0;
-    if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum)
-        fprintf(stderr, "block-vpc: The header checksum of '%s' is "
+    if (vpc_checksum(s->footer_buf, FOOTER_SIZE) != checksum) {
+        fprintf(stderr, "block-vpc: The footer checksum of '%s' is "
             "incorrect.\n", bs->filename);
+    }
 
     /* Write 'checksum' back to footer, or else will leave it with zero. */
     footer->checksum = be32_to_cpu(checksum);
 
-    // The visible size of a image in Virtual PC depends on the geometry
+    // The visible size of an image in Virtual PC may depend on the geometry
     // rather than on the size stored in the footer (the size in the footer
-    // is too large usually)
-    bs->total_sectors = (int64_t)
-        be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
+    // is usually larger). Nevertheless we must use the real size here.
+    bs->total_sectors = be64_to_cpu(footer->size) / 512;
 
     /* Allow a maximum disk size of approximately 2 TB */
     if (bs->total_sectors >= 65535LL * 255 * 255) {
@@ -209,7 +217,7 @@ static int vpc_open(BlockDriverState *bs, int flags)
 
     if (disk_type == VHD_DYNAMIC) {
         if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf,
-                HEADER_SIZE) != HEADER_SIZE) {
+                FOOTER_SIZE) != FOOTER_SIZE) {
             goto fail;
         }
 
@@ -359,7 +367,7 @@ static int rewrite_footer(BlockDriverState* bs)
     BDRVVPCState *s = bs->opaque;
     int64_t offset = s->free_data_block_offset;
 
-    ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE);
+    ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, FOOTER_SIZE);
     if (ret < 0)
         return ret;
 
@@ -585,17 +593,17 @@ static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors)
     int ret = -EIO;
 
     // Write the footer (twice: at the beginning and at the end)
-    block_size = 0x200000;
+    block_size = VHD_DEFAULT_BLOCK_SIZE;
     num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
 
-    if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
+    if (write(fd, buf, FOOTER_SIZE) != FOOTER_SIZE) {
         goto fail;
     }
 
     if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) {
         goto fail;
     }
-    if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
+    if (write(fd, buf, FOOTER_SIZE) != FOOTER_SIZE) {
         goto fail;
     }
 
@@ -655,7 +663,7 @@ static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size)
     if (lseek(fd, -512, SEEK_END) < 0) {
         goto fail;
     }
-    if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
+    if (write(fd, buf, FOOTER_SIZE) != FOOTER_SIZE) {
         goto fail;
     }
 
@@ -670,7 +678,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
     uint8_t buf[1024];
     struct vhd_footer *footer = (struct vhd_footer *) buf;
     QEMUOptionParameter *disk_type_param;
-    int fd, i;
+    int fd;
     uint16_t cyls = 0;
     uint8_t heads = 0;
     uint8_t secs_per_cyl = 0;
@@ -702,22 +710,15 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
     }
 
     /*
-     * Calculate matching total_size and geometry. Increase the number of
-     * sectors requested until we get enough (or fail). This ensures that
-     * qemu-img convert doesn't truncate images, but rather rounds up.
+     * Calculate geometry. For guests which use the geometry values,
+     * the last blocks may be invisible.
      */
     total_sectors = total_size / BDRV_SECTOR_SIZE;
-    for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
-        if (calculate_geometry(total_sectors + i, &cyls, &heads,
-                               &secs_per_cyl))
-        {
-            ret = -EFBIG;
-            goto fail;
-        }
+    if (calculate_geometry(total_sectors, &cyls, &heads, &secs_per_cyl)) {
+        ret = -EFBIG;
+        goto fail;
     }
 
-    total_sectors = (int64_t) cyls * heads * secs_per_cyl;
-
     /* Prepare the Hard Disk Footer */
     memset(buf, 0, 1024);
 
@@ -729,7 +730,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
     footer->features = be32_to_cpu(0x02);
     footer->version = be32_to_cpu(0x00010000);
     if (disk_type == VHD_DYNAMIC) {
-        footer->data_offset = be64_to_cpu(HEADER_SIZE);
+        footer->data_offset = be64_to_cpu(FOOTER_SIZE);
     } else {
         footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL);
     }
@@ -740,11 +741,10 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
     footer->minor = be16_to_cpu(0x0003);
     if (disk_type == VHD_DYNAMIC) {
         footer->orig_size = be64_to_cpu(total_sectors * 512);
-        footer->size = be64_to_cpu(total_sectors * 512);
     } else {
         footer->orig_size = be64_to_cpu(total_size);
-        footer->size = be64_to_cpu(total_size);
     }
+    footer->size = footer->orig_size;
     footer->cyls = be16_to_cpu(cyls);
     footer->heads = heads;
     footer->secs_per_cyl = secs_per_cyl;
@@ -755,7 +755,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
     uuid_generate(footer->uuid);
 #endif
 
-    footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
+    footer->checksum = be32_to_cpu(vpc_checksum(buf, FOOTER_SIZE));
 
     if (disk_type == VHD_DYNAMIC) {
         ret = create_dynamic_disk(fd, buf, total_sectors);
-- 
1.7.6.msysgit.0

