xref: /haiku/src/bin/makebootable/platform/bios_ia32/makebootable.cpp (revision 24159a0c7d6d6dcba9f2a0c1a7c08d2c8167f21b)
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