]> CyberLeo.Net >> Repos - FreeBSD/releng/10.1.git/blob - usr.bin/mkimg/vhd.c
Document r273098, options for displaying mkimg(1) internals
[FreeBSD/releng/10.1.git] / usr.bin / mkimg / vhd.c
1 /*-
2  * Copyright (c) 2014 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/types.h>
31 #include <sys/endian.h>
32 #include <sys/errno.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <uuid.h>
38
39 #include "image.h"
40 #include "format.h"
41 #include "mkimg.h"
42
43 #ifndef __has_extension
44 #define __has_extension(x)      0
45 #endif
46
47 /*
48  * General notes:
49  * o   File is in network byte order.
50  * o   The timestamp is seconds since 1/1/2000 12:00:00 AM UTC
51  *
52  * This file is divided in 3 parts:
53  * 1.  Common definitions
54  * 2.  Dynamic VHD support
55  * 3.  Fixed VHD support
56  */
57
58 /*
59  * PART 1: Common definitions
60  */
61
62 #define VHD_SECTOR_SIZE 512
63 #define VHD_BLOCK_SIZE  (4096 * VHD_SECTOR_SIZE)        /* 2MB blocks */
64
65 struct vhd_footer {
66         uint64_t        cookie;
67 #define VHD_FOOTER_COOKIE       0x636f6e6563746978
68         uint32_t        features;
69 #define VHD_FEATURES_TEMPORARY  0x01
70 #define VHD_FEATURES_RESERVED   0x02
71         uint32_t        version;
72 #define VHD_VERSION             0x00010000
73         uint64_t        data_offset;
74         uint32_t        timestamp;
75         uint32_t        creator_tool;
76 #define VHD_CREATOR_TOOL        0x2a696d67      /* FreeBSD mkimg */
77         uint32_t        creator_version;
78 #define VHD_CREATOR_VERSION     0x00010000
79         uint32_t        creator_os;
80 #define VHD_CREATOR_OS          0x46425344
81         uint64_t        original_size;
82         uint64_t        current_size;
83         uint16_t        cylinders;
84         uint8_t         heads;
85         uint8_t         sectors;
86         uint32_t        disk_type;
87 #define VHD_DISK_TYPE_FIXED     2
88 #define VHD_DISK_TYPE_DYNAMIC   3
89 #define VHD_DISK_TYPE_DIFF      4
90         uint32_t        checksum;
91         uuid_t          id;
92         uint8_t         saved_state;
93         uint8_t         _reserved[427];
94 };
95 #if __has_extension(c_static_assert)
96 _Static_assert(sizeof(struct vhd_footer) == VHD_SECTOR_SIZE,
97     "Wrong size for footer");
98 #endif
99
100 static uint32_t
101 vhd_checksum(void *buf, size_t sz)
102 {
103         uint8_t *p = buf;
104         uint32_t sum;
105         size_t ofs;
106
107         sum = 0;
108         for (ofs = 0; ofs < sz; ofs++)
109                 sum += p[ofs];
110         return (~sum);
111 }
112
113 static void
114 vhd_geometry(struct vhd_footer *footer, uint64_t image_size)
115 {
116         lba_t imgsz;
117         long cth;
118
119         /* Respect command line options if possible. */
120         if (nheads > 1 && nheads < 256 &&
121             nsecs > 1 && nsecs < 256 &&
122             ncyls < 65536) {
123                 be16enc(&footer->cylinders, ncyls);
124                 footer->heads = nheads;
125                 footer->sectors = nsecs;
126                 return;
127         }
128
129         imgsz = image_size / VHD_SECTOR_SIZE;
130         if (imgsz > 65536 * 16 * 255)
131                 imgsz = 65536 * 16 * 255;
132         if (imgsz >= 65535 * 16 * 63) {
133                 be16enc(&footer->cylinders, imgsz / (16 * 255));
134                 footer->heads = 16;
135                 footer->sectors = 255;
136                 return;
137         }
138         footer->sectors = 17;
139         cth = imgsz / 17;
140         footer->heads = (cth + 1023) / 1024;
141         if (footer->heads < 4)
142                 footer->heads = 4;
143         if (cth >= (footer->heads * 1024) || footer->heads > 16) {
144                 footer->heads = 16;
145                 footer->sectors = 31;
146                 cth = imgsz / 31;
147         }
148         if (cth >= (footer->heads * 1024)) {
149                 footer->heads = 16;
150                 footer->sectors = 63;
151                 cth = imgsz / 63;
152         }
153         be16enc(&footer->cylinders, cth / footer->heads);
154 }
155
156 static uint32_t
157 vhd_timestamp(void)
158 {
159         time_t t;
160
161         if (!unit_testing) {
162                 t = time(NULL);
163                 return (t - 0x386d4380);
164         }
165
166         return (0x01234567);
167 }
168
169 static void
170 vhd_uuid_enc(void *buf, const uuid_t *uuid)
171 {
172         uint8_t *p = buf;
173         int i;
174
175         be32enc(p, uuid->time_low);
176         be16enc(p + 4, uuid->time_mid);
177         be16enc(p + 6, uuid->time_hi_and_version);
178         p[8] = uuid->clock_seq_hi_and_reserved;
179         p[9] = uuid->clock_seq_low;
180         for (i = 0; i < _UUID_NODE_LEN; i++)
181                 p[10 + i] = uuid->node[i];
182 }
183
184 static void
185 vhd_make_footer(struct vhd_footer *footer, uint64_t image_size,
186     uint32_t disk_type, uint64_t data_offset)
187 {
188         uuid_t id;
189
190         memset(footer, 0, sizeof(*footer));
191         be64enc(&footer->cookie, VHD_FOOTER_COOKIE);
192         be32enc(&footer->features, VHD_FEATURES_RESERVED);
193         be32enc(&footer->version, VHD_VERSION);
194         be64enc(&footer->data_offset, data_offset);
195         be32enc(&footer->timestamp, vhd_timestamp());
196         be32enc(&footer->creator_tool, VHD_CREATOR_TOOL);
197         be32enc(&footer->creator_version, VHD_CREATOR_VERSION);
198         be32enc(&footer->creator_os, VHD_CREATOR_OS);
199         be64enc(&footer->original_size, image_size);
200         be64enc(&footer->current_size, image_size);
201         vhd_geometry(footer, image_size);
202         be32enc(&footer->disk_type, disk_type);
203         mkimg_uuid(&id);
204         vhd_uuid_enc(&footer->id, &id);
205         be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer)));
206 }
207
208 /*
209  * We round the image size to 2MB for both the dynamic and
210  * fixed VHD formats. For dynamic VHD, this is needed to
211  * have the image size be a multiple of the grain size. For
212  * fixed VHD this is not really needed, but makes sure that
213  * it's easy to convert from fixed VHD to dynamic VHD.
214  */
215 static int
216 vhd_resize(lba_t imgsz)
217 {
218         uint64_t imagesz;
219
220         imagesz = imgsz * secsz;
221         imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1);
222         return (image_set_size(imagesz / secsz));
223 }
224
225 /*
226  * PART 2: Dynamic VHD support
227  *
228  * Notes:
229  * o   File layout:
230  *      copy of disk footer
231  *      dynamic disk header
232  *      block allocation table (BAT)
233  *      data blocks
234  *      disk footer
235  */
236
237 struct vhd_dyn_header {
238         uint64_t        cookie;
239 #define VHD_HEADER_COOKIE       0x6378737061727365
240         uint64_t        data_offset;
241         uint64_t        table_offset;
242         uint32_t        version;
243         uint32_t        max_entries;
244         uint32_t        block_size;
245         uint32_t        checksum;
246         uuid_t          parent_id;
247         uint32_t        parent_timestamp;
248         char            _reserved1[4];
249         uint16_t        parent_name[256];       /* UTF-16 */
250         struct {
251                 uint32_t        code;
252                 uint32_t        data_space;
253                 uint32_t        data_length;
254                 uint32_t        _reserved;
255                 uint64_t        data_offset;
256         } parent_locator[8];
257         char            _reserved2[256];
258 };
259 #if __has_extension(c_static_assert)
260 _Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2,
261     "Wrong size for header");
262 #endif
263
264 static int
265 vhd_dyn_write(int fd)
266 {
267         struct vhd_footer footer;
268         struct vhd_dyn_header header;
269         uint64_t imgsz;
270         lba_t blk, blkcnt, nblks;
271         uint32_t *bat;
272         void *bitmap;
273         size_t batsz;
274         uint32_t sector;
275         int bat_entries, error, entry;
276
277         imgsz = image_get_size() * secsz;
278         bat_entries = imgsz / VHD_BLOCK_SIZE;
279
280         vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer));
281         if (sparse_write(fd, &footer, sizeof(footer)) < 0)
282                 return (errno);
283
284         memset(&header, 0, sizeof(header));
285         be64enc(&header.cookie, VHD_HEADER_COOKIE);
286         be64enc(&header.data_offset, ~0ULL);
287         be64enc(&header.table_offset, sizeof(footer) + sizeof(header));
288         be32enc(&header.version, VHD_VERSION);
289         be32enc(&header.max_entries, bat_entries);
290         be32enc(&header.block_size, VHD_BLOCK_SIZE);
291         be32enc(&header.checksum, vhd_checksum(&header, sizeof(header)));
292         if (sparse_write(fd, &header, sizeof(header)) < 0)
293                 return (errno);
294
295         batsz = bat_entries * sizeof(uint32_t);
296         batsz = (batsz + VHD_SECTOR_SIZE - 1) & ~(VHD_SECTOR_SIZE - 1);
297         bat = malloc(batsz);
298         if (bat == NULL)
299                 return (errno);
300         memset(bat, 0xff, batsz);
301         blkcnt = VHD_BLOCK_SIZE / secsz;
302         sector = (sizeof(footer) + sizeof(header) + batsz) / VHD_SECTOR_SIZE;
303         for (entry = 0; entry < bat_entries; entry++) {
304                 blk = entry * blkcnt;
305                 if (image_data(blk, blkcnt)) {
306                         be32enc(&bat[entry], sector);
307                         sector += (VHD_BLOCK_SIZE / VHD_SECTOR_SIZE) + 1;
308                 }
309         }
310         if (sparse_write(fd, bat, batsz) < 0) {
311                 free(bat);
312                 return (errno);
313         }
314         free(bat);
315
316         bitmap = malloc(VHD_SECTOR_SIZE);
317         if (bitmap == NULL)
318                 return (errno);
319         memset(bitmap, 0xff, VHD_SECTOR_SIZE);
320
321         blk = 0;
322         blkcnt = VHD_BLOCK_SIZE / secsz;
323         error = 0;
324         nblks = image_get_size();
325         while (blk < nblks) {
326                 if (!image_data(blk, blkcnt)) {
327                         blk += blkcnt;
328                         continue;
329                 }
330                 if (sparse_write(fd, bitmap, VHD_SECTOR_SIZE) < 0) {
331                         error = errno;
332                         break;
333                 }
334                 error = image_copyout_region(fd, blk, blkcnt);
335                 if (error)
336                         break;
337                 blk += blkcnt;
338         }
339         free(bitmap);
340         if (blk != nblks)
341                 return (error);
342
343         if (sparse_write(fd, &footer, sizeof(footer)) < 0)
344                 return (errno);
345
346         return (0);
347 }
348
349 static struct mkimg_format vhd_dyn_format = {
350         .name = "vhd",
351         .description = "Virtual Hard Disk",
352         .resize = vhd_resize,
353         .write = vhd_dyn_write,
354 };
355
356 FORMAT_DEFINE(vhd_dyn_format);
357
358 /*
359  * PART 2: Fixed VHD
360  */
361
362 static int
363 vhd_fix_write(int fd)
364 {
365         struct vhd_footer footer;
366         uint64_t imgsz;
367         int error;
368
369         error = image_copyout(fd);
370         if (!error) {
371                 imgsz = image_get_size() * secsz;
372                 vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_FIXED, ~0ULL);
373                 if (sparse_write(fd, &footer, sizeof(footer)) < 0)
374                         error = errno;
375         }
376         return (error);
377 }
378
379 static struct mkimg_format vhd_fix_format = {
380         .name = "vhdf",
381         .description = "Fixed Virtual Hard Disk",
382         .resize = vhd_resize,
383         .write = vhd_fix_write,
384 };
385
386 FORMAT_DEFINE(vhd_fix_format);