Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F174128929
D60151.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D60151.diff
View Options
diff --git a/usr.bin/mkimg/Makefile b/usr.bin/mkimg/Makefile
--- a/usr.bin/mkimg/Makefile
+++ b/usr.bin/mkimg/Makefile
@@ -29,7 +29,7 @@
BINDIR?=/usr/bin
-LIBADD= util
+LIBADD= util z
HAS_TESTS=
SUBDIR.${MK_TESTS}+= tests
diff --git a/usr.bin/mkimg/vmdk.c b/usr.bin/mkimg/vmdk.c
--- a/usr.bin/mkimg/vmdk.c
+++ b/usr.bin/mkimg/vmdk.c
@@ -32,6 +32,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <zlib.h>
#include "endian.h"
#include "image.h"
@@ -41,12 +42,14 @@
#define VMDK_IMAGE_ROUND 1048576
#define VMDK_MIN_GRAIN_SIZE 8192
#define VMDK_SECTOR_SIZE 512
+#define VMDK_STREAM_GRAIN_SIZE 65536
struct vmdk_header {
uint32_t magic;
#define VMDK_MAGIC 0x564d444b
uint32_t version;
#define VMDK_VERSION 1
+#define VMDK_VERSION_STREAM 3
uint32_t flags;
#define VMDK_FLAGS_NL_TEST (1 << 0)
#define VMDK_FLAGS_RGT_USED (1 << 1)
@@ -60,6 +63,7 @@
#define VMDK_NGTES 512
uint64_t rgd_offset;
uint64_t gd_offset;
+#define VMDK_GD_AT_END 0xffffffffffffffffULL
uint64_t overhead;
uint8_t unclean;
uint32_t nl_test;
@@ -70,6 +74,35 @@
char padding[433];
} __attribute__((__packed__));
+/*
+ * In a stream-optimized VMDK, each grain is preceded by a 12-byte marker
+ * giving the grain's LBA and compressed size, and metadata (grain tables,
+ * the grain directory, and the footer) is preceded by a sector-sized
+ * marker giving the size of the metadata in sectors and its type. A
+ * sector of zeroes marks the end of the stream.
+ */
+#define VMDK_GRAIN_MARKER_SIZE 12
+
+struct vmdk_marker {
+ uint64_t val;
+ uint32_t size;
+} __attribute__((__packed__));
+_Static_assert(sizeof(struct vmdk_marker) == VMDK_GRAIN_MARKER_SIZE,
+ "Wrong size for VMDK marker");
+
+struct vmdk_metadata_marker {
+ uint64_t val;
+ uint32_t size;
+ uint32_t type;
+#define VMDK_MARKER_EOS 0
+#define VMDK_MARKER_GT 1
+#define VMDK_MARKER_GD 2
+#define VMDK_MARKER_FOOTER 3
+ char padding[496];
+} __attribute__((__packed__));
+_Static_assert(sizeof(struct vmdk_metadata_marker) == VMDK_SECTOR_SIZE,
+ "Wrong size for VMDK marker");
+
static const char desc_fmt[] =
"# Disk DescriptorFile\n"
"version=%d\n"
@@ -265,3 +298,279 @@
};
FORMAT_DEFINE(vmdk_format);
+
+/*
+ * Stream-optimized VMDK: grains are compressed and written sequentially,
+ * each grain table is written after the grains it describes, and the grain
+ * directory and a footer containing a copy of the header (with the grain
+ * directory offset filled in) come at the end.
+ */
+
+static int
+vmdk_stream_resize(lba_t imgsz)
+{
+ uint64_t imagesz;
+
+ imagesz = imgsz * secsz;
+ imagesz = (imagesz + VMDK_IMAGE_ROUND - 1) & ~(VMDK_IMAGE_ROUND - 1);
+ grainsz = (secsz < VMDK_STREAM_GRAIN_SIZE) ?
+ VMDK_STREAM_GRAIN_SIZE : secsz;
+
+ if (verbose)
+ fprintf(stderr, "VMDK: image size = %ju, grain size = %ju\n",
+ (uintmax_t)imagesz, (uintmax_t)grainsz);
+
+ grainsz /= VMDK_SECTOR_SIZE;
+ return (image_set_size(imagesz / secsz));
+}
+
+static int
+vmdk_stream_marker(int fd, uint64_t val, uint32_t type)
+{
+ struct vmdk_metadata_marker m;
+
+ memset(&m, 0, sizeof(m));
+ le64enc(&m.val, val);
+ le32enc(&m.type, type);
+ if (sparse_write(fd, &m, sizeof(m)) < 0)
+ return (errno);
+ return (0);
+}
+
+/*
+ * Write out the grain table gt, preceded by a marker, starting at sector
+ * *secp; record its location in the grain directory entry *gde, advance
+ * *secp past it, and clear gt for reuse.
+ */
+static int
+vmdk_stream_gt(int fd, uint32_t *gt, uint32_t *gde, uint64_t *secp)
+{
+ size_t gtsz;
+ int error;
+
+ gtsz = VMDK_NGTES * sizeof(uint32_t);
+ error = vmdk_stream_marker(fd, gtsz / VMDK_SECTOR_SIZE,
+ VMDK_MARKER_GT);
+ if (error)
+ return (error);
+ *secp += 1;
+
+ /* Grain directory entries are 32-bit sector numbers. */
+ if (*secp > UINT32_MAX)
+ return (EFBIG);
+ le32enc(gde, *secp);
+ if (sparse_write(fd, gt, gtsz) < 0)
+ return (errno);
+ *secp += gtsz / VMDK_SECTOR_SIZE;
+ memset(gt, 0, gtsz);
+ return (0);
+}
+
+static int
+vmdk_stream_iszero(const uint8_t *buf, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] != 0)
+ return (0);
+ }
+ return (1);
+}
+
+static int
+vmdk_stream_write(int fd)
+{
+ struct vmdk_header hdr;
+ struct vmdk_marker ghead;
+ uint32_t *gd, *gt;
+ uint8_t *gbuf, *zbuf;
+ char *buf, *desc;
+ uint64_t gdofs, grain, imagesz, ngrains, ngts, overhead, sec;
+ lba_t blkcnt;
+ size_t gdsz, gtsz, grainbytes, pos, len, zbufsz;
+ uLongf zlen;
+ int desc_len, error, gtused, n, zerror;
+
+ imagesz = (image_get_size() * secsz) / VMDK_SECTOR_SIZE;
+ grainbytes = grainsz * VMDK_SECTOR_SIZE;
+ blkcnt = grainbytes / secsz;
+ ngrains = imagesz / grainsz;
+ ngts = (ngrains + VMDK_NGTES - 1) / VMDK_NGTES;
+ gdsz = (ngts * sizeof(uint32_t) + VMDK_SECTOR_SIZE - 1) &
+ ~(VMDK_SECTOR_SIZE - 1);
+ gtsz = VMDK_NGTES * sizeof(uint32_t);
+
+ n = asprintf(&desc, desc_fmt, 1 /*version*/, 0 /*CID*/,
+ "streamOptimized" /*type*/, (uintmax_t)imagesz /*size*/,
+ "" /*name*/, ncyls /*cylinders*/, nheads /*heads*/,
+ nsecs /*sectors*/, "ddb.virtualHWVersion = \"4\"\n" /*extra*/);
+ if (n == -1)
+ return (ENOMEM);
+
+ desc_len = (n + VMDK_SECTOR_SIZE - 1) & ~(VMDK_SECTOR_SIZE - 1);
+ buf = realloc(desc, desc_len);
+ if (buf == NULL) {
+ free(desc);
+ return (ENOMEM);
+ }
+ desc = buf;
+ memset(desc + n, 0, desc_len - n);
+
+ /* Grains start at the first grain boundary after the descriptor. */
+ overhead = 1 + desc_len / VMDK_SECTOR_SIZE;
+ overhead = (overhead + grainsz - 1) & ~(grainsz - 1);
+
+ memset(&hdr, 0, sizeof(hdr));
+ le32enc(&hdr.magic, VMDK_MAGIC);
+ le32enc(&hdr.version, VMDK_VERSION_STREAM);
+ le32enc(&hdr.flags, VMDK_FLAGS_NL_TEST | VMDK_FLAGS_COMPRESSED |
+ VMDK_FLAGS_MARKERS);
+ le64enc(&hdr.capacity, imagesz);
+ le64enc(&hdr.grain_size, grainsz);
+ le64enc(&hdr.desc_offset, 1);
+ le64enc(&hdr.desc_size, desc_len / VMDK_SECTOR_SIZE);
+ le32enc(&hdr.ngtes, VMDK_NGTES);
+ le64enc(&hdr.gd_offset, VMDK_GD_AT_END);
+ le64enc(&hdr.overhead, overhead);
+ be32enc(&hdr.nl_test, VMDK_NL_TEST);
+ le16enc(&hdr.compress, VMDK_COMPRESS_DEFLATE);
+
+ if (verbose)
+ fprintf(stderr, "VMDK: overhead = %ju\n",
+ (uintmax_t)(overhead * VMDK_SECTOR_SIZE));
+
+ gd = calloc(1, gdsz);
+ gt = calloc(1, gtsz);
+ gbuf = malloc(grainbytes);
+ zbufsz = (VMDK_GRAIN_MARKER_SIZE + compressBound(grainbytes) +
+ VMDK_SECTOR_SIZE - 1) & ~(VMDK_SECTOR_SIZE - 1);
+ zbuf = malloc(zbufsz);
+ if (gd == NULL || gt == NULL || gbuf == NULL || zbuf == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Write the header and descriptor, and pad to the first grain. */
+ error = 0;
+ if (sparse_write(fd, &hdr, VMDK_SECTOR_SIZE) < 0 ||
+ sparse_write(fd, desc, desc_len) < 0) {
+ error = errno;
+ goto out;
+ }
+ error = image_copyout_zeroes(fd,
+ (overhead - 1) * VMDK_SECTOR_SIZE - desc_len);
+ if (error)
+ goto out;
+ sec = overhead;
+
+ gtused = 0;
+ for (grain = 0; grain < ngrains; grain++) {
+ /*
+ * If we're starting a new grain table, write out the
+ * previous one (but only if it has any entries).
+ */
+ if (grain % VMDK_NGTES == 0 && gtused) {
+ error = vmdk_stream_gt(fd, gt,
+ gd + grain / VMDK_NGTES - 1, &sec);
+ if (error)
+ goto out;
+ gtused = 0;
+ }
+
+ /* Skip grains which are entirely zero. */
+ if (!image_data(grain * blkcnt, blkcnt))
+ continue;
+ error = image_buffer_region((char *)gbuf, grain * blkcnt,
+ blkcnt);
+ if (error)
+ goto out;
+ if (vmdk_stream_iszero(gbuf, grainbytes))
+ continue;
+
+ /* Compress the grain, after space for the grain marker. */
+ zlen = zbufsz - VMDK_GRAIN_MARKER_SIZE;
+ zerror = compress2(zbuf + VMDK_GRAIN_MARKER_SIZE, &zlen,
+ gbuf, grainbytes, Z_DEFAULT_COMPRESSION);
+ if (zerror != Z_OK) {
+ error = (zerror == Z_MEM_ERROR) ? ENOMEM : EIO;
+ goto out;
+ }
+
+ /* Fill in the marker, and pad to a sector boundary. */
+ le64enc(&ghead.val, grain * grainsz);
+ le32enc(&ghead.size, zlen);
+ memcpy(&zbuf[0], &ghead, VMDK_GRAIN_MARKER_SIZE);
+ pos = VMDK_GRAIN_MARKER_SIZE + zlen;
+ len = (pos + VMDK_SECTOR_SIZE - 1) & ~(VMDK_SECTOR_SIZE - 1);
+ memset(&zbuf[pos], 0, len - pos);
+
+ /* Grain table entries are 32-bit sector numbers. */
+ if (sec > UINT32_MAX) {
+ error = EFBIG;
+ goto out;
+ }
+
+ /* Record the grain's location and write it out. */
+ le32enc(gt + grain % VMDK_NGTES, sec);
+ if (sparse_write(fd, zbuf, len) < 0) {
+ error = errno;
+ goto out;
+ }
+ sec += len / VMDK_SECTOR_SIZE;
+ gtused = 1;
+ }
+
+ /* Write out the final grain table, if it has any entries. */
+ if (gtused) {
+ error = vmdk_stream_gt(fd, gt, gd + (ngrains - 1) / VMDK_NGTES,
+ &sec);
+ if (error)
+ goto out;
+ }
+
+ /* Write the grain directory. */
+ error = vmdk_stream_marker(fd, gdsz / VMDK_SECTOR_SIZE,
+ VMDK_MARKER_GD);
+ if (error)
+ goto out;
+ gdofs = sec + 1;
+ if (sparse_write(fd, gd, gdsz) < 0) {
+ error = errno;
+ goto out;
+ }
+
+ /* Write the footer: a copy of the header with the GD offset. */
+ error = vmdk_stream_marker(fd, 1, VMDK_MARKER_FOOTER);
+ if (error)
+ goto out;
+ le64enc(&hdr.gd_offset, gdofs);
+ if (sparse_write(fd, &hdr, VMDK_SECTOR_SIZE) < 0) {
+ error = errno;
+ goto out;
+ }
+
+ /* Write the end-of-stream marker. */
+ error = vmdk_stream_marker(fd, 0, VMDK_MARKER_EOS);
+ if (error)
+ goto out;
+
+ error = image_copyout_done(fd);
+
+out:
+ free(zbuf);
+ free(gbuf);
+ free(gt);
+ free(gd);
+ free(desc);
+ return (error);
+}
+
+static struct mkimg_format vmdk_stream_format = {
+ .name = "vmdks",
+ .description = "Virtual Machine Disk, stream-optimized (compressed)",
+ .resize = vmdk_stream_resize,
+ .write = vmdk_stream_write,
+};
+
+FORMAT_DEFINE(vmdk_stream_format);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Oct 1, 7:39 PM (11 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39974271
Default Alt Text
D60151.diff (9 KB)
Attached To
Mode
D60151: mkimg: Add support for stream-optimized VMDK
Attached
Detach File
Event Timeline
Log In to Comment