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