Author: marcel
Date: Thu Jul 17 16:33:38 2014
New Revision: 268802
URL: http://svnweb.freebsd.org/changeset/base/268802

Log:
  Add support for the fixed image type. The fixed image is effectively
  a raw image with a VHD footer appended. There's little value that I
  can see to use the fixed image type, but in order to make VHD images
  for use by Microsoft's Azure platform, they must be fixed VHD images.
  
  Support has been added by refactoring the code to re-use common code
  and by adding a second output format structure.  To created fixed VHD
  images, specify "vhdf" as the output format.

Modified:
  head/usr.bin/mkimg/vhd.c

Modified: head/usr.bin/mkimg/vhd.c
==============================================================================
--- head/usr.bin/mkimg/vhd.c    Thu Jul 17 15:59:13 2014        (r268801)
+++ head/usr.bin/mkimg/vhd.c    Thu Jul 17 16:33:38 2014        (r268802)
@@ -41,15 +41,18 @@ __FBSDID("$FreeBSD$");
 #include "mkimg.h"
 
 /*
- * Notes:
+ * General notes:
  * o   File is in network byte order.
- * o   File layout:
- *     copy of disk footer
- *     dynamic disk header
- *     block allocation table (BAT)
- *     data blocks
- *     disk footer
  * o   The timestamp is seconds since 1/1/2000 12:00:00 AM UTC
+ *
+ * This file is divided in 3 parts:
+ * 1.  Common definitions
+ * 2.  Dynamic VHD support
+ * 3.  Fixed VHD support
+ */
+
+/*
+ * PART 1: Common definitions
  */
 
 #define        VHD_SECTOR_SIZE 512
@@ -88,41 +91,6 @@ struct vhd_footer {
 _Static_assert(sizeof(struct vhd_footer) == VHD_SECTOR_SIZE,
     "Wrong size for footer");
 
-struct vhd_dyn_header {
-       uint64_t        cookie;
-#define        VHD_HEADER_COOKIE       0x6378737061727365
-       uint64_t        data_offset;
-       uint64_t        table_offset;
-       uint32_t        version;
-       uint32_t        max_entries;
-       uint32_t        block_size;
-       uint32_t        checksum;
-       uuid_t          parent_id;
-       uint32_t        parent_timestamp;
-       char            _reserved1[4];
-       uint16_t        parent_name[256];       /* UTF-16 */
-       struct {
-               uint32_t        code;
-               uint32_t        data_space;
-               uint32_t        data_length;
-               uint32_t        _reserved;
-               uint64_t        data_offset;
-       } parent_locator[8];
-       char            _reserved2[256];
-};
-_Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2,
-    "Wrong size for header");
-
-static int
-vhd_resize(lba_t imgsz)
-{
-       uint64_t imagesz;
-
-       imagesz = imgsz * secsz;
-       imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1);
-       return (image_set_size(imagesz / secsz));
-}
-
 static uint32_t
 vhd_checksum(void *buf, size_t sz)
 {
@@ -136,36 +104,8 @@ vhd_checksum(void *buf, size_t sz)
        return (~sum);
 }
 
-static uint32_t
-vhd_timestamp(void)
-{
-       time_t t;
-
-       if (!unit_testing) {
-               t = time(NULL);
-               return (t - 0x386d4380);
-       }
-
-       return (0x01234567);
-}
-
 static void
-vhd_uuid_enc(void *buf, const uuid_t *uuid)
-{
-       uint8_t *p = buf;
-       int i;
-
-       be32enc(p, uuid->time_low);
-       be16enc(p + 4, uuid->time_mid);
-       be16enc(p + 6, uuid->time_hi_and_version);
-       p[8] = uuid->clock_seq_hi_and_reserved;
-       p[9] = uuid->clock_seq_low;
-       for (i = 0; i < _UUID_NODE_LEN; i++)
-               p[10 + i] = uuid->node[i];
-}
-
-static void
-vhd_geometry(struct vhd_footer *footer)
+vhd_geometry(struct vhd_footer *footer, uint64_t image_size)
 {
        lba_t imgsz;
        long cth;
@@ -180,7 +120,7 @@ vhd_geometry(struct vhd_footer *footer)
                return;
        }
 
-       imgsz = (image_get_size() * secsz) / VHD_SECTOR_SIZE;
+       imgsz = image_size / VHD_SECTOR_SIZE;
        if (imgsz > 65536 * 16 * 255)
                imgsz = 65536 * 16 * 255;
        if (imgsz >= 65535 * 16 * 63) {
@@ -207,12 +147,117 @@ vhd_geometry(struct vhd_footer *footer)
        be16enc(&footer->cylinders, cth / footer->heads);
 }
 
+static uint32_t
+vhd_timestamp(void)
+{
+       time_t t;
+
+       if (!unit_testing) {
+               t = time(NULL);
+               return (t - 0x386d4380);
+       }
+
+       return (0x01234567);
+}
+
+static void
+vhd_uuid_enc(void *buf, const uuid_t *uuid)
+{
+       uint8_t *p = buf;
+       int i;
+
+       be32enc(p, uuid->time_low);
+       be16enc(p + 4, uuid->time_mid);
+       be16enc(p + 6, uuid->time_hi_and_version);
+       p[8] = uuid->clock_seq_hi_and_reserved;
+       p[9] = uuid->clock_seq_low;
+       for (i = 0; i < _UUID_NODE_LEN; i++)
+               p[10 + i] = uuid->node[i];
+}
+
+static void
+vhd_make_footer(struct vhd_footer *footer, uint64_t image_size,
+    uint32_t disk_type, uint64_t data_offset)
+{
+       uuid_t id;
+
+       memset(footer, 0, sizeof(*footer));
+       be64enc(&footer->cookie, VHD_FOOTER_COOKIE);
+       be32enc(&footer->features, VHD_FEATURES_RESERVED);
+       be32enc(&footer->version, VHD_VERSION);
+       be64enc(&footer->data_offset, data_offset);
+       be32enc(&footer->timestamp, vhd_timestamp());
+       be32enc(&footer->creator_tool, VHD_CREATOR_TOOL);
+       be32enc(&footer->creator_version, VHD_CREATOR_VERSION);
+       be32enc(&footer->creator_os, VHD_CREATOR_OS);
+       be64enc(&footer->original_size, image_size);
+       be64enc(&footer->current_size, image_size);
+       vhd_geometry(footer, image_size);
+       be32enc(&footer->disk_type, disk_type);
+       mkimg_uuid(&id);
+       vhd_uuid_enc(&footer->id, &id);
+       be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer)));
+}
+
+/*
+ * We round the image size to 2MB for both the dynamic and
+ * fixed VHD formats. For dynamic VHD, this is needed to
+ * have the image size be a multiple of the grain size. For
+ * fixed VHD this is not really needed, but makes sure that
+ * it's easy to convert from fixed VHD to dynamic VHD.
+ */
+static int
+vhd_resize(lba_t imgsz)
+{
+       uint64_t imagesz;
+
+       imagesz = imgsz * secsz;
+       imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1);
+       return (image_set_size(imagesz / secsz));
+}
+
+/*
+ * PART 2: Dynamic VHD support
+ *
+ * Notes:
+ * o   File layout:
+ *     copy of disk footer
+ *     dynamic disk header
+ *     block allocation table (BAT)
+ *     data blocks
+ *     disk footer
+ */
+
+struct vhd_dyn_header {
+       uint64_t        cookie;
+#define        VHD_HEADER_COOKIE       0x6378737061727365
+       uint64_t        data_offset;
+       uint64_t        table_offset;
+       uint32_t        version;
+       uint32_t        max_entries;
+       uint32_t        block_size;
+       uint32_t        checksum;
+       uuid_t          parent_id;
+       uint32_t        parent_timestamp;
+       char            _reserved1[4];
+       uint16_t        parent_name[256];       /* UTF-16 */
+       struct {
+               uint32_t        code;
+               uint32_t        data_space;
+               uint32_t        data_length;
+               uint32_t        _reserved;
+               uint64_t        data_offset;
+       } parent_locator[8];
+       char            _reserved2[256];
+};
+_Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2,
+    "Wrong size for header");
+
 static int
-vhd_write(int fd)
+vhd_dyn_write(int fd)
 {
        struct vhd_footer footer;
        struct vhd_dyn_header header;
-       uuid_t id;
        uint64_t imgsz;
        lba_t blk, blkcnt, nblks;
        uint32_t *bat;
@@ -224,22 +269,7 @@ vhd_write(int fd)
        imgsz = image_get_size() * secsz;
        bat_entries = imgsz / VHD_BLOCK_SIZE;
 
-       memset(&footer, 0, sizeof(footer));
-       be64enc(&footer.cookie, VHD_FOOTER_COOKIE);
-       be32enc(&footer.features, VHD_FEATURES_RESERVED);
-       be32enc(&footer.version, VHD_VERSION);
-       be64enc(&footer.data_offset, sizeof(footer));
-       be32enc(&footer.timestamp, vhd_timestamp());
-       be32enc(&footer.creator_tool, VHD_CREATOR_TOOL);
-       be32enc(&footer.creator_version, VHD_CREATOR_VERSION);
-       be32enc(&footer.creator_os, VHD_CREATOR_OS);
-       be64enc(&footer.original_size, imgsz);
-       be64enc(&footer.current_size, imgsz);
-       vhd_geometry(&footer);
-       be32enc(&footer.disk_type, VHD_DISK_TYPE_DYNAMIC);
-       mkimg_uuid(&id);
-       vhd_uuid_enc(&footer.id, &id);
-       be32enc(&footer.checksum, vhd_checksum(&footer, sizeof(footer)));
+       vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer));
        if (sparse_write(fd, &footer, sizeof(footer)) < 0)
                return (errno);
 
@@ -308,11 +338,41 @@ vhd_write(int fd)
        return (0);
 }
 
-static struct mkimg_format vhd_format = {
+static struct mkimg_format vhd_dyn_format = {
        .name = "vhd",
        .description = "Virtual Hard Disk",
        .resize = vhd_resize,
-       .write = vhd_write,
+       .write = vhd_dyn_write,
+};
+
+FORMAT_DEFINE(vhd_dyn_format);
+
+/*
+ * PART 2: Fixed VHD
+ */
+
+static int
+vhd_fix_write(int fd)
+{
+       struct vhd_footer footer;
+       uint64_t imgsz;
+       int error;
+
+       error = image_copyout(fd);
+       if (!error) {
+               imgsz = image_get_size() * secsz;
+               vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_FIXED, ~0ULL);
+               if (sparse_write(fd, &footer, sizeof(footer)) < 0)
+                       error = errno;
+       }
+       return (error);
+}
+
+static struct mkimg_format vhd_fix_format = {
+        .name = "vhdf",
+        .description = "Fixed Virtual Hard Disk",
+        .resize = vhd_resize,
+        .write = vhd_fix_write,
 };
 
-FORMAT_DEFINE(vhd_format);
+FORMAT_DEFINE(vhd_fix_format);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to