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