2 * Copyright (c) 2014 Juniper Networks, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/types.h>
31 #include <sys/endian.h>
32 #include <sys/errno.h>
43 #define VMDK_IMAGE_ROUND 1048576
44 #define VMDK_MIN_GRAIN_SIZE 8192
45 #define VMDK_SECTOR_SIZE 512
49 #define VMDK_MAGIC 0x564d444b
51 #define VMDK_VERSION 1
53 #define VMDK_FLAGS_NL_TEST (1 << 0)
54 #define VMDK_FLAGS_RGT_USED (1 << 1)
55 #define VMDK_FLAGS_COMPRESSED (1 << 16)
56 #define VMDK_FLAGS_MARKERS (1 << 17)
62 #define VMDK_NGTES 512
68 #define VMDK_NL_TEST 0x0a200d0a
70 #define VMDK_COMPRESS_NONE 0
71 #define VMDK_COMPRESS_DEFLATE 1
73 } __attribute__((__packed__
));
75 static const char desc_fmt
[] =
76 "# Disk DescriptorFile\n"
79 "parentCID=ffffffff\n"
80 "createType=\"monolithicSparse\"\n"
81 "# Extent description\n"
82 "RW %ju SPARSE \"%s\"\n"
83 "# The Disk Data Base\n"
85 "ddb.adapterType = \"ide\"\n"
86 "ddb.geometry.cylinders = \"%u\"\n"
87 "ddb.geometry.heads = \"%u\"\n"
88 "ddb.geometry.sectors = \"%u\"\n";
90 static uint64_t grainsz
;
93 vmdk_resize(lba_t imgsz
)
97 imagesz
= imgsz
* secsz
;
98 imagesz
= (imagesz
+ VMDK_IMAGE_ROUND
- 1) & ~(VMDK_IMAGE_ROUND
- 1);
99 grainsz
= (blksz
< VMDK_MIN_GRAIN_SIZE
) ? VMDK_MIN_GRAIN_SIZE
: blksz
;
102 fprintf(stderr
, "VMDK: image size = %ju, grain size = %ju\n",
103 (uintmax_t)imagesz
, (uintmax_t)grainsz
);
105 grainsz
/= VMDK_SECTOR_SIZE
;
106 return (image_set_size(imagesz
/ secsz
));
112 struct vmdk_header hdr
;
113 uint32_t *gt
, *gd
, *rgd
;
117 lba_t blkofs
, blkcnt
;
119 uint32_t sec
, cursec
;
120 int error
, desc_len
, n
, ngrains
, ngts
;
122 imagesz
= (image_get_size() * secsz
) / VMDK_SECTOR_SIZE
;
124 memset(&hdr
, 0, sizeof(hdr
));
125 le32enc(&hdr
.magic
, VMDK_MAGIC
);
126 le32enc(&hdr
.version
, VMDK_VERSION
);
127 le32enc(&hdr
.flags
, VMDK_FLAGS_NL_TEST
| VMDK_FLAGS_RGT_USED
);
128 le64enc(&hdr
.capacity
, imagesz
);
129 le64enc(&hdr
.grain_size
, grainsz
);
131 n
= asprintf(&desc
, desc_fmt
, 1 /*version*/, 0 /*CID*/,
132 (uintmax_t)imagesz
/*size*/, "" /*name*/,
133 ncyls
/*cylinders*/, nheads
/*heads*/, nsecs
/*sectors*/);
137 desc_len
= (n
+ VMDK_SECTOR_SIZE
- 1) & ~(VMDK_SECTOR_SIZE
- 1);
138 desc
= realloc(desc
, desc_len
);
139 memset(desc
+ n
, 0, desc_len
- n
);
141 le64enc(&hdr
.desc_offset
, 1);
142 le64enc(&hdr
.desc_size
, desc_len
/ VMDK_SECTOR_SIZE
);
143 le32enc(&hdr
.ngtes
, VMDK_NGTES
);
145 sec
= desc_len
/ VMDK_SECTOR_SIZE
+ 1;
147 ngrains
= imagesz
/ grainsz
;
148 ngts
= (ngrains
+ VMDK_NGTES
- 1) / VMDK_NGTES
;
149 gdsz
= (ngts
* sizeof(uint32_t) + VMDK_SECTOR_SIZE
- 1) &
150 ~(VMDK_SECTOR_SIZE
- 1);
152 gd
= calloc(1, gdsz
);
157 le64enc(&hdr
.gd_offset
, sec
);
158 sec
+= gdsz
/ VMDK_SECTOR_SIZE
;
159 for (n
= 0; n
< ngts
; n
++) {
160 le32enc(gd
+ n
, sec
);
161 sec
+= VMDK_NGTES
* sizeof(uint32_t) / VMDK_SECTOR_SIZE
;
164 rgd
= calloc(1, gdsz
);
170 le64enc(&hdr
.rgd_offset
, sec
);
171 sec
+= gdsz
/ VMDK_SECTOR_SIZE
;
172 for (n
= 0; n
< ngts
; n
++) {
173 le32enc(rgd
+ n
, sec
);
174 sec
+= VMDK_NGTES
* sizeof(uint32_t) / VMDK_SECTOR_SIZE
;
177 sec
= (sec
+ grainsz
- 1) & ~(grainsz
- 1);
180 fprintf(stderr
, "VMDK: overhead = %ju\n",
181 (uintmax_t)(sec
* VMDK_SECTOR_SIZE
));
183 le64enc(&hdr
.overhead
, sec
);
184 be32enc(&hdr
.nl_test
, VMDK_NL_TEST
);
186 gt
= calloc(ngts
, VMDK_NGTES
* sizeof(uint32_t));
193 gtsz
= ngts
* VMDK_NGTES
* sizeof(uint32_t);
196 blkcnt
= (grainsz
* VMDK_SECTOR_SIZE
) / secsz
;
197 for (n
= 0; n
< ngrains
; n
++) {
199 if (image_data(blkofs
, blkcnt
)) {
200 le32enc(gt
+ n
, cursec
);
206 if (!error
&& sparse_write(fd
, &hdr
, VMDK_SECTOR_SIZE
) < 0)
208 if (!error
&& sparse_write(fd
, desc
, desc_len
) < 0)
210 if (!error
&& sparse_write(fd
, gd
, gdsz
) < 0)
212 if (!error
&& sparse_write(fd
, gt
, gtsz
) < 0)
214 if (!error
&& sparse_write(fd
, rgd
, gdsz
) < 0)
216 if (!error
&& sparse_write(fd
, gt
, gtsz
) < 0)
225 cur
= VMDK_SECTOR_SIZE
+ desc_len
+ (gdsz
+ gtsz
) * 2;
226 lim
= sec
* VMDK_SECTOR_SIZE
;
228 buf
= calloc(1, VMDK_SECTOR_SIZE
);
231 while (!error
&& cur
< lim
) {
232 if (sparse_write(fd
, buf
, VMDK_SECTOR_SIZE
) < 0)
234 cur
+= VMDK_SECTOR_SIZE
;
242 blkcnt
= (grainsz
* VMDK_SECTOR_SIZE
) / secsz
;
243 for (n
= 0; n
< ngrains
; n
++) {
245 if (image_data(blkofs
, blkcnt
)) {
246 error
= image_copyout_region(fd
, blkofs
, blkcnt
);
251 return (image_copyout_done(fd
));
254 static struct mkimg_format vmdk_format
= {
256 .description
= "Virtual Machine Disk",
257 .resize
= vmdk_resize
,
261 FORMAT_DEFINE(vmdk_format
);