Author: marcel
Date: Thu Jul  3 20:31:43 2014
New Revision: 268236
URL: http://svnweb.freebsd.org/changeset/base/268236

Log:
  Add VHD support to mkimg(1). VHD is used by Xen and Microsoft's Hyper-V
  among others.
  
  Add an undocumented option for unit testing (-y). When given, the image
  will have UUIDs and timestamps synthesized in a way that gives identical
  results across runs. As such, UUIDs stop being unique, globally or
  otherwise.
  
  VHD support requested by: gjb@

Added:
  head/usr.bin/mkimg/vhd.c
     - copied unchanged from r268229, user/marcel/mkimg/vhd.c
Modified:
  head/usr.bin/mkimg/Makefile
  head/usr.bin/mkimg/gpt.c
  head/usr.bin/mkimg/image.c
  head/usr.bin/mkimg/image.h
  head/usr.bin/mkimg/mkimg.c
  head/usr.bin/mkimg/mkimg.h
  head/usr.bin/mkimg/raw.c
  head/usr.bin/mkimg/vmdk.c
Directory Properties:
  head/usr.bin/mkimg/   (props changed)

Modified: head/usr.bin/mkimg/Makefile
==============================================================================
--- head/usr.bin/mkimg/Makefile Thu Jul  3 20:16:48 2014        (r268235)
+++ head/usr.bin/mkimg/Makefile Thu Jul  3 20:31:43 2014        (r268236)
@@ -9,6 +9,7 @@ CFLAGS+=-DSPARSE_WRITE
 # List of formats to support
 SRCS+= \
        raw.c \
+       vhd.c \
        vmdk.c
 
 # List of schemes to support

Modified: head/usr.bin/mkimg/gpt.c
==============================================================================
--- head/usr.bin/mkimg/gpt.c    Thu Jul  3 20:16:48 2014        (r268235)
+++ head/usr.bin/mkimg/gpt.c    Thu Jul  3 20:31:43 2014        (r268236)
@@ -211,7 +211,7 @@ gpt_mktbl(u_int tblsz)
        STAILQ_FOREACH(part, &partlist, link) {
                ent = tbl + part->index;
                gpt_uuid_enc(&ent->ent_type, ALIAS_TYPE2PTR(part->type));
-               uuidgen(&uuid, 1);
+               mkimg_uuid(&uuid);
                gpt_uuid_enc(&ent->ent_uuid, &uuid);
                le64enc(&ent->ent_lba_start, part->block);
                le64enc(&ent->ent_lba_end, part->block + part->size - 1);
@@ -279,7 +279,7 @@ gpt_write(lba_t imgsz, void *bootcode)
        le32enc(&hdr->hdr_size, offsetof(struct gpt_hdr, padding));
        le64enc(&hdr->hdr_lba_start, 2 + tblsz);
        le64enc(&hdr->hdr_lba_end, imgsz - tblsz - 2);
-       uuidgen(&uuid, 1);
+       mkimg_uuid(&uuid);
        gpt_uuid_enc(&hdr->hdr_uuid, &uuid);
        le32enc(&hdr->hdr_entries, nparts);
        le32enc(&hdr->hdr_entsz, sizeof(struct gpt_ent));

Modified: head/usr.bin/mkimg/image.c
==============================================================================
--- head/usr.bin/mkimg/image.c  Thu Jul  3 20:16:48 2014        (r268235)
+++ head/usr.bin/mkimg/image.c  Thu Jul  3 20:31:43 2014        (r268236)
@@ -94,21 +94,42 @@ image_copyin(lba_t blk, int fd, uint64_t
 int
 image_copyout(int fd)
 {
+       off_t ofs;
+       int error;
+
+       error = image_copyout_region(fd, 0, image_size);
+       if (error)
+               return (error);
+
+       ofs = lseek(fd, 0L, SEEK_CUR);
+       if (ofs == -1)
+               return (0);
+       error = (ftruncate(fd, ofs) == -1) ? errno : 0;
+       return (error);
+}
+
+int
+image_copyout_region(int fd, lba_t blk, lba_t size)
+{
        char *buffer;
        off_t ofs;
+       size_t sz;
        ssize_t rdsz, wrsz;
        int error;
 
        ofs = lseek(fd, 0L, SEEK_CUR);
 
-       if (lseek(image_fd, 0, SEEK_SET) != 0)
+       blk *= secsz;
+       if (lseek(image_fd, blk, SEEK_SET) != blk)
                return (errno);
        buffer = malloc(BUFFER_SIZE);
        if (buffer == NULL)
                return (errno);
        error = 0;
-       while (1) {
-               rdsz = read(image_fd, buffer, BUFFER_SIZE);
+       size *= secsz;
+       while (size > 0) {
+               sz = (BUFFER_SIZE < size) ? BUFFER_SIZE : size;
+               rdsz = read(image_fd, buffer, sz);
                if (rdsz <= 0) {
                        error = (rdsz < 0) ? errno : 0;
                        break;
@@ -120,14 +141,10 @@ image_copyout(int fd)
                        error = errno;
                        break;
                }
+               assert(wrsz == rdsz);
+               size -= rdsz;
        }
        free(buffer);
-       if (error)
-               return (error);
-       ofs = lseek(fd, 0L, SEEK_CUR);
-       if (ofs == -1)
-               return (errno);
-       error = (ftruncate(fd, ofs) == -1) ? errno : 0;
        return (error);
 }
 

Modified: head/usr.bin/mkimg/image.h
==============================================================================
--- head/usr.bin/mkimg/image.h  Thu Jul  3 20:16:48 2014        (r268235)
+++ head/usr.bin/mkimg/image.h  Thu Jul  3 20:31:43 2014        (r268236)
@@ -33,6 +33,7 @@ typedef int64_t lba_t;
 
 int image_copyin(lba_t blk, int fd, uint64_t *sizep);
 int image_copyout(int fd);
+int image_copyout_region(int fd, lba_t blk, lba_t size);
 lba_t image_get_size(void);
 int image_init(void);
 int image_set_size(lba_t blk);

Modified: head/usr.bin/mkimg/mkimg.c
==============================================================================
--- head/usr.bin/mkimg/mkimg.c  Thu Jul  3 20:16:48 2014        (r268235)
+++ head/usr.bin/mkimg/mkimg.c  Thu Jul  3 20:31:43 2014        (r268236)
@@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/queue.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/uuid.h>
 #include <errno.h>
 #include <err.h>
 #include <fcntl.h>
@@ -50,6 +51,7 @@ __FBSDID("$FreeBSD$");
 struct partlisthead partlist = STAILQ_HEAD_INITIALIZER(partlist);
 u_int nparts = 0;
 
+u_int unit_testing;
 u_int verbose;
 
 u_int ncyls = 0;
@@ -258,6 +260,22 @@ sparse_write(int fd, const void *ptr, si
 }
 #endif /* SPARSE_WRITE */
 
+void
+mkimg_uuid(struct uuid *uuid)
+{
+       static uint8_t gen[sizeof(struct uuid)];
+       u_int i;
+
+       if (!unit_testing) {
+               uuidgen(uuid, 1);
+               return;
+       }
+
+       for (i = 0; i < sizeof(gen); i++)
+               gen[i]++;
+       memcpy(uuid, gen, sizeof(uuid_t));
+}
+
 static void
 mkimg(void)
 {
@@ -337,7 +355,7 @@ main(int argc, char *argv[])
 
        bcfd = -1;
        outfd = 1;      /* Write to stdout by default */
-       while ((c = getopt(argc, argv, "b:f:o:p:s:vH:P:S:T:")) != -1) {
+       while ((c = getopt(argc, argv, "b:f:o:p:s:vyH:P:S:T:")) != -1) {
                switch (c) {
                case 'b':       /* BOOT CODE */
                        if (bcfd != -1)
@@ -373,6 +391,9 @@ main(int argc, char *argv[])
                        if (error)
                                errc(EX_DATAERR, error, "scheme");
                        break;
+               case 'y':
+                       unit_testing++;
+                       break;
                case 'v':
                        verbose++;
                        break;

Modified: head/usr.bin/mkimg/mkimg.h
==============================================================================
--- head/usr.bin/mkimg/mkimg.h  Thu Jul  3 20:16:48 2014        (r268235)
+++ head/usr.bin/mkimg/mkimg.h  Thu Jul  3 20:31:43 2014        (r268236)
@@ -50,6 +50,7 @@ struct part {
 extern STAILQ_HEAD(partlisthead, part) partlist;
 extern u_int nparts;
 
+extern u_int unit_testing;
 extern u_int verbose;
 
 extern u_int ncyls;
@@ -71,4 +72,7 @@ round_block(lba_t n)
 ssize_t sparse_write(int, const void *, size_t);
 #endif
 
+struct uuid;
+void mkimg_uuid(struct uuid *);
+
 #endif /* _MKIMG_MKIMG_H_ */

Modified: head/usr.bin/mkimg/raw.c
==============================================================================
--- head/usr.bin/mkimg/raw.c    Thu Jul  3 20:16:48 2014        (r268235)
+++ head/usr.bin/mkimg/raw.c    Thu Jul  3 20:31:43 2014        (r268236)
@@ -28,7 +28,6 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/types.h>
-#include <sys/apm.h>
 #include <sys/endian.h>
 #include <sys/errno.h>
 #include <stdlib.h>

Copied: head/usr.bin/mkimg/vhd.c (from r268229, user/marcel/mkimg/vhd.c)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/usr.bin/mkimg/vhd.c    Thu Jul  3 20:31:43 2014        (r268236, copy 
of r268229, user/marcel/mkimg/vhd.c)
@@ -0,0 +1,308 @@
+/*-
+ * Copyright (c) 2014 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <uuid.h>
+
+#include "image.h"
+#include "format.h"
+#include "mkimg.h"
+
+/*
+ * 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
+ */
+
+#define        VHD_SECTOR_SIZE 512
+#define        VHD_BLOCK_SIZE  (4096 * VHD_SECTOR_SIZE)        /* 2MB blocks */
+
+struct vhd_footer {
+       uint64_t        cookie;
+#define        VHD_FOOTER_COOKIE       0x636f6e6563746978
+       uint32_t        features;
+#define        VHD_FEATURES_TEMPORARY  0x01
+#define        VHD_FEATURES_RESERVED   0x02
+       uint32_t        version;
+#define        VHD_VERSION             0x00010000
+       uint64_t        data_offset;
+       uint32_t        timestamp;
+       uint32_t        creator_tool;
+#define        VHD_CREATOR_TOOL        0x2a696d67      /* FreeBSD mkimg */
+       uint32_t        creator_version;
+#define        VHD_CREATOR_VERSION     0x00010000
+       uint32_t        creator_os;
+#define        VHD_CREATOR_OS          0x46425344
+       uint64_t        original_size;
+       uint64_t        current_size;
+       uint16_t        cylinders;
+       uint8_t         heads;
+       uint8_t         sectors;
+       uint32_t        disk_type;
+#define        VHD_DISK_TYPE_FIXED     2
+#define        VHD_DISK_TYPE_DYNAMIC   3
+#define        VHD_DISK_TYPE_DIFF      4
+       uint32_t        checksum;
+       uuid_t          id;
+       uint8_t         saved_state;
+       uint8_t         _reserved[427];
+};
+_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)
+{
+       uint8_t *p = buf;
+       uint32_t sum;
+       size_t ofs;
+
+       sum = 0;
+       for (ofs = 0; ofs < sz; ofs++)
+               sum += p[ofs];
+       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)
+{
+       lba_t imgsz;
+       long cth;
+
+       /* Respect command line options if possible. */
+       if (nheads > 1 && nheads < 256 &&
+           nsecs > 1 && nsecs < 256 &&
+           ncyls < 65536) {
+               be16enc(&footer->cylinders, ncyls);
+               footer->heads = nheads;
+               footer->sectors = nsecs;
+               return;
+       }
+
+       imgsz = (image_get_size() * secsz) / VHD_SECTOR_SIZE;
+       if (imgsz > 65536 * 16 * 255)
+               imgsz = 65536 * 16 * 255;
+       if (imgsz >= 65535 * 16 * 63) {
+               be16enc(&footer->cylinders, imgsz / (16 * 255));
+               footer->heads = 16;
+               footer->sectors = 255;
+               return;
+       }
+       footer->sectors = 17;
+       cth = imgsz / 17;
+       footer->heads = (cth + 1023) / 1024;
+       if (footer->heads < 4)
+               footer->heads = 4;
+       if (cth >= (footer->heads * 1024) || footer->heads > 16) {
+               footer->heads = 16;
+               footer->sectors = 31;
+               cth = imgsz / 31;
+       }
+       if (cth >= (footer->heads * 1024)) {
+               footer->heads = 16;
+               footer->sectors = 63;
+               cth = imgsz / 63;
+       }
+       be16enc(&footer->cylinders, cth / footer->heads);
+}
+
+static int
+vhd_write(int fd)
+{
+       struct vhd_footer footer;
+       struct vhd_dyn_header header;
+       uuid_t id;
+       uint64_t imgsz;
+       lba_t blk, nblks;
+       uint32_t *bat;
+       void *bitmap;
+       size_t batsz;
+       uint32_t sector;
+       int bat_entries, error, entry;
+
+       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)));
+       if (sparse_write(fd, &footer, sizeof(footer)) < 0)
+               return (errno);
+
+       memset(&header, 0, sizeof(header));
+       be64enc(&header.cookie, VHD_HEADER_COOKIE);
+       be64enc(&header.data_offset, ~0ULL);
+       be64enc(&header.table_offset, sizeof(footer) + sizeof(header));
+       be32enc(&header.version, VHD_VERSION);
+       be32enc(&header.max_entries, bat_entries);
+       be32enc(&header.block_size, VHD_BLOCK_SIZE);
+       be32enc(&header.checksum, vhd_checksum(&header, sizeof(header)));
+       if (sparse_write(fd, &header, sizeof(header)) < 0)
+               return (errno);
+
+       batsz = bat_entries * sizeof(uint32_t);
+       batsz = (batsz + VHD_SECTOR_SIZE - 1) & ~(VHD_SECTOR_SIZE - 1);
+       bat = malloc(batsz);
+       if (bat == NULL)
+               return (errno);
+       memset(bat, 0xff, batsz);
+       sector = (sizeof(footer) + sizeof(header) + batsz) / VHD_SECTOR_SIZE;
+       for (entry = 0; entry < bat_entries; entry++) {
+               be32enc(&bat[entry], sector);
+               sector += (VHD_BLOCK_SIZE / VHD_SECTOR_SIZE) + 1;
+       }
+       if (sparse_write(fd, bat, batsz) < 0) {
+               free(bat);
+               return (errno);
+       }
+       free(bat);
+
+       bitmap = malloc(VHD_SECTOR_SIZE);
+       if (bitmap == NULL)
+               return (errno);
+       memset(bitmap, 0xff, VHD_SECTOR_SIZE);
+
+       blk = 0;
+       nblks = image_get_size();
+       while (blk < nblks) {
+               if (sparse_write(fd, bitmap, VHD_SECTOR_SIZE) < 0) {
+                       error = errno;
+                       break;
+               }
+               error = image_copyout_region(fd, blk, VHD_BLOCK_SIZE / secsz);
+               if (error)
+                       break;
+               blk += VHD_BLOCK_SIZE / secsz;
+       }
+       free(bitmap);
+       if (blk != nblks)
+               return (error);
+
+       if (sparse_write(fd, &footer, sizeof(footer)) < 0)
+               return (errno);
+
+       return (0);
+}
+
+static struct mkimg_format vhd_format = {
+       .name = "vhd",
+       .description = "Virtual Hard Disk",
+       .resize = vhd_resize,
+       .write = vhd_write,
+};
+
+FORMAT_DEFINE(vhd_format);

Modified: head/usr.bin/mkimg/vmdk.c
==============================================================================
--- head/usr.bin/mkimg/vmdk.c   Thu Jul  3 20:16:48 2014        (r268235)
+++ head/usr.bin/mkimg/vmdk.c   Thu Jul  3 20:31:43 2014        (r268236)
@@ -28,7 +28,6 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/types.h>
-#include <sys/apm.h>
 #include <sys/endian.h>
 #include <sys/errno.h>
 #include <stdint.h>
_______________________________________________
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