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