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