HaikuDepot: notify work status from main window
[haiku.git] / src / tools / vmdkimage / vmdkimage.cpp
blob88793630f2b944ab27bc551df0d31195d9980885
1 /*
2 * Copyright 2007, Marcus Overhagen. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <limits.h>
11 #include <stdarg.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
19 #include <vmdk.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
25 #endif
28 static void
29 print_usage()
31 printf("\n");
32 printf("vmdkimage\n");
33 printf("\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 "
42 "one\n");
43 printf(" -c, --clear-image set the image content to zero\n");
44 printf(" -H, --header-only write only the header\n");
45 exit(EXIT_FAILURE);
49 static void
50 dump_image_info(const char *filename)
52 int image = open(filename, O_RDONLY);
53 if (image < 0) {
54 fprintf(stderr, "Error: couldn't open file %s (%s)\n", filename,
55 strerror(errno));
56 exit(EXIT_FAILURE);
59 SparseExtentHeader header;
60 if (read(image, &header, 512) != 512) {
61 fprintf(stderr, "Error: couldn't read header: %s\n", strerror(errno));
62 exit(EXIT_FAILURE);
65 if (header.magicNumber != VMDK_SPARSE_MAGICNUMBER) {
66 fprintf(stderr, "Error: invalid header magic.\n");
67 exit(EXIT_FAILURE);
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);
95 exit(EXIT_FAILURE);
98 if (pread(image, descriptor, descriptorSize,
99 header.descriptorOffset * 512) != (ssize_t)descriptorSize) {
100 fprintf(stderr, "Error: couldn't read header: %s\n",
101 strerror(errno));
102 exit(EXIT_FAILURE);
105 puts(descriptor);
106 putchar('\n');
107 free(descriptor);
110 close(image);
114 static uint64_t
115 hash_string(const char *string)
117 uint64_t hash = 0;
118 char c;
120 while ((c = *string++) != 0) {
121 hash = c + (hash << 6) + (hash << 16) - hash;
124 return hash;
128 static bool
129 is_valid_uuid(const char *uuid)
131 const char *kHex = "0123456789abcdef";
132 for (int i = 0; i < 36; i++) {
133 if (!uuid[i])
134 return false;
135 if (i == 8 || i == 13 || i == 18 || i == 23) {
136 if (uuid[i] != '-')
137 return false;
138 continue;
140 if (strchr(kHex, uuid[i]) == NULL)
141 return false;
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));
162 exit(EXIT_FAILURE);
165 while (1) {
166 int c;
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'},
175 {0, 0, 0, 0}
178 opterr = 0; /* don't print errors */
179 c = getopt_long(argc, argv, "dh:i:u:cHf:", long_options, NULL);
180 if (c == -1)
181 break;
183 switch (c) {
184 case 'd':
185 dumpOnly = true;
186 break;
188 case 'h':
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'))
195 headerSize *= 1024;
196 break;
198 case 'i':
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'))
205 imageSize *= 1024;
206 break;
208 case 'u':
209 uuid = optarg;
210 if (!is_valid_uuid(uuid)) {
211 fprintf(stderr, "Error: invalid UUID given (use "
212 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format only).\n");
213 exit(EXIT_FAILURE);
215 break;
217 case 'f':
218 file = optarg;
219 break;
221 case 'c':
222 clearImage = true;
223 break;
225 case 'H':
226 headerOnly = true;
227 break;
229 default:
230 print_usage();
234 if (file == NULL && optind == argc - 1)
235 file = argv[optind];
237 if (dumpOnly && file != NULL) {
238 dump_image_info(file);
239 return 0;
242 if (!headerSize || !imageSize || !file)
243 print_usage();
245 char desc[1024];
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)));
251 exit(EXIT_FAILURE);
254 if (headerSize % 512) {
255 fprintf(stderr, "Error: header size must be a multiple of 512 bytes\n");
256 exit(EXIT_FAILURE);
259 if (imageSize % 512) {
260 fprintf(stderr, "Error: image size must be a multiple of 512 bytes\n");
261 exit(EXIT_FAILURE);
264 // arbitrary 1 GB limitation
265 if (headerSize > 0x40000000ULL) {
266 fprintf(stderr, "Error: header size too large\n");
267 exit(EXIT_FAILURE);
270 // arbitrary 160 GB limitation
271 if (imageSize > 0x2800000000ULL) {
272 fprintf(stderr, "Error: image size too large\n");
273 exit(EXIT_FAILURE);
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);
283 uint64_t sectors;
284 uint64_t heads;
285 uint64_t cylinders;
287 // TODO: fixme!
288 sectors = 63;
289 heads = 16;
290 cylinders = imageSize / (sectors * heads * 512);
291 while (cylinders > 1024) {
292 cylinders /= 2;
293 heads *= 2;
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;
302 header.flags = 1;
303 header.capacity = 0;
304 header.grainSize = 16;
305 header.descriptorOffset = 1;
306 header.descriptorSize = (sizeof(desc) + 511) / 512;
307 header.numGTEsPerGT = 512;
308 header.rgdOffset = 0;
309 header.gdOffset = 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;
319 if (uuid == NULL) {
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';
331 if (pathLength < 42)
332 fullPath[42] = '\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
342 strcat(desc,
343 "# Disk Descriptor File\n"
344 "version=1\n"
345 "CID=fffffffe\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),
354 "# Disk Data Base\n"
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);
364 if (uuid == NULL) {
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);
369 } else
370 sprintf(desc + strlen(desc), "ddb.uuid.image=\"%s\"\n", uuid);
372 int fd = open(file, O_RDWR | O_CREAT, 0666);
373 if (fd < 0) {
374 fprintf(stderr, "Error: couldn't open file %s (%s)\n", file,
375 strerror(errno));
376 exit(EXIT_FAILURE);
378 if (write(fd, &header, sizeof(header)) != sizeof(header))
379 goto write_err;
381 if (write(fd, desc, sizeof(desc)) != sizeof(desc))
382 goto write_err;
384 if ((uint64_t)lseek(fd, headerSize - 1, SEEK_SET) != headerSize - 1)
385 goto write_err;
387 if (1 != write(fd, "", 1))
388 goto write_err;
390 if (!headerOnly) {
391 if ((clearImage && ftruncate(fd, headerSize) != 0)
392 || ftruncate(fd, actualImageSize + headerSize) != 0) {
393 fprintf(stderr, "Error: resizing file %s failed (%s)\n", file,
394 strerror(errno));
395 exit(EXIT_FAILURE);
399 close(fd);
400 return 0;
402 write_err:
403 fprintf(stderr, "Error: writing file %s failed (%s)\n", file,
404 strerror(errno));
405 close(fd);
406 exit(EXIT_FAILURE);