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