From owner-svn-src-head@FreeBSD.ORG Thu Jul 17 16:33:39 2014 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 74E5E6B1; Thu, 17 Jul 2014 16:33:39 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 5707828CC; Thu, 17 Jul 2014 16:33:39 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s6HGXdPV068621; Thu, 17 Jul 2014 16:33:39 GMT (envelope-from marcel@svn.freebsd.org) Received: (from marcel@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s6HGXdlY068620; Thu, 17 Jul 2014 16:33:39 GMT (envelope-from marcel@svn.freebsd.org) Message-Id: <201407171633.s6HGXdlY068620@svn.freebsd.org> From: Marcel Moolenaar Date: Thu, 17 Jul 2014 16:33:39 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r268802 - head/usr.bin/mkimg X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 17 Jul 2014 16:33:39 -0000 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);