xref: /haiku/src/bin/makebootable/platform/bios_ia32/makebootable.cpp (revision b9a5b9a6ee494261f2882bfc0ee9fde92282bef6)
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, 512);
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