aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/mkimg
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/mkimg')
-rw-r--r--usr.bin/mkimg/Makefile1
-rw-r--r--usr.bin/mkimg/gpt.c4
-rw-r--r--usr.bin/mkimg/image.c35
-rw-r--r--usr.bin/mkimg/image.h1
-rw-r--r--usr.bin/mkimg/mkimg.c23
-rw-r--r--usr.bin/mkimg/mkimg.h4
-rw-r--r--usr.bin/mkimg/raw.c1
-rw-r--r--usr.bin/mkimg/vhd.c308
-rw-r--r--usr.bin/mkimg/vmdk.c1
9 files changed, 364 insertions, 14 deletions
diff --git a/usr.bin/mkimg/Makefile b/usr.bin/mkimg/Makefile
index 582ca6032514..b802799f96ed 100644
--- a/usr.bin/mkimg/Makefile
+++ b/usr.bin/mkimg/Makefile
@@ -9,6 +9,7 @@ CFLAGS+=-DSPARSE_WRITE
# List of formats to support
SRCS+= \
raw.c \
+ vhd.c \
vmdk.c
# List of schemes to support
diff --git a/usr.bin/mkimg/gpt.c b/usr.bin/mkimg/gpt.c
index 47fa64a133f1..959deb37bc0d 100644
--- a/usr.bin/mkimg/gpt.c
+++ b/usr.bin/mkimg/gpt.c
@@ -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));
diff --git a/usr.bin/mkimg/image.c b/usr.bin/mkimg/image.c
index e8dc8fe2082f..264c5b2c9e87 100644
--- a/usr.bin/mkimg/image.c
+++ b/usr.bin/mkimg/image.c
@@ -94,21 +94,42 @@ image_copyin(lba_t blk, int fd, uint64_t *sizep)
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);
}
diff --git a/usr.bin/mkimg/image.h b/usr.bin/mkimg/image.h
index d30e11b84236..c4e842e54282 100644
--- a/usr.bin/mkimg/image.h
+++ b/usr.bin/mkimg/image.h
@@ -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);
diff --git a/usr.bin/mkimg/mkimg.c b/usr.bin/mkimg/mkimg.c
index 45d09d5cd72a..edfd1f5c29ef 100644
--- a/usr.bin/mkimg/mkimg.c
+++ b/usr.bin/mkimg/mkimg.c
@@ -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, size_t sz)
}
#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;
diff --git a/usr.bin/mkimg/mkimg.h b/usr.bin/mkimg/mkimg.h
index 290ca1efc201..9558b863d36d 100644
--- a/usr.bin/mkimg/mkimg.h
+++ b/usr.bin/mkimg/mkimg.h
@@ -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_ */
diff --git a/usr.bin/mkimg/raw.c b/usr.bin/mkimg/raw.c
index ea13931240c0..759debf322bf 100644
--- a/usr.bin/mkimg/raw.c
+++ b/usr.bin/mkimg/raw.c
@@ -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>
diff --git a/usr.bin/mkimg/vhd.c b/usr.bin/mkimg/vhd.c
new file mode 100644
index 000000000000..b0ce0cc9eff5
--- /dev/null
+++ b/usr.bin/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);
diff --git a/usr.bin/mkimg/vmdk.c b/usr.bin/mkimg/vmdk.c
index b2770032cf2d..af5f786d9809 100644
--- a/usr.bin/mkimg/vmdk.c
+++ b/usr.bin/mkimg/vmdk.c
@@ -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>