Date: Thu, 3 Jul 2014 20:31:43 +0000 (UTC) From: Marcel Moolenaar <marcel@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r268236 - head/usr.bin/mkimg Message-ID: <201407032031.s63KVh4E067310@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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>
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201407032031.s63KVh4E067310>