1 /* 2 * Copyright 2007, Marcus Overhagen. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <getopt.h> 9 #include <limits.h> 10 #include <stdarg.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/types.h> 15 #include <sys/stat.h> 16 #include <unistd.h> 17 18 #include "vmdkimage.h" 19 20 #if defined(__BEOS__) && !defined(__HAIKU__) 21 #define pread(_fd, _buf, _count, _pos) read_pos(_fd, _pos, _buf, _count) 22 #define realpath(x, y) NULL 23 #endif 24 25 static void 26 print_usage() 27 { 28 printf("\n"); 29 printf("vmdkimage\n"); 30 printf("\n"); 31 printf("usage: vmdkimage -i <imagesize> -h <headersize> [-c] [-H] " 32 "[-u <uuid>] [-f] <file>\n"); 33 printf(" or: vmdkimage -d <file>\n"); 34 printf(" -d, --dump dumps info for the image file\n"); 35 printf(" -i, --imagesize size of raw partition image file\n"); 36 printf(" -h, --headersize size of the vmdk header to write\n"); 37 printf(" -f, --file the raw partition image file\n"); 38 printf(" -u, --uuid UUID for the image instead of a computed " 39 "one\n"); 40 printf(" -c, --clear-image set the image content to zero\n"); 41 printf(" -H, --header-only write only the header\n"); 42 exit(EXIT_FAILURE); 43 } 44 45 46 static void 47 dump_image_info(const char *filename) 48 { 49 int image = open(filename, O_RDONLY); 50 if (image < 0) { 51 fprintf(stderr, "Error: couldn't open file %s (%s)\n", filename, 52 strerror(errno)); 53 exit(EXIT_FAILURE); 54 } 55 56 SparseExtentHeader header; 57 if (read(image, &header, 512) != 512) { 58 fprintf(stderr, "Error: couldn't read header: %s\n", strerror(errno)); 59 exit(EXIT_FAILURE); 60 } 61 62 if (header.magicNumber != SPARSE_MAGICNUMBER) { 63 fprintf(stderr, "Error: invalid header magic.\n"); 64 exit(EXIT_FAILURE); 65 } 66 67 printf("--------------- Header ---------------\n"); 68 printf(" version: %d\n", (int)header.version); 69 printf(" flags: %d\n", (int)header.flags); 70 printf(" capacity: %d\n", (int)header.capacity); 71 printf(" grainSize: %lld\n", header.grainSize); 72 printf(" descriptorOffset: %lld\n", header.descriptorOffset); 73 printf(" descriptorSize: %lld\n", header.descriptorSize); 74 printf(" numGTEsPerGT: %u\n", (unsigned int)header.numGTEsPerGT); 75 printf(" rgdOffset: %lld\n", header.rgdOffset); 76 printf(" gdOffset: %lld\n", header.gdOffset); 77 printf(" overHead: %lld\n", header.overHead); 78 printf(" uncleanShutdown: %s\n", 79 header.uncleanShutdown ? "yes" : "no"); 80 printf(" singleEndLineChar: '%c'\n", header.singleEndLineChar); 81 printf(" nonEndLineChar: '%c'\n", header.nonEndLineChar); 82 printf(" doubleEndLineChar1: '%c'\n", header.doubleEndLineChar1); 83 printf(" doubleEndLineChar2: '%c'\n", header.doubleEndLineChar2); 84 85 if (header.descriptorOffset != 0) { 86 printf("\n--------------- Descriptor ---------------\n"); 87 size_t descriptorSize = header.descriptorSize * 512 * 2; 88 char *descriptor = (char *)malloc(descriptorSize); 89 if (descriptor == NULL) { 90 fprintf(stderr, "Error: cannot allocate descriptor size %u.\n", 91 (unsigned int)descriptorSize); 92 exit(EXIT_FAILURE); 93 } 94 95 if (pread(image, descriptor, descriptorSize, 96 header.descriptorOffset * 512) != (ssize_t)descriptorSize) { 97 fprintf(stderr, "Error: couldn't read header: %s\n", 98 strerror(errno)); 99 exit(EXIT_FAILURE); 100 } 101 102 puts(descriptor); 103 putchar('\n'); 104 free(descriptor); 105 } 106 107 close(image); 108 } 109 110 111 static uint64 112 hash_string(const char *string) 113 { 114 uint64 hash = 0; 115 char c; 116 117 while ((c = *string++) != 0) { 118 hash = c + (hash << 6) + (hash << 16) - hash; 119 } 120 121 return hash; 122 } 123 124 125 static bool 126 is_valid_uuid(const char *uuid) 127 { 128 const char *kHex = "0123456789abcdef"; 129 for (int i = 0; i < 36; i++) { 130 if (!uuid[i]) 131 return false; 132 if (i == 8 || i == 13 || i == 18 || i == 23) { 133 if (uuid[i] != '-') 134 return false; 135 continue; 136 } 137 if (strchr(kHex, uuid[i]) == NULL) 138 return false; 139 } 140 141 return uuid[36] == '\0'; 142 } 143 144 145 int 146 main(int argc, char *argv[]) 147 { 148 uint64 headerSize = 0; 149 uint64 imageSize = 0; 150 const char *file = NULL; 151 const char *uuid = NULL; 152 bool headerOnly = false; 153 bool clearImage = false; 154 bool dumpOnly = false; 155 156 if (sizeof(SparseExtentHeader) != 512) { 157 fprintf(stderr, "compilation error: struct size is %u byte\n", 158 (unsigned)sizeof(SparseExtentHeader)); 159 exit(EXIT_FAILURE); 160 } 161 162 while (1) { 163 int c; 164 static struct option long_options[] = { 165 {"dump", no_argument, 0, 'd'}, 166 {"headersize", required_argument, 0, 'h'}, 167 {"imagesize", required_argument, 0, 'i'}, 168 {"file", required_argument, 0, 'f'}, 169 {"uuid", required_argument, 0, 'u'}, 170 {"clear-image", no_argument, 0, 'c'}, 171 {"header-only", no_argument, 0, 'H'}, 172 {0, 0, 0, 0} 173 }; 174 175 opterr = 0; /* don't print errors */ 176 c = getopt_long(argc, argv, "dh:i:u:cHf:", long_options, NULL); 177 if (c == -1) 178 break; 179 180 switch (c) { 181 case 'd': 182 dumpOnly = true; 183 break; 184 185 case 'h': 186 headerSize = strtoull(optarg, NULL, 10); 187 if (strchr(optarg, 'G') || strchr(optarg, 'g')) 188 headerSize *= 1024 * 1024 * 1024; 189 else if (strchr(optarg, 'M') || strchr(optarg, 'm')) 190 headerSize *= 1024 * 1024; 191 else if (strchr(optarg, 'K') || strchr(optarg, 'k')) 192 headerSize *= 1024; 193 break; 194 195 case 'i': 196 imageSize = strtoull(optarg, NULL, 10); 197 if (strchr(optarg, 'G') || strchr(optarg, 'g')) 198 imageSize *= 1024 * 1024 * 1024; 199 else if (strchr(optarg, 'M') || strchr(optarg, 'm')) 200 imageSize *= 1024 * 1024; 201 else if (strchr(optarg, 'K') || strchr(optarg, 'k')) 202 imageSize *= 1024; 203 break; 204 205 case 'u': 206 uuid = optarg; 207 if (!is_valid_uuid(uuid)) { 208 fprintf(stderr, "Error: invalid UUID given (use " 209 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format only).\n"); 210 exit(EXIT_FAILURE); 211 } 212 break; 213 214 case 'f': 215 file = optarg; 216 break; 217 218 case 'c': 219 clearImage = true; 220 break; 221 222 case 'H': 223 headerOnly = true; 224 break; 225 226 default: 227 print_usage(); 228 } 229 } 230 231 if (file == NULL && optind == argc - 1) 232 file = argv[optind]; 233 234 if (dumpOnly && file != NULL) { 235 dump_image_info(file); 236 return 0; 237 } 238 239 if (!headerSize || !imageSize || !file) 240 print_usage(); 241 242 char desc[1024]; 243 SparseExtentHeader header; 244 245 if (headerSize < sizeof(desc) + sizeof(header)) { 246 fprintf(stderr, "Error: header size must be at least %u byte\n", 247 (unsigned)(sizeof(desc) + sizeof(header))); 248 exit(EXIT_FAILURE); 249 } 250 251 if (headerSize % 512) { 252 fprintf(stderr, "Error: header size must be a multiple of 512 bytes\n"); 253 exit(EXIT_FAILURE); 254 } 255 256 if (imageSize % 512) { 257 fprintf(stderr, "Error: image size must be a multiple of 512 bytes\n"); 258 exit(EXIT_FAILURE); 259 } 260 261 // arbitrary 1 GB limitation 262 if (headerSize > 0x40000000ULL) { 263 fprintf(stderr, "Error: header size too large\n"); 264 exit(EXIT_FAILURE); 265 } 266 267 // arbitrary 4 GB limitation 268 if (imageSize > 0x100000000ULL) { 269 fprintf(stderr, "Error: image size too large\n"); 270 exit(EXIT_FAILURE); 271 } 272 273 const char *name = strrchr(file, '/'); 274 name = name ? (name + 1) : file; 275 276 // printf("headerSize %llu\n", headerSize); 277 // printf("imageSize %llu\n", imageSize); 278 // printf("file %s\n", file); 279 280 uint64 sectors; 281 uint64 heads; 282 uint64 cylinders; 283 284 // TODO: fixme! 285 sectors = 63; 286 heads = 16; 287 cylinders = imageSize / (sectors * heads * 512); 288 while (cylinders > 1024) { 289 cylinders /= 2; 290 heads *= 2; 291 } 292 off_t actualImageSize = (off_t)cylinders * sectors * heads * 512; 293 294 memset(desc, 0, sizeof(desc)); 295 memset(&header, 0, sizeof(header)); 296 297 header.magicNumber = SPARSE_MAGICNUMBER; 298 header.version = 1; 299 header.flags = 1; 300 header.capacity = 0; 301 header.grainSize = 16; 302 header.descriptorOffset = 1; 303 header.descriptorSize = (sizeof(desc) + 511) / 512; 304 header.numGTEsPerGT = 512; 305 header.rgdOffset = 0; 306 header.gdOffset = 0; 307 header.overHead = headerSize / 512; 308 header.uncleanShutdown = 0; 309 header.singleEndLineChar = '\n'; 310 header.nonEndLineChar = ' '; 311 header.doubleEndLineChar1 = '\r'; 312 header.doubleEndLineChar2 = '\n'; 313 314 // Generate UUID for the image by hashing its full path 315 uint64 uuid1 = 0, uuid2 = 0, uuid3 = 0, uuid4 = 0, uuid5 = 0; 316 if (uuid == NULL) { 317 char fullPath[PATH_MAX + 6]; 318 strcpy(fullPath, "Haiku"); 319 320 if (realpath(file, fullPath + 5) == NULL) 321 strncpy(fullPath + 5, file, sizeof(fullPath) - 5); 322 323 size_t pathLength = strlen(fullPath); 324 for (size_t i = pathLength; i < 42; i++) { 325 // fill rest with some numbers 326 fullPath[i] = i % 10 + '0'; 327 } 328 if (pathLength < 42) 329 fullPath[42] = '\0'; 330 331 uuid1 = hash_string(fullPath); 332 uuid2 = hash_string(fullPath + 5); 333 uuid3 = hash_string(fullPath + 13); 334 uuid4 = hash_string(fullPath + 19); 335 uuid5 = hash_string(fullPath + 29); 336 } 337 338 // Create embedded descriptor 339 strcat(desc, 340 "# Disk Descriptor File\n" 341 "version=1\n" 342 "CID=fffffffe\n" 343 "parentCID=ffffffff\n" 344 "createType=\"monolithicFlat\"\n"); 345 sprintf(desc + strlen(desc), 346 "# Extent Description\n" 347 "RW %llu FLAT \"%s\" %llu\n", 348 actualImageSize / 512, name, headerSize / 512); 349 sprintf(desc + strlen(desc), 350 "# Disk Data Base\n" 351 "ddb.toolsVersion = \"0\"\n" 352 "ddb.virtualHWVersion = \"3\"\n" 353 "ddb.geometry.sectors = \"%llu\"\n" 354 "ddb.adapterType = \"ide\"\n" 355 "ddb.geometry.heads = \"%llu\"\n" 356 "ddb.geometry.cylinders = \"%llu\"\n", 357 sectors, heads, cylinders); 358 359 if (uuid == NULL) { 360 sprintf(desc + strlen(desc), 361 "ddb.uuid.image=\"%08llx-%04llx-%04llx-%04llx-%012llx\"\n", 362 uuid1 & 0xffffffff, uuid2 & 0xffff, uuid3 & 0xffff, uuid4 & 0xffff, 363 uuid5 & 0xffffffffffffLL); 364 } else 365 sprintf(desc + strlen(desc), "ddb.uuid.image=\"%s\"\n", uuid); 366 367 int fd = open(file, O_RDWR | O_CREAT, 0666); 368 if (fd < 0) { 369 fprintf(stderr, "Error: couldn't open file %s (%s)\n", file, 370 strerror(errno)); 371 exit(EXIT_FAILURE); 372 } 373 if (write(fd, &header, sizeof(header)) != sizeof(header)) 374 goto write_err; 375 376 if (write(fd, desc, sizeof(desc)) != sizeof(desc)) 377 goto write_err; 378 379 if ((uint64)lseek(fd, headerSize - 1, SEEK_SET) != headerSize - 1) 380 goto write_err; 381 382 if (1 != write(fd, "", 1)) 383 goto write_err; 384 385 if (!headerOnly) { 386 if ((clearImage && ftruncate(fd, headerSize) != 0) 387 || ftruncate(fd, actualImageSize + headerSize) != 0) { 388 fprintf(stderr, "Error: resizing file %s failed (%s)\n", file, 389 strerror(errno)); 390 exit(EXIT_FAILURE); 391 } 392 } 393 394 close(fd); 395 return 0; 396 397 write_err: 398 fprintf(stderr, "Error: writing file %s failed (%s)\n", file, 399 strerror(errno)); 400 close(fd); 401 exit(EXIT_FAILURE); 402 } 403