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