2 * Copyright 2007, Marcus Overhagen. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
15 #include <sys/types.h>
22 #if defined(__BEOS__) && !defined(__HAIKU__)
23 #define pread(_fd, _buf, _count, _pos) read_pos(_fd, _pos, _buf, _count)
24 #define realpath(x, y) NULL
32 printf("vmdkimage\n");
34 printf("usage: vmdkimage -i <imagesize> -h <headersize> [-c] [-H] "
35 "[-u <uuid>] [-f] <file>\n");
36 printf(" or: vmdkimage -d <file>\n");
37 printf(" -d, --dump dumps info for the image file\n");
38 printf(" -i, --imagesize size of raw partition image file\n");
39 printf(" -h, --headersize size of the vmdk header to write\n");
40 printf(" -f, --file the raw partition image file\n");
41 printf(" -u, --uuid UUID for the image instead of a computed "
43 printf(" -c, --clear-image set the image content to zero\n");
44 printf(" -H, --header-only write only the header\n");
50 dump_image_info(const char *filename
)
52 int image
= open(filename
, O_RDONLY
);
54 fprintf(stderr
, "Error: couldn't open file %s (%s)\n", filename
,
59 SparseExtentHeader header
;
60 if (read(image
, &header
, 512) != 512) {
61 fprintf(stderr
, "Error: couldn't read header: %s\n", strerror(errno
));
65 if (header
.magicNumber
!= VMDK_SPARSE_MAGICNUMBER
) {
66 fprintf(stderr
, "Error: invalid header magic.\n");
70 printf("--------------- Header ---------------\n");
71 printf(" version: %d\n", (int)header
.version
);
72 printf(" flags: %d\n", (int)header
.flags
);
73 printf(" capacity: %d\n", (int)header
.capacity
);
74 printf(" grainSize: %lld\n", (long long)header
.grainSize
);
75 printf(" descriptorOffset: %lld\n", (long long)header
.descriptorOffset
);
76 printf(" descriptorSize: %lld\n", (long long)header
.descriptorSize
);
77 printf(" numGTEsPerGT: %u\n", (unsigned int)header
.numGTEsPerGT
);
78 printf(" rgdOffset: %lld\n", (long long)header
.rgdOffset
);
79 printf(" gdOffset: %lld\n", (long long)header
.gdOffset
);
80 printf(" overHead: %lld\n", (long long)header
.overHead
);
81 printf(" uncleanShutdown: %s\n",
82 header
.uncleanShutdown
? "yes" : "no");
83 printf(" singleEndLineChar: '%c'\n", header
.singleEndLineChar
);
84 printf(" nonEndLineChar: '%c'\n", header
.nonEndLineChar
);
85 printf(" doubleEndLineChar1: '%c'\n", header
.doubleEndLineChar1
);
86 printf(" doubleEndLineChar2: '%c'\n", header
.doubleEndLineChar2
);
88 if (header
.descriptorOffset
!= 0) {
89 printf("\n--------------- Descriptor ---------------\n");
90 size_t descriptorSize
= header
.descriptorSize
* 512 * 2;
91 char *descriptor
= (char *)malloc(descriptorSize
);
92 if (descriptor
== NULL
) {
93 fprintf(stderr
, "Error: cannot allocate descriptor size %u.\n",
94 (unsigned int)descriptorSize
);
98 if (pread(image
, descriptor
, descriptorSize
,
99 header
.descriptorOffset
* 512) != (ssize_t
)descriptorSize
) {
100 fprintf(stderr
, "Error: couldn't read header: %s\n",
115 hash_string(const char *string
)
120 while ((c
= *string
++) != 0) {
121 hash
= c
+ (hash
<< 6) + (hash
<< 16) - hash
;
129 is_valid_uuid(const char *uuid
)
131 const char *kHex
= "0123456789abcdef";
132 for (int i
= 0; i
< 36; i
++) {
135 if (i
== 8 || i
== 13 || i
== 18 || i
== 23) {
140 if (strchr(kHex
, uuid
[i
]) == NULL
)
144 return uuid
[36] == '\0';
149 main(int argc
, char *argv
[])
151 uint64_t headerSize
= 0;
152 uint64_t imageSize
= 0;
153 const char *file
= NULL
;
154 const char *uuid
= NULL
;
155 bool headerOnly
= false;
156 bool clearImage
= false;
157 bool dumpOnly
= false;
159 if (sizeof(SparseExtentHeader
) != 512) {
160 fprintf(stderr
, "compilation error: struct size is %u byte\n",
161 (unsigned)sizeof(SparseExtentHeader
));
167 static struct option long_options
[] = {
168 {"dump", no_argument
, 0, 'd'},
169 {"headersize", required_argument
, 0, 'h'},
170 {"imagesize", required_argument
, 0, 'i'},
171 {"file", required_argument
, 0, 'f'},
172 {"uuid", required_argument
, 0, 'u'},
173 {"clear-image", no_argument
, 0, 'c'},
174 {"header-only", no_argument
, 0, 'H'},
178 opterr
= 0; /* don't print errors */
179 c
= getopt_long(argc
, argv
, "dh:i:u:cHf:", long_options
, NULL
);
189 headerSize
= strtoull(optarg
, NULL
, 10);
190 if (strchr(optarg
, 'G') || strchr(optarg
, 'g'))
191 headerSize
*= 1024 * 1024 * 1024;
192 else if (strchr(optarg
, 'M') || strchr(optarg
, 'm'))
193 headerSize
*= 1024 * 1024;
194 else if (strchr(optarg
, 'K') || strchr(optarg
, 'k'))
199 imageSize
= strtoull(optarg
, NULL
, 10);
200 if (strchr(optarg
, 'G') || strchr(optarg
, 'g'))
201 imageSize
*= 1024 * 1024 * 1024;
202 else if (strchr(optarg
, 'M') || strchr(optarg
, 'm'))
203 imageSize
*= 1024 * 1024;
204 else if (strchr(optarg
, 'K') || strchr(optarg
, 'k'))
210 if (!is_valid_uuid(uuid
)) {
211 fprintf(stderr
, "Error: invalid UUID given (use "
212 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format only).\n");
234 if (file
== NULL
&& optind
== argc
- 1)
237 if (dumpOnly
&& file
!= NULL
) {
238 dump_image_info(file
);
242 if (!headerSize
|| !imageSize
|| !file
)
246 SparseExtentHeader header
;
248 if (headerSize
< sizeof(desc
) + sizeof(header
)) {
249 fprintf(stderr
, "Error: header size must be at least %u byte\n",
250 (unsigned)(sizeof(desc
) + sizeof(header
)));
254 if (headerSize
% 512) {
255 fprintf(stderr
, "Error: header size must be a multiple of 512 bytes\n");
259 if (imageSize
% 512) {
260 fprintf(stderr
, "Error: image size must be a multiple of 512 bytes\n");
264 // arbitrary 1 GB limitation
265 if (headerSize
> 0x40000000ULL
) {
266 fprintf(stderr
, "Error: header size too large\n");
270 // arbitrary 160 GB limitation
271 if (imageSize
> 0x2800000000ULL
) {
272 fprintf(stderr
, "Error: image size too large\n");
276 const char *name
= strrchr(file
, '/');
277 name
= name
? (name
+ 1) : file
;
279 // printf("headerSize %llu\n", headerSize);
280 // printf("imageSize %llu\n", imageSize);
281 // printf("file %s\n", file);
290 cylinders
= imageSize
/ (sectors
* heads
* 512);
291 while (cylinders
> 1024) {
295 off_t actualImageSize
= (off_t
)cylinders
* sectors
* heads
* 512;
297 memset(desc
, 0, sizeof(desc
));
298 memset(&header
, 0, sizeof(header
));
300 header
.magicNumber
= VMDK_SPARSE_MAGICNUMBER
;
301 header
.version
= VMDK_SPARSE_VERSION
;
304 header
.grainSize
= 16;
305 header
.descriptorOffset
= 1;
306 header
.descriptorSize
= (sizeof(desc
) + 511) / 512;
307 header
.numGTEsPerGT
= 512;
308 header
.rgdOffset
= 0;
310 header
.overHead
= headerSize
/ 512;
311 header
.uncleanShutdown
= 0;
312 header
.singleEndLineChar
= '\n';
313 header
.nonEndLineChar
= ' ';
314 header
.doubleEndLineChar1
= '\r';
315 header
.doubleEndLineChar2
= '\n';
317 // Generate UUID for the image by hashing its full path
318 uint64_t uuid1
= 0, uuid2
= 0, uuid3
= 0, uuid4
= 0, uuid5
= 0;
320 char fullPath
[PATH_MAX
+ 6];
321 strcpy(fullPath
, "Haiku");
323 if (realpath(file
, fullPath
+ 5) == NULL
)
324 strncpy(fullPath
+ 5, file
, sizeof(fullPath
) - 5);
326 size_t pathLength
= strlen(fullPath
);
327 for (size_t i
= pathLength
; i
< 42; i
++) {
328 // fill rest with some numbers
329 fullPath
[i
] = i
% 10 + '0';
334 uuid1
= hash_string(fullPath
);
335 uuid2
= hash_string(fullPath
+ 5);
336 uuid3
= hash_string(fullPath
+ 13);
337 uuid4
= hash_string(fullPath
+ 19);
338 uuid5
= hash_string(fullPath
+ 29);
341 // Create embedded descriptor
343 "# Disk Descriptor File\n"
346 "parentCID=ffffffff\n"
347 "createType=\"monolithicFlat\"\n");
348 sprintf(desc
+ strlen(desc
),
349 "# Extent Description\n"
350 "RW %llu FLAT \"%s\" %llu\n",
351 (unsigned long long)actualImageSize
/ 512, name
,
352 (unsigned long long)headerSize
/ 512);
353 sprintf(desc
+ strlen(desc
),
355 "ddb.toolsVersion = \"0\"\n"
356 "ddb.virtualHWVersion = \"3\"\n"
357 "ddb.geometry.sectors = \"%llu\"\n"
358 "ddb.adapterType = \"ide\"\n"
359 "ddb.geometry.heads = \"%llu\"\n"
360 "ddb.geometry.cylinders = \"%llu\"\n",
361 (unsigned long long)sectors
, (unsigned long long)heads
,
362 (unsigned long long)cylinders
);
365 sprintf(desc
+ strlen(desc
),
366 "ddb.uuid.image=\"%08llx-%04llx-%04llx-%04llx-%012llx\"\n",
367 uuid1
& 0xffffffffLL
, uuid2
& 0xffffLL
, uuid3
& 0xffffLL
,
368 uuid4
& 0xffffLL
, uuid5
& 0xffffffffffffLL
);
370 sprintf(desc
+ strlen(desc
), "ddb.uuid.image=\"%s\"\n", uuid
);
372 int fd
= open(file
, O_RDWR
| O_CREAT
, 0666);
374 fprintf(stderr
, "Error: couldn't open file %s (%s)\n", file
,
378 if (write(fd
, &header
, sizeof(header
)) != sizeof(header
))
381 if (write(fd
, desc
, sizeof(desc
)) != sizeof(desc
))
384 if ((uint64_t)lseek(fd
, headerSize
- 1, SEEK_SET
) != headerSize
- 1)
387 if (1 != write(fd
, "", 1))
391 if ((clearImage
&& ftruncate(fd
, headerSize
) != 0)
392 || ftruncate(fd
, actualImageSize
+ headerSize
) != 0) {
393 fprintf(stderr
, "Error: resizing file %s failed (%s)\n", file
,
403 fprintf(stderr
, "Error: writing file %s failed (%s)\n", file
,