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