1 /* 2 * Copyright 2005-2008, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <unistd.h> 12 #include <sys/stat.h> 13 14 #include <ByteOrder.h> 15 #include <Drivers.h> 16 #include <Entry.h> 17 #include <File.h> 18 #include <fs_info.h> 19 #include <Resources.h> 20 #include <TypeConstants.h> 21 22 // Linux and FreeBSD support 23 #ifdef HAIKU_HOST_PLATFORM_LINUX 24 # include <ctype.h> 25 # include <linux/hdreg.h> 26 # include <sys/ioctl.h> 27 28 # include "PartitionMap.h" 29 # include "PartitionMapParser.h" 30 #elif HAIKU_HOST_PLATFORM_FREEBSD 31 # include <ctype.h> 32 # include <sys/disklabel.h> 33 # include <sys/disk.h> 34 # include <sys/ioctl.h> 35 36 # include "PartitionMap.h" 37 # include "PartitionMapParser.h" 38 #endif 39 40 #ifdef __HAIKU__ 41 # include <image.h> 42 #endif 43 44 45 static const char *kCommandName = "makebootable"; 46 47 static const int kBootCodeSize = 1024; 48 static const int kFirstBootCodePartSize = 512; 49 static const int kSecondBootcodePartOffset = 676; 50 static const int kSecondBootcodePartSize = kBootCodeSize 51 - kSecondBootcodePartOffset; 52 static const int kPartitionOffsetOffset = 506; 53 54 static int kArgc; 55 static const char *const *kArgv; 56 57 // usage 58 const char *kUsage = 59 "Usage: %s [ options ] <file> ...\n" 60 "\n" 61 "Makes the specified BFS partitions/devices bootable by writing boot code\n" 62 "into the first two sectors. It doesn't mark the partition(s) active.\n" 63 "\n" 64 "If a given <file> refers to a directory, the partition/device on which the\n" 65 "directory resides will be made bootable. If it refers to a regular file,\n" 66 "the file is considered a disk image and the boot code will be written to\n" 67 "it.\n" 68 "\n" 69 "Options:\n" 70 " -h, --help - Print this help text and exit.\n" 71 " --dry-run - Do everything but actually writing the boot block to disk.\n" 72 "\n" 73 "[compatibility]\n" 74 " -alert - Compatibility option. Ignored.\n" 75 " -full - Compatibility option. Ignored.\n" 76 " -safe - Compatibility option. Fail when specified.\n" 77 ; 78 79 80 // print_usage 81 static void 82 print_usage(bool error) 83 { 84 // get command name 85 const char *commandName = NULL; 86 if (kArgc > 0) { 87 if (const char *lastSlash = strchr(kArgv[0], '/')) 88 commandName = lastSlash + 1; 89 else 90 commandName = kArgv[0]; 91 } 92 93 if (!commandName || strlen(commandName) == 0) 94 commandName = kCommandName; 95 96 // print usage 97 fprintf((error ? stderr : stdout), kUsage, commandName, commandName, 98 commandName); 99 } 100 101 102 // print_usage_and_exit 103 static void 104 print_usage_and_exit(bool error) 105 { 106 print_usage(error); 107 exit(error ? 1 : 0); 108 } 109 110 111 // read_boot_code_data 112 static uint8 * 113 read_boot_code_data(const char* programPath) 114 { 115 // open our executable 116 BFile executableFile; 117 status_t error = executableFile.SetTo(programPath, B_READ_ONLY); 118 if (error != B_OK) { 119 fprintf(stderr, "Error: Failed to open my executable file (\"%s\": " 120 "%s\n", programPath, strerror(error)); 121 exit(1); 122 } 123 124 uint8 *bootCodeData = new uint8[kBootCodeSize]; 125 126 // open our resources 127 BResources resources; 128 error = resources.SetTo(&executableFile); 129 const void *resourceData = NULL; 130 if (error == B_OK) { 131 // read the boot block from the resources 132 size_t resourceSize; 133 resourceData = resources.LoadResource(B_RAW_TYPE, 666, &resourceSize); 134 135 if (resourceData && resourceSize != (size_t)kBootCodeSize) { 136 resourceData = NULL; 137 printf("Warning: Something is fishy with my resources! The boot " 138 "code doesn't have the correct size. Trying the attribute " 139 "instead ...\n"); 140 } 141 } 142 143 if (resourceData) { 144 // found boot data in the resources 145 memcpy(bootCodeData, resourceData, kBootCodeSize); 146 } else { 147 // no boot data in the resources; try the attribute 148 ssize_t bytesRead = executableFile.ReadAttr("BootCode", B_RAW_TYPE, 149 0, bootCodeData, kBootCodeSize); 150 if (bytesRead < 0) { 151 fprintf(stderr, "Error: Failed to read boot code from resources " 152 "or attribute."); 153 exit(1); 154 } 155 if (bytesRead != kBootCodeSize) { 156 fprintf(stderr, "Error: Failed to read boot code from resources, " 157 "and the boot code in the attribute has the wrong size!"); 158 exit(1); 159 } 160 } 161 162 return bootCodeData; 163 } 164 165 166 // write_boot_code_part 167 static void 168 write_boot_code_part(const char *fileName, int fd, off_t imageOffset, 169 const uint8 *bootCodeData, int offset, int size, bool dryRun) 170 { 171 if (!dryRun) { 172 ssize_t bytesWritten = write_pos(fd, imageOffset + offset, 173 bootCodeData + offset, size); 174 if (bytesWritten != size) { 175 fprintf(stderr, "Error: Failed to write to \"%s\": %s\n", fileName, 176 strerror(bytesWritten < 0 ? errno : B_ERROR)); 177 } 178 } 179 } 180 181 182 #ifdef __HAIKU__ 183 static status_t 184 find_own_image(image_info *info) 185 { 186 int32 cookie = 0; 187 while (get_next_image_info(B_CURRENT_TEAM, &cookie, info) == B_OK) { 188 if (((addr_t)info->text <= (addr_t)find_own_image 189 && (addr_t)info->text + info->text_size 190 > (addr_t)find_own_image)) { 191 return B_OK; 192 } 193 } 194 195 return B_NAME_NOT_FOUND; 196 } 197 #endif 198 199 200 // main 201 int 202 main(int argc, const char *const *argv) 203 { 204 kArgc = argc; 205 kArgv = argv; 206 207 if (argc < 2) 208 print_usage_and_exit(true); 209 210 // parameters 211 const char **files = new const char*[argc]; 212 int fileCount = 0; 213 bool dryRun = false; 214 off_t startOffset = 0; 215 216 // parse arguments 217 for (int argi = 1; argi < argc;) { 218 const char *arg = argv[argi++]; 219 220 if (arg[0] == '-') { 221 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { 222 print_usage_and_exit(false); 223 } else if (strcmp(arg, "--dry-run") == 0) { 224 dryRun = true; 225 } else if (strcmp(arg, "-alert") == 0) { 226 // ignore 227 } else if (strcmp(arg, "-full") == 0) { 228 // ignore 229 } else if (strcmp(arg, "--start-offset") == 0) { 230 if (argi >= argc) 231 print_usage_and_exit(true); 232 startOffset = strtoll(argv[argi++], NULL, 0); 233 } else if (strcmp(arg, "-safe") == 0) { 234 fprintf(stderr, "Error: Sorry, BeOS R3 isn't supported!\n"); 235 exit(1); 236 } else { 237 print_usage_and_exit(true); 238 } 239 240 } else { 241 files[fileCount++] = arg; 242 } 243 } 244 245 // we need at least one file 246 if (fileCount == 0) 247 print_usage_and_exit(true); 248 249 // read the boot code 250 uint8 *bootCodeData = NULL; 251 #ifndef __HAIKU__ 252 bootCodeData = read_boot_code_data(argv[0]); 253 #else 254 image_info info; 255 if (find_own_image(&info) == B_OK) 256 bootCodeData = read_boot_code_data(info.name); 257 #endif 258 if (!bootCodeData) { 259 fprintf(stderr, "Error: Failed to read "); 260 exit(1); 261 } 262 263 // iterate through the files and make them bootable 264 status_t error; 265 for (int i = 0; i < fileCount; i++) { 266 const char *fileName = files[i]; 267 BEntry entry; 268 error = entry.SetTo(fileName, true); 269 if (error != B_OK) { 270 fprintf(stderr, "Error: Failed to open \"%s\": %s\n", 271 fileName, strerror(error)); 272 exit(1); 273 } 274 275 // get stat to check the type of the file 276 struct stat st; 277 error = entry.GetStat(&st); 278 if (error != B_OK) { 279 fprintf(stderr, "Error: Failed to stat \"%s\": %s\n", 280 fileName, strerror(error)); 281 exit(1); 282 } 283 284 bool noPartition = false; 285 int64 partitionOffset = 0; 286 fs_info info; // needs to be here (we use the device name later) 287 if (S_ISDIR(st.st_mode)) { 288 #ifdef __BEOS__ 289 290 // a directory: get the device 291 error = fs_stat_dev(st.st_dev, &info); 292 if (error != B_OK) { 293 fprintf(stderr, "Error: Failed to determine device for " 294 "\"%s\": %s\n", fileName, strerror(error)); 295 exit(1); 296 } 297 298 fileName = info.device_name; 299 300 #else 301 302 (void)info; 303 fprintf(stderr, "Error: Specifying directories not supported " 304 "on this platform!\n"); 305 exit(1); 306 307 #endif 308 309 } else if (S_ISREG(st.st_mode)) { 310 // a regular file: fine 311 noPartition = true; 312 } else if (S_ISCHR(st.st_mode)) { 313 // character special: a device or partition under BeOS 314 // or under FreeBSD 315 #if !defined(__BEOS__) && !defined(HAIKU_HOST_PLATFORM_FREEBSD) 316 317 fprintf(stderr, "Error: Character special devices not " 318 "supported on this platform.\n"); 319 exit(1); 320 321 #endif 322 323 #ifdef HAIKU_HOST_PLATFORM_FREEBSD 324 325 // chop off the trailing number 326 int fileNameLen = strlen(fileName); 327 int baseNameLen = -1; 328 for (int k = fileNameLen - 1; k >= 0; k--) { 329 if (!isdigit(fileName[k])) { 330 baseNameLen = k + 1; 331 break; 332 } 333 } 334 335 // Remove de 's' from 'ad2s2' slice device (partition for DOS 336 // users) to get 'ad2' base device 337 baseNameLen--; 338 339 if (baseNameLen < 0) { 340 // only digits? 341 fprintf(stderr, "Error: Failed to get base device name.\n"); 342 exit(1); 343 } 344 345 if (baseNameLen < fileNameLen) { 346 // get base device name and partition index 347 char baseDeviceName[B_PATH_NAME_LENGTH]; 348 int partitionIndex = atoi(fileName + baseNameLen + 1); 349 // Don't forget the 's' of slice :) 350 memcpy(baseDeviceName, fileName, baseNameLen); 351 baseDeviceName[baseNameLen] = '\0'; 352 353 // open base device 354 int baseFD = open(baseDeviceName, O_RDONLY); 355 if (baseFD < 0) { 356 fprintf(stderr, "Error: Failed to open \"%s\": %s\n", 357 baseDeviceName, strerror(errno)); 358 exit(1); 359 } 360 361 // get device size 362 int64 deviceSize; 363 if (ioctl(baseFD, DIOCGMEDIASIZE, &deviceSize) == -1) { 364 fprintf(stderr, "Error: Failed to get device geometry " 365 "for \"%s\": %s\n", baseDeviceName, 366 strerror(errno)); 367 exit(1); 368 } 369 370 // parse the partition map 371 PartitionMapParser parser(baseFD, 0, deviceSize); 372 PartitionMap map; 373 error = parser.Parse(NULL, &map); 374 if (error != B_OK) { 375 fprintf(stderr, "Error: Parsing partition table on " 376 "device \"%s\" failed: %s\n", baseDeviceName, 377 strerror(error)); 378 exit(1); 379 } 380 381 close(baseFD); 382 383 // check the partition we are supposed to write at 384 Partition *partition = map.PartitionAt(partitionIndex - 1); 385 if (!partition || partition->IsEmpty()) { 386 fprintf(stderr, "Error: Invalid partition index %d.\n", 387 partitionIndex); 388 exit(1); 389 } 390 391 if (partition->IsExtended()) { 392 fprintf(stderr, "Error: Partition %d is an extended " 393 "partition.\n", partitionIndex); 394 exit(1); 395 } 396 397 partitionOffset = partition->Offset(); 398 399 } else { 400 // The given device is the base device. We'll write at 401 // offset 0. 402 } 403 404 #endif // HAIKU_HOST_PLATFORM_FREEBSD 405 406 } else if (S_ISBLK(st.st_mode)) { 407 // block device: a device or partition under Linux 408 #ifdef HAIKU_HOST_PLATFORM_LINUX 409 410 // chop off the trailing number 411 int fileNameLen = strlen(fileName); 412 int baseNameLen = -1; 413 for (int k = fileNameLen - 1; k >= 0; k--) { 414 if (!isdigit(fileName[k])) { 415 baseNameLen = k + 1; 416 break; 417 } 418 } 419 420 if (baseNameLen < 0) { 421 // only digits? 422 fprintf(stderr, "Error: Failed to get base device name.\n"); 423 exit(1); 424 } 425 426 if (baseNameLen < fileNameLen) { 427 // get base device name and partition index 428 char baseDeviceName[B_PATH_NAME_LENGTH]; 429 int partitionIndex = atoi(fileName + baseNameLen); 430 memcpy(baseDeviceName, fileName, baseNameLen); 431 baseDeviceName[baseNameLen] = '\0'; 432 433 // open base device 434 int baseFD = open(baseDeviceName, O_RDONLY); 435 if (baseFD < 0) { 436 fprintf(stderr, "Error: Failed to open \"%s\": %s\n", 437 baseDeviceName, strerror(errno)); 438 exit(1); 439 } 440 441 // get device geometry 442 hd_geometry geometry; 443 if (ioctl(baseFD, HDIO_GETGEO, &geometry) < 0) { 444 fprintf(stderr, "Error: Failed to get device geometry " 445 "for \"%s\": %s\n", baseDeviceName, 446 strerror(errno)); 447 exit(1); 448 } 449 int64 deviceSize = (int64)geometry.heads * geometry.sectors 450 * geometry.cylinders * 512; 451 452 // parse the partition map 453 PartitionMapParser parser(baseFD, 0, deviceSize); 454 PartitionMap map; 455 error = parser.Parse(NULL, &map); 456 if (error != B_OK) { 457 fprintf(stderr, "Error: Parsing partition table on " 458 "device \"%s\" failed: %s\n", baseDeviceName, 459 strerror(error)); 460 exit(1); 461 } 462 463 close(baseFD); 464 465 // check the partition we are supposed to write at 466 Partition *partition = map.PartitionAt(partitionIndex - 1); 467 if (!partition || partition->IsEmpty()) { 468 fprintf(stderr, "Error: Invalid partition index %d.\n", 469 partitionIndex); 470 exit(1); 471 } 472 473 if (partition->IsExtended()) { 474 fprintf(stderr, "Error: Partition %d is an extended " 475 "partition.\n", partitionIndex); 476 exit(1); 477 } 478 479 partitionOffset = partition->Offset(); 480 481 } else { 482 // The given device is the base device. We'll write at 483 // offset 0. 484 } 485 486 #else // !HAIKU_HOST_PLATFORM_LINUX 487 488 // partitions are block devices under Haiku, but not under BeOS 489 #ifndef __HAIKU__ 490 fprintf(stderr, "Error: Block devices not supported on this " 491 "platform!\n"); 492 exit(1); 493 #endif // __HAIKU__ 494 495 #endif 496 } else { 497 fprintf(stderr, "Error: File type of \"%s\" is not supported.\n", 498 fileName); 499 exit(1); 500 } 501 502 // open the file 503 int fd = open(fileName, O_RDWR); 504 if (fd < 0) { 505 fprintf(stderr, "Error: Failed to open \"%s\": %s\n", fileName, 506 strerror(errno)); 507 exit(1); 508 } 509 510 #ifdef __BEOS__ 511 512 // get a partition info 513 if (!noPartition 514 && strlen(fileName) >= 3 515 && strncmp("raw", fileName + strlen(fileName) - 3, 3)) { 516 partition_info partitionInfo; 517 if (ioctl(fd, B_GET_PARTITION_INFO, &partitionInfo, 518 sizeof(partitionInfo)) == 0) { 519 partitionOffset = partitionInfo.offset; 520 } else { 521 fprintf(stderr, "Error: Failed to get partition info: %s\n", 522 strerror(errno)); 523 exit(1); 524 } 525 } 526 527 #endif // __BEOS__ 528 529 // adjust the partition offset in the boot code data 530 // hard coded sector size: 512 bytes 531 *(uint32*)(bootCodeData + kPartitionOffsetOffset) 532 = B_HOST_TO_LENDIAN_INT32((uint32)(partitionOffset / 512)); 533 534 // write the boot code 535 printf("Writing boot code to \"%s\" (partition offset: %lld bytes) " 536 "...\n", fileName, partitionOffset); 537 538 write_boot_code_part(fileName, fd, startOffset, bootCodeData, 0, 539 kFirstBootCodePartSize, dryRun); 540 write_boot_code_part(fileName, fd, startOffset, bootCodeData, 541 kSecondBootcodePartOffset, kSecondBootcodePartSize, 542 dryRun); 543 544 close(fd); 545 } 546 547 return 0; 548 } 549