1f0e9dcedSMarcel Moolenaar /*-
2f0e9dcedSMarcel Moolenaar * Copyright (c) 2014 Juniper Networks, Inc.
3f0e9dcedSMarcel Moolenaar * All rights reserved.
4f0e9dcedSMarcel Moolenaar *
5f0e9dcedSMarcel Moolenaar * Redistribution and use in source and binary forms, with or without
6f0e9dcedSMarcel Moolenaar * modification, are permitted provided that the following conditions
7f0e9dcedSMarcel Moolenaar * are met:
8f0e9dcedSMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright
9f0e9dcedSMarcel Moolenaar * notice, this list of conditions and the following disclaimer.
10f0e9dcedSMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright
11f0e9dcedSMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the
12f0e9dcedSMarcel Moolenaar * documentation and/or other materials provided with the distribution.
13f0e9dcedSMarcel Moolenaar *
14f0e9dcedSMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15f0e9dcedSMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16f0e9dcedSMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17f0e9dcedSMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18f0e9dcedSMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19f0e9dcedSMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20f0e9dcedSMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21f0e9dcedSMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22f0e9dcedSMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23f0e9dcedSMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24f0e9dcedSMarcel Moolenaar * SUCH DAMAGE.
25f0e9dcedSMarcel Moolenaar */
26f0e9dcedSMarcel Moolenaar
27f0e9dcedSMarcel Moolenaar #include <sys/cdefs.h>
28f0e9dcedSMarcel Moolenaar #include <sys/errno.h>
29f0e9dcedSMarcel Moolenaar #include <stdint.h>
30f0e9dcedSMarcel Moolenaar #include <stdio.h>
31f0e9dcedSMarcel Moolenaar #include <stdlib.h>
32f0e9dcedSMarcel Moolenaar #include <string.h>
33f0e9dcedSMarcel Moolenaar
34*5aad7d9aSMarcel Moolenaar #include "endian.h"
35f0e9dcedSMarcel Moolenaar #include "image.h"
36f0e9dcedSMarcel Moolenaar #include "format.h"
37f0e9dcedSMarcel Moolenaar #include "mkimg.h"
38f0e9dcedSMarcel Moolenaar
39f0e9dcedSMarcel Moolenaar #define VMDK_IMAGE_ROUND 1048576
40f0e9dcedSMarcel Moolenaar #define VMDK_MIN_GRAIN_SIZE 8192
41f0e9dcedSMarcel Moolenaar #define VMDK_SECTOR_SIZE 512
42f0e9dcedSMarcel Moolenaar
43f0e9dcedSMarcel Moolenaar struct vmdk_header {
44f0e9dcedSMarcel Moolenaar uint32_t magic;
45f0e9dcedSMarcel Moolenaar #define VMDK_MAGIC 0x564d444b
46f0e9dcedSMarcel Moolenaar uint32_t version;
47f0e9dcedSMarcel Moolenaar #define VMDK_VERSION 1
48f0e9dcedSMarcel Moolenaar uint32_t flags;
49f0e9dcedSMarcel Moolenaar #define VMDK_FLAGS_NL_TEST (1 << 0)
50f0e9dcedSMarcel Moolenaar #define VMDK_FLAGS_RGT_USED (1 << 1)
51f0e9dcedSMarcel Moolenaar #define VMDK_FLAGS_COMPRESSED (1 << 16)
52f0e9dcedSMarcel Moolenaar #define VMDK_FLAGS_MARKERS (1 << 17)
53f0e9dcedSMarcel Moolenaar uint64_t capacity;
54f0e9dcedSMarcel Moolenaar uint64_t grain_size;
55f0e9dcedSMarcel Moolenaar uint64_t desc_offset;
56f0e9dcedSMarcel Moolenaar uint64_t desc_size;
57f0e9dcedSMarcel Moolenaar uint32_t ngtes;
58f0e9dcedSMarcel Moolenaar #define VMDK_NGTES 512
59f0e9dcedSMarcel Moolenaar uint64_t rgd_offset;
60f0e9dcedSMarcel Moolenaar uint64_t gd_offset;
61f0e9dcedSMarcel Moolenaar uint64_t overhead;
62f0e9dcedSMarcel Moolenaar uint8_t unclean;
63f0e9dcedSMarcel Moolenaar uint32_t nl_test;
64f0e9dcedSMarcel Moolenaar #define VMDK_NL_TEST 0x0a200d0a
65f0e9dcedSMarcel Moolenaar uint16_t compress;
66f0e9dcedSMarcel Moolenaar #define VMDK_COMPRESS_NONE 0
67f0e9dcedSMarcel Moolenaar #define VMDK_COMPRESS_DEFLATE 1
68f0e9dcedSMarcel Moolenaar char padding[433];
69f0e9dcedSMarcel Moolenaar } __attribute__((__packed__));
70f0e9dcedSMarcel Moolenaar
71f0e9dcedSMarcel Moolenaar static const char desc_fmt[] =
72f0e9dcedSMarcel Moolenaar "# Disk DescriptorFile\n"
73f0e9dcedSMarcel Moolenaar "version=%d\n"
74f0e9dcedSMarcel Moolenaar "CID=%08x\n"
75f0e9dcedSMarcel Moolenaar "parentCID=ffffffff\n"
76f0e9dcedSMarcel Moolenaar "createType=\"monolithicSparse\"\n"
77f0e9dcedSMarcel Moolenaar "# Extent description\n"
78f0e9dcedSMarcel Moolenaar "RW %ju SPARSE \"%s\"\n"
79f0e9dcedSMarcel Moolenaar "# The Disk Data Base\n"
80f0e9dcedSMarcel Moolenaar "#DDB\n"
81f0e9dcedSMarcel Moolenaar "ddb.adapterType = \"ide\"\n"
82f0e9dcedSMarcel Moolenaar "ddb.geometry.cylinders = \"%u\"\n"
83f0e9dcedSMarcel Moolenaar "ddb.geometry.heads = \"%u\"\n"
84f0e9dcedSMarcel Moolenaar "ddb.geometry.sectors = \"%u\"\n";
85f0e9dcedSMarcel Moolenaar
86f0e9dcedSMarcel Moolenaar static uint64_t grainsz;
87f0e9dcedSMarcel Moolenaar
88f0e9dcedSMarcel Moolenaar static int
vmdk_resize(lba_t imgsz)89f0e9dcedSMarcel Moolenaar vmdk_resize(lba_t imgsz)
90f0e9dcedSMarcel Moolenaar {
91f0e9dcedSMarcel Moolenaar uint64_t imagesz;
92f0e9dcedSMarcel Moolenaar
93f0e9dcedSMarcel Moolenaar imagesz = imgsz * secsz;
94f0e9dcedSMarcel Moolenaar imagesz = (imagesz + VMDK_IMAGE_ROUND - 1) & ~(VMDK_IMAGE_ROUND - 1);
95f0e9dcedSMarcel Moolenaar grainsz = (blksz < VMDK_MIN_GRAIN_SIZE) ? VMDK_MIN_GRAIN_SIZE : blksz;
96f0e9dcedSMarcel Moolenaar
97f0e9dcedSMarcel Moolenaar if (verbose)
98f0e9dcedSMarcel Moolenaar fprintf(stderr, "VMDK: image size = %ju, grain size = %ju\n",
99f0e9dcedSMarcel Moolenaar (uintmax_t)imagesz, (uintmax_t)grainsz);
100f0e9dcedSMarcel Moolenaar
101f0e9dcedSMarcel Moolenaar grainsz /= VMDK_SECTOR_SIZE;
102f0e9dcedSMarcel Moolenaar return (image_set_size(imagesz / secsz));
103f0e9dcedSMarcel Moolenaar }
104f0e9dcedSMarcel Moolenaar
105f0e9dcedSMarcel Moolenaar static int
vmdk_write(int fd)106f0e9dcedSMarcel Moolenaar vmdk_write(int fd)
107f0e9dcedSMarcel Moolenaar {
108f0e9dcedSMarcel Moolenaar struct vmdk_header hdr;
10962193493SMarcel Moolenaar uint32_t *gt, *gd, *rgd;
110f0e9dcedSMarcel Moolenaar char *buf, *desc;
111f0e9dcedSMarcel Moolenaar off_t cur, lim;
112f0e9dcedSMarcel Moolenaar uint64_t imagesz;
113a0f136e1SMarcel Moolenaar lba_t blkofs, blkcnt;
114f0e9dcedSMarcel Moolenaar size_t gdsz, gtsz;
115a0f136e1SMarcel Moolenaar uint32_t sec, cursec;
116f0e9dcedSMarcel Moolenaar int error, desc_len, n, ngrains, ngts;
117f0e9dcedSMarcel Moolenaar
118f0e9dcedSMarcel Moolenaar imagesz = (image_get_size() * secsz) / VMDK_SECTOR_SIZE;
119f0e9dcedSMarcel Moolenaar
120f0e9dcedSMarcel Moolenaar memset(&hdr, 0, sizeof(hdr));
121f0e9dcedSMarcel Moolenaar le32enc(&hdr.magic, VMDK_MAGIC);
122f0e9dcedSMarcel Moolenaar le32enc(&hdr.version, VMDK_VERSION);
123f0e9dcedSMarcel Moolenaar le32enc(&hdr.flags, VMDK_FLAGS_NL_TEST | VMDK_FLAGS_RGT_USED);
124f0e9dcedSMarcel Moolenaar le64enc(&hdr.capacity, imagesz);
125f0e9dcedSMarcel Moolenaar le64enc(&hdr.grain_size, grainsz);
126f0e9dcedSMarcel Moolenaar
127f0e9dcedSMarcel Moolenaar n = asprintf(&desc, desc_fmt, 1 /*version*/, 0 /*CID*/,
128f0e9dcedSMarcel Moolenaar (uintmax_t)imagesz /*size*/, "" /*name*/,
129f0e9dcedSMarcel Moolenaar ncyls /*cylinders*/, nheads /*heads*/, nsecs /*sectors*/);
130f0e9dcedSMarcel Moolenaar if (n == -1)
131f0e9dcedSMarcel Moolenaar return (ENOMEM);
132f0e9dcedSMarcel Moolenaar
133f0e9dcedSMarcel Moolenaar desc_len = (n + VMDK_SECTOR_SIZE - 1) & ~(VMDK_SECTOR_SIZE - 1);
134f0e9dcedSMarcel Moolenaar desc = realloc(desc, desc_len);
135f0e9dcedSMarcel Moolenaar memset(desc + n, 0, desc_len - n);
136f0e9dcedSMarcel Moolenaar
137f0e9dcedSMarcel Moolenaar le64enc(&hdr.desc_offset, 1);
138f0e9dcedSMarcel Moolenaar le64enc(&hdr.desc_size, desc_len / VMDK_SECTOR_SIZE);
139f0e9dcedSMarcel Moolenaar le32enc(&hdr.ngtes, VMDK_NGTES);
140f0e9dcedSMarcel Moolenaar
141f0e9dcedSMarcel Moolenaar sec = desc_len / VMDK_SECTOR_SIZE + 1;
142f0e9dcedSMarcel Moolenaar
143f0e9dcedSMarcel Moolenaar ngrains = imagesz / grainsz;
144f0e9dcedSMarcel Moolenaar ngts = (ngrains + VMDK_NGTES - 1) / VMDK_NGTES;
145f0e9dcedSMarcel Moolenaar gdsz = (ngts * sizeof(uint32_t) + VMDK_SECTOR_SIZE - 1) &
146f0e9dcedSMarcel Moolenaar ~(VMDK_SECTOR_SIZE - 1);
14762193493SMarcel Moolenaar
1483af7c805SPedro F. Giffuni gd = calloc(1, gdsz);
149f0e9dcedSMarcel Moolenaar if (gd == NULL) {
150f0e9dcedSMarcel Moolenaar free(desc);
151f0e9dcedSMarcel Moolenaar return (ENOMEM);
152f0e9dcedSMarcel Moolenaar }
15362193493SMarcel Moolenaar le64enc(&hdr.gd_offset, sec);
154f0e9dcedSMarcel Moolenaar sec += gdsz / VMDK_SECTOR_SIZE;
155f0e9dcedSMarcel Moolenaar for (n = 0; n < ngts; n++) {
156f0e9dcedSMarcel Moolenaar le32enc(gd + n, sec);
157f0e9dcedSMarcel Moolenaar sec += VMDK_NGTES * sizeof(uint32_t) / VMDK_SECTOR_SIZE;
158f0e9dcedSMarcel Moolenaar }
159f0e9dcedSMarcel Moolenaar
1603af7c805SPedro F. Giffuni rgd = calloc(1, gdsz);
16162193493SMarcel Moolenaar if (rgd == NULL) {
16262193493SMarcel Moolenaar free(gd);
16362193493SMarcel Moolenaar free(desc);
16462193493SMarcel Moolenaar return (ENOMEM);
16562193493SMarcel Moolenaar }
16662193493SMarcel Moolenaar le64enc(&hdr.rgd_offset, sec);
16762193493SMarcel Moolenaar sec += gdsz / VMDK_SECTOR_SIZE;
16862193493SMarcel Moolenaar for (n = 0; n < ngts; n++) {
16962193493SMarcel Moolenaar le32enc(rgd + n, sec);
17062193493SMarcel Moolenaar sec += VMDK_NGTES * sizeof(uint32_t) / VMDK_SECTOR_SIZE;
17162193493SMarcel Moolenaar }
17262193493SMarcel Moolenaar
173f0e9dcedSMarcel Moolenaar sec = (sec + grainsz - 1) & ~(grainsz - 1);
174f0e9dcedSMarcel Moolenaar
175f0e9dcedSMarcel Moolenaar if (verbose)
176f0e9dcedSMarcel Moolenaar fprintf(stderr, "VMDK: overhead = %ju\n",
177f0e9dcedSMarcel Moolenaar (uintmax_t)(sec * VMDK_SECTOR_SIZE));
178f0e9dcedSMarcel Moolenaar
179f0e9dcedSMarcel Moolenaar le64enc(&hdr.overhead, sec);
180f0e9dcedSMarcel Moolenaar be32enc(&hdr.nl_test, VMDK_NL_TEST);
181f0e9dcedSMarcel Moolenaar
1823af7c805SPedro F. Giffuni gt = calloc(ngts, VMDK_NGTES * sizeof(uint32_t));
183f0e9dcedSMarcel Moolenaar if (gt == NULL) {
18462193493SMarcel Moolenaar free(rgd);
185f0e9dcedSMarcel Moolenaar free(gd);
186f0e9dcedSMarcel Moolenaar free(desc);
187f0e9dcedSMarcel Moolenaar return (ENOMEM);
188f0e9dcedSMarcel Moolenaar }
1893af7c805SPedro F. Giffuni gtsz = ngts * VMDK_NGTES * sizeof(uint32_t);
190f0e9dcedSMarcel Moolenaar
191a0f136e1SMarcel Moolenaar cursec = sec;
192a0f136e1SMarcel Moolenaar blkcnt = (grainsz * VMDK_SECTOR_SIZE) / secsz;
193a0f136e1SMarcel Moolenaar for (n = 0; n < ngrains; n++) {
194a0f136e1SMarcel Moolenaar blkofs = n * blkcnt;
195a0f136e1SMarcel Moolenaar if (image_data(blkofs, blkcnt)) {
196a0f136e1SMarcel Moolenaar le32enc(gt + n, cursec);
197a0f136e1SMarcel Moolenaar cursec += grainsz;
198a0f136e1SMarcel Moolenaar }
199a0f136e1SMarcel Moolenaar }
200f0e9dcedSMarcel Moolenaar
201f0e9dcedSMarcel Moolenaar error = 0;
202f0e9dcedSMarcel Moolenaar if (!error && sparse_write(fd, &hdr, VMDK_SECTOR_SIZE) < 0)
203f0e9dcedSMarcel Moolenaar error = errno;
204f0e9dcedSMarcel Moolenaar if (!error && sparse_write(fd, desc, desc_len) < 0)
205f0e9dcedSMarcel Moolenaar error = errno;
206f0e9dcedSMarcel Moolenaar if (!error && sparse_write(fd, gd, gdsz) < 0)
207f0e9dcedSMarcel Moolenaar error = errno;
208f0e9dcedSMarcel Moolenaar if (!error && sparse_write(fd, gt, gtsz) < 0)
209f0e9dcedSMarcel Moolenaar error = errno;
21062193493SMarcel Moolenaar if (!error && sparse_write(fd, rgd, gdsz) < 0)
21162193493SMarcel Moolenaar error = errno;
21262193493SMarcel Moolenaar if (!error && sparse_write(fd, gt, gtsz) < 0)
21362193493SMarcel Moolenaar error = errno;
214f0e9dcedSMarcel Moolenaar free(gt);
21562193493SMarcel Moolenaar free(rgd);
216f0e9dcedSMarcel Moolenaar free(gd);
217f0e9dcedSMarcel Moolenaar free(desc);
218f0e9dcedSMarcel Moolenaar if (error)
219f0e9dcedSMarcel Moolenaar return (error);
220f0e9dcedSMarcel Moolenaar
22162193493SMarcel Moolenaar cur = VMDK_SECTOR_SIZE + desc_len + (gdsz + gtsz) * 2;
222f0e9dcedSMarcel Moolenaar lim = sec * VMDK_SECTOR_SIZE;
223f0e9dcedSMarcel Moolenaar if (cur < lim) {
2243af7c805SPedro F. Giffuni buf = calloc(1, VMDK_SECTOR_SIZE);
225f0e9dcedSMarcel Moolenaar if (buf == NULL)
226f0e9dcedSMarcel Moolenaar error = ENOMEM;
227f0e9dcedSMarcel Moolenaar while (!error && cur < lim) {
228f0e9dcedSMarcel Moolenaar if (sparse_write(fd, buf, VMDK_SECTOR_SIZE) < 0)
229f0e9dcedSMarcel Moolenaar error = errno;
230f0e9dcedSMarcel Moolenaar cur += VMDK_SECTOR_SIZE;
231f0e9dcedSMarcel Moolenaar }
232f0e9dcedSMarcel Moolenaar if (buf != NULL)
233f0e9dcedSMarcel Moolenaar free(buf);
234f0e9dcedSMarcel Moolenaar }
235a0f136e1SMarcel Moolenaar if (error)
236f0e9dcedSMarcel Moolenaar return (error);
237a0f136e1SMarcel Moolenaar
238a0f136e1SMarcel Moolenaar blkcnt = (grainsz * VMDK_SECTOR_SIZE) / secsz;
239a0f136e1SMarcel Moolenaar for (n = 0; n < ngrains; n++) {
240a0f136e1SMarcel Moolenaar blkofs = n * blkcnt;
241a0f136e1SMarcel Moolenaar if (image_data(blkofs, blkcnt)) {
242a0f136e1SMarcel Moolenaar error = image_copyout_region(fd, blkofs, blkcnt);
243a0f136e1SMarcel Moolenaar if (error)
244a0f136e1SMarcel Moolenaar return (error);
245a0f136e1SMarcel Moolenaar }
246a0f136e1SMarcel Moolenaar }
247a0f136e1SMarcel Moolenaar return (image_copyout_done(fd));
248f0e9dcedSMarcel Moolenaar }
249f0e9dcedSMarcel Moolenaar
250f0e9dcedSMarcel Moolenaar static struct mkimg_format vmdk_format = {
251f0e9dcedSMarcel Moolenaar .name = "vmdk",
252f0e9dcedSMarcel Moolenaar .description = "Virtual Machine Disk",
253f0e9dcedSMarcel Moolenaar .resize = vmdk_resize,
254f0e9dcedSMarcel Moolenaar .write = vmdk_write,
255f0e9dcedSMarcel Moolenaar };
256f0e9dcedSMarcel Moolenaar
257f0e9dcedSMarcel Moolenaar FORMAT_DEFINE(vmdk_format);
258