17a44648marcel/*-
27a44648marcel * Copyright (c) 2014 Juniper Networks, Inc.
37a44648marcel * All rights reserved.
47a44648marcel *
57a44648marcel * Redistribution and use in source and binary forms, with or without
67a44648marcel * modification, are permitted provided that the following conditions
77a44648marcel * are met:
87a44648marcel * 1. Redistributions of source code must retain the above copyright
97a44648marcel *    notice, this list of conditions and the following disclaimer.
107a44648marcel * 2. Redistributions in binary form must reproduce the above copyright
117a44648marcel *    notice, this list of conditions and the following disclaimer in the
127a44648marcel *    documentation and/or other materials provided with the distribution.
137a44648marcel *
147a44648marcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
157a44648marcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
167a44648marcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
177a44648marcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
187a44648marcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
197a44648marcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
207a44648marcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
217a44648marcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
227a44648marcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
237a44648marcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
247a44648marcel * SUCH DAMAGE.
257a44648marcel */
267a44648marcel
277a44648marcel#include <sys/cdefs.h>
287a44648marcel__FBSDID("$FreeBSD$");
297a44648marcel
307a44648marcel#include <sys/errno.h>
319f8da78marcel#include <stdint.h>
329f8da78marcel#include <stdio.h>
337a44648marcel#include <stdlib.h>
347a44648marcel#include <string.h>
357a44648marcel
36741bf12marcel#include "endian.h"
377a44648marcel#include "image.h"
38fb17722marcel#include "format.h"
397a44648marcel#include "mkimg.h"
407a44648marcel
412257ed4marcel#define	VMDK_IMAGE_ROUND	1048576
425a1f714marcel#define	VMDK_MIN_GRAIN_SIZE	8192
435a1f714marcel#define	VMDK_SECTOR_SIZE	512
445a1f714marcel
45f7a8c7bmarcelstruct vmdk_header {
46f7a8c7bmarcel	uint32_t	magic;
47f7a8c7bmarcel#define	VMDK_MAGIC		0x564d444b
48f7a8c7bmarcel	uint32_t	version;
49f7a8c7bmarcel#define	VMDK_VERSION		1
50f7a8c7bmarcel	uint32_t	flags;
51f7a8c7bmarcel#define	VMDK_FLAGS_NL_TEST	(1 << 0)
52f7a8c7bmarcel#define	VMDK_FLAGS_RGT_USED	(1 << 1)
53f7a8c7bmarcel#define	VMDK_FLAGS_COMPRESSED	(1 << 16)
54f7a8c7bmarcel#define	VMDK_FLAGS_MARKERS	(1 << 17)
55f7a8c7bmarcel	uint64_t	capacity;
56f7a8c7bmarcel	uint64_t	grain_size;
57f7a8c7bmarcel	uint64_t	desc_offset;
58f7a8c7bmarcel	uint64_t	desc_size;
59f7a8c7bmarcel	uint32_t	ngtes;
60f7a8c7bmarcel#define	VMDK_NGTES		512
61f7a8c7bmarcel	uint64_t	rgd_offset;
62f7a8c7bmarcel	uint64_t	gd_offset;
63f7a8c7bmarcel	uint64_t	overhead;
64f7a8c7bmarcel	uint8_t		unclean;
655a1f714marcel	uint32_t	nl_test;
66f7a8c7bmarcel#define	VMDK_NL_TEST		0x0a200d0a
67f7a8c7bmarcel	uint16_t	compress;
68f7a8c7bmarcel#define	VMDK_COMPRESS_NONE	0
69f7a8c7bmarcel#define	VMDK_COMPRESS_DEFLATE	1
70f7a8c7bmarcel	char		padding[433];
71f7a8c7bmarcel} __attribute__((__packed__));
72f7a8c7bmarcel
739f8da78marcelstatic const char desc_fmt[] =
749f8da78marcel    "# Disk DescriptorFile\n"
759f8da78marcel    "version=%d\n"
769f8da78marcel    "CID=%08x\n"
779f8da78marcel    "parentCID=ffffffff\n"
789f8da78marcel    "createType=\"monolithicSparse\"\n"
799f8da78marcel    "# Extent description\n"
809f8da78marcel    "RW %ju SPARSE \"%s\"\n"
819f8da78marcel    "# The Disk Data Base\n"
829f8da78marcel    "#DDB\n"
839f8da78marcel    "ddb.adapterType = \"ide\"\n"
849f8da78marcel    "ddb.geometry.cylinders = \"%u\"\n"
859f8da78marcel    "ddb.geometry.heads = \"%u\"\n"
869f8da78marcel    "ddb.geometry.sectors = \"%u\"\n";
879f8da78marcel
885a1f714marcelstatic uint64_t grainsz;
895a1f714marcel
907a44648marcelstatic int
915a1f714marcelvmdk_resize(lba_t imgsz)
92fb17722marcel{
935a1f714marcel	uint64_t imagesz;
945a1f714marcel
955a1f714marcel	imagesz = imgsz * secsz;
962257ed4marcel	imagesz = (imagesz + VMDK_IMAGE_ROUND - 1) & ~(VMDK_IMAGE_ROUND - 1);
975a1f714marcel	grainsz = (blksz < VMDK_MIN_GRAIN_SIZE) ? VMDK_MIN_GRAIN_SIZE : blksz;
985a1f714marcel
995a1f714marcel	if (verbose)
1005a1f714marcel		fprintf(stderr, "VMDK: image size = %ju, grain size = %ju\n",
1015a1f714marcel		    (uintmax_t)imagesz, (uintmax_t)grainsz);
102fb17722marcel
1035a1f714marcel	grainsz /= VMDK_SECTOR_SIZE;
1045a1f714marcel	return (image_set_size(imagesz / secsz));
105fb17722marcel}
106fb17722marcel
107fb17722marcelstatic int
1085a1f714marcelvmdk_write(int fd)
1097a44648marcel{
1105a1f714marcel	struct vmdk_header hdr;
111e23364dmarcel	uint32_t *gt, *gd, *rgd;
1122a0c5f7marcel	char *buf, *desc;
1132a0c5f7marcel	off_t cur, lim;
1145a1f714marcel	uint64_t imagesz;
115ca1d592marcel	lba_t blkofs, blkcnt;
1165a1f714marcel	size_t gdsz, gtsz;
117ca1d592marcel	uint32_t sec, cursec;
118048d89bmarcel	int error, desc_len, n, ngrains, ngts;
1199f8da78marcel
1205a1f714marcel	imagesz = (image_get_size() * secsz) / VMDK_SECTOR_SIZE;
1215a1f714marcel
1225a1f714marcel	memset(&hdr, 0, sizeof(hdr));
1235a1f714marcel	le32enc(&hdr.magic, VMDK_MAGIC);
1245a1f714marcel	le32enc(&hdr.version, VMDK_VERSION);
1255a1f714marcel	le32enc(&hdr.flags, VMDK_FLAGS_NL_TEST | VMDK_FLAGS_RGT_USED);
1265a1f714marcel	le64enc(&hdr.capacity, imagesz);
1275a1f714marcel	le64enc(&hdr.grain_size, grainsz);
1285a1f714marcel
1295a1f714marcel	n = asprintf(&desc, desc_fmt, 1 /*version*/, 0 /*CID*/,
1305a1f714marcel	    (uintmax_t)imagesz /*size*/, "" /*name*/,
1319f8da78marcel	    ncyls /*cylinders*/, nheads /*heads*/, nsecs /*sectors*/);
132048d89bmarcel	if (n == -1)
133048d89bmarcel		return (ENOMEM);
134048d89bmarcel
1355a1f714marcel	desc_len = (n + VMDK_SECTOR_SIZE - 1) & ~(VMDK_SECTOR_SIZE - 1);
1369f8da78marcel	desc = realloc(desc, desc_len);
1375a1f714marcel	memset(desc + n, 0, desc_len - n);
1385a1f714marcel
1395a1f714marcel	le64enc(&hdr.desc_offset, 1);
1405a1f714marcel	le64enc(&hdr.desc_size, desc_len / VMDK_SECTOR_SIZE);
1415a1f714marcel	le32enc(&hdr.ngtes, VMDK_NGTES);
1425a1f714marcel
1435a1f714marcel	sec = desc_len / VMDK_SECTOR_SIZE + 1;
1445a1f714marcel
1455a1f714marcel	ngrains = imagesz / grainsz;
1465a1f714marcel	ngts = (ngrains + VMDK_NGTES - 1) / VMDK_NGTES;
1475a1f714marcel	gdsz = (ngts * sizeof(uint32_t) + VMDK_SECTOR_SIZE - 1) &
1485a1f714marcel	    ~(VMDK_SECTOR_SIZE - 1);
149e23364dmarcel
15080204ccpfg	gd = calloc(1, gdsz);
151048d89bmarcel	if (gd == NULL) {
152048d89bmarcel		free(desc);
153048d89bmarcel		return (ENOMEM);
154048d89bmarcel	}
155e23364dmarcel	le64enc(&hdr.gd_offset, sec);
1565a1f714marcel	sec += gdsz / VMDK_SECTOR_SIZE;
1575a1f714marcel	for (n = 0; n < ngts; n++) {
1585a1f714marcel		le32enc(gd + n, sec);
1595a1f714marcel		sec += VMDK_NGTES * sizeof(uint32_t) / VMDK_SECTOR_SIZE;
1605a1f714marcel	}
1615a1f714marcel
16280204ccpfg	rgd = calloc(1, gdsz);
163e23364dmarcel	if (rgd == NULL) {
164e23364dmarcel		free(gd);
165e23364dmarcel		free(desc);
166e23364dmarcel		return (ENOMEM);
167e23364dmarcel	}
168e23364dmarcel	le64enc(&hdr.rgd_offset, sec);
169e23364dmarcel	sec += gdsz / VMDK_SECTOR_SIZE;
170e23364dmarcel	for (n = 0; n < ngts; n++) {
171e23364dmarcel		le32enc(rgd + n, sec);
172e23364dmarcel		sec += VMDK_NGTES * sizeof(uint32_t) / VMDK_SECTOR_SIZE;
173e23364dmarcel	}
174e23364dmarcel
1755a1f714marcel	sec = (sec + grainsz - 1) & ~(grainsz - 1);
1765a1f714marcel
1775a1f714marcel	if (verbose)
1785a1f714marcel		fprintf(stderr, "VMDK: overhead = %ju\n",
1795a1f714marcel		    (uintmax_t)(sec * VMDK_SECTOR_SIZE));
1805a1f714marcel
1815a1f714marcel	le64enc(&hdr.overhead, sec);
1825a1f714marcel	be32enc(&hdr.nl_test, VMDK_NL_TEST);
1835a1f714marcel
18480204ccpfg	gt = calloc(ngts, VMDK_NGTES * sizeof(uint32_t));
185048d89bmarcel	if (gt == NULL) {
186e23364dmarcel		free(rgd);
187048d89bmarcel		free(gd);
188048d89bmarcel		free(desc);
189048d89bmarcel		return (ENOMEM);
190048d89bmarcel	}
19180204ccpfg	gtsz = ngts * VMDK_NGTES * sizeof(uint32_t);
1925a1f714marcel
193ca1d592marcel	cursec = sec;
194ca1d592marcel	blkcnt = (grainsz * VMDK_SECTOR_SIZE) / secsz;
195ca1d592marcel	for (n = 0; n < ngrains; n++) {
196ca1d592marcel		blkofs = n * blkcnt;
197ca1d592marcel		if (image_data(blkofs, blkcnt)) {
198ca1d592marcel			le32enc(gt + n, cursec);
199ca1d592marcel			cursec += grainsz;
200ca1d592marcel		}
201ca1d592marcel	}
2027a44648marcel
2032a0c5f7marcel	error = 0;
2042a0c5f7marcel	if (!error && sparse_write(fd, &hdr, VMDK_SECTOR_SIZE) < 0)
2052a0c5f7marcel		error = errno;
2062a0c5f7marcel	if (!error && sparse_write(fd, desc, desc_len) < 0)
2072a0c5f7marcel		error = errno;
2082a0c5f7marcel	if (!error && sparse_write(fd, gd, gdsz) < 0)
2092a0c5f7marcel		error = errno;
2102a0c5f7marcel	if (!error && sparse_write(fd, gt, gtsz) < 0)
2112a0c5f7marcel		error = errno;
212e23364dmarcel	if (!error && sparse_write(fd, rgd, gdsz) < 0)
213e23364dmarcel		error = errno;
214e23364dmarcel	if (!error && sparse_write(fd, gt, gtsz) < 0)
215e23364dmarcel		error = errno;
216048d89bmarcel	free(gt);
217e23364dmarcel	free(rgd);
218048d89bmarcel	free(gd);
219048d89bmarcel	free(desc);
2202a0c5f7marcel	if (error)
2212a0c5f7marcel		return (error);
2222a0c5f7marcel
223e23364dmarcel	cur = VMDK_SECTOR_SIZE + desc_len + (gdsz + gtsz) * 2;
2242a0c5f7marcel	lim = sec * VMDK_SECTOR_SIZE;
2252a0c5f7marcel	if (cur < lim) {
22680204ccpfg		buf = calloc(1, VMDK_SECTOR_SIZE);
2272a0c5f7marcel		if (buf == NULL)
2282a0c5f7marcel			error = ENOMEM;
2292a0c5f7marcel		while (!error && cur < lim) {
2302a0c5f7marcel			if (sparse_write(fd, buf, VMDK_SECTOR_SIZE) < 0)
2312a0c5f7marcel				error = errno;
2322a0c5f7marcel			cur += VMDK_SECTOR_SIZE;
2332a0c5f7marcel		}
2342a0c5f7marcel		if (buf != NULL)
2352a0c5f7marcel			free(buf);
2362a0c5f7marcel	}
237ca1d592marcel	if (error)
238ca1d592marcel		return (error);
239ca1d592marcel
240ca1d592marcel	blkcnt = (grainsz * VMDK_SECTOR_SIZE) / secsz;
241ca1d592marcel	for (n = 0; n < ngrains; n++) {
242ca1d592marcel		blkofs = n * blkcnt;
243ca1d592marcel		if (image_data(blkofs, blkcnt)) {
244ca1d592marcel			error = image_copyout_region(fd, blkofs, blkcnt);
245ca1d592marcel			if (error)
246ca1d592marcel				return (error);
247ca1d592marcel		}
248ca1d592marcel	}
249ca1d592marcel	return (image_copyout_done(fd));
2507a44648marcel}
2517a44648marcel
2527a44648marcelstatic struct mkimg_format vmdk_format = {
2537a44648marcel	.name = "vmdk",
2547a44648marcel	.description = "Virtual Machine Disk",
255fb17722marcel	.resize = vmdk_resize,
2567a44648marcel	.write = vmdk_write,
2577a44648marcel};
2587a44648marcel
2597a44648marcelFORMAT_DEFINE(vmdk_format);
260