1 /* 2 * Copyright 2007, Marcus Overhagen. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 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> 18 19 #include <vmdk.h> 20 21 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 26 27 28 static void 29 print_usage() 30 { 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); 46 } 47 48 49 static void 50 dump_image_info(const char *filename) 51 { 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); 57 } 58 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); 63 } 64 65 if (header.magicNumber != VMDK_SPARSE_MAGICNUMBER) { 66 fprintf(stderr, "Error: invalid header magic.\n"); 67 exit(EXIT_FAILURE); 68 } 69 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); 87 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); 96 } 97 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); 103 } 104 105 puts(descriptor); 106 putchar('\n'); 107 free(descriptor); 108 } 109 110 close(image); 111 } 112 113 114 static uint64_t 115 hash_string(const char *string) 116 { 117 uint64_t hash = 0; 118 char c; 119 120 while ((c = *string++) != 0) { 121 hash = c + (hash << 6) + (hash << 16) - hash; 122 } 123 124 return hash; 125 } 126 127 128 static bool 129 is_valid_uuid(const char *uuid) 130 { 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; 139 } 140 if (strchr(kHex, uuid[i]) == NULL) 141 return false; 142 } 143 144 return uuid[36] == '\0'; 145 } 146 147 148 int 149 main(int argc, char *argv[]) 150 { 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; 158 159 if (sizeof(SparseExtentHeader) != 512) { 160 fprintf(stderr, "compilation error: struct size is %u byte\n", 161 (unsigned)sizeof(SparseExtentHeader)); 162 exit(EXIT_FAILURE); 163 } 164 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} 176 }; 177 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; 182 183 switch (c) { 184 case 'd': 185 dumpOnly = true; 186 break; 187 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; 197 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; 207 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); 214 } 215 break; 216 217 case 'f': 218 file = optarg; 219 break; 220 221 case 'c': 222 clearImage = true; 223 break; 224 225 case 'H': 226 headerOnly = true; 227 break; 228 229 default: 230 print_usage(); 231 } 232 } 233 234 if (file == NULL && optind == argc - 1) 235 file = argv[optind]; 236 237 if (dumpOnly && file != NULL) { 238 dump_image_info(file); 239 return 0; 240 } 241 242 if (!headerSize || !imageSize || !file) 243 print_usage(); 244 245 char desc[1024]; 246 SparseExtentHeader header; 247 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); 252 } 253 254 if (headerSize % 512) { 255 fprintf(stderr, "Error: header size must be a multiple of 512 bytes\n"); 256 exit(EXIT_FAILURE); 257 } 258 259 if (imageSize % 512) { 260 fprintf(stderr, "Error: image size must be a multiple of 512 bytes\n"); 261 exit(EXIT_FAILURE); 262 } 263 264 // arbitrary 1 GB limitation 265 if (headerSize > 0x40000000ULL) { 266 fprintf(stderr, "Error: header size too large\n"); 267 exit(EXIT_FAILURE); 268 } 269 270 // arbitrary 160 GB limitation 271 if (imageSize > 0x2800000000ULL) { 272 fprintf(stderr, "Error: image size too large\n"); 273 exit(EXIT_FAILURE); 274 } 275 276 const char *name = strrchr(file, '/'); 277 name = name ? (name + 1) : file; 278 279 // printf("headerSize %llu\n", headerSize); 280 // printf("imageSize %llu\n", imageSize); 281 // printf("file %s\n", file); 282 283 uint64_t sectors; 284 uint64_t heads; 285 uint64_t cylinders; 286 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; 294 } 295 off_t actualImageSize = (off_t)cylinders * sectors * heads * 512; 296 297 memset(desc, 0, sizeof(desc)); 298 memset(&header, 0, sizeof(header)); 299 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'; 316 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"); 322 323 if (realpath(file, fullPath + 5) == NULL) 324 strncpy(fullPath + 5, file, sizeof(fullPath) - 5); 325 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'; 330 } 331 if (pathLength < 42) 332 fullPath[42] = '\0'; 333 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); 339 } 340 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); 363 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); 371 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); 377 } 378 if (write(fd, &header, sizeof(header)) != sizeof(header)) 379 goto write_err; 380 381 if (write(fd, desc, sizeof(desc)) != sizeof(desc)) 382 goto write_err; 383 384 if ((uint64_t)lseek(fd, headerSize - 1, SEEK_SET) != headerSize - 1) 385 goto write_err; 386 387 if (1 != write(fd, "", 1)) 388 goto write_err; 389 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); 396 } 397 } 398 399 close(fd); 400 return 0; 401 402 write_err: 403 fprintf(stderr, "Error: writing file %s failed (%s)\n", file, 404 strerror(errno)); 405 close(fd); 406 exit(EXIT_FAILURE); 407 } 408