xref: /haiku/src/bin/makebootable/platform/bios_ia32/makebootable.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 /*
2  * Copyright 2005, 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 static const char *kCommandName = "makebootable";
23 
24 static const int kBootCodeSize				= 1024;
25 static const int kFirstBootCodePartSize		= 512;
26 static const int kSecondBootcodePartOffset	= 676;
27 static const int kSecondBootcodePartSize	= kBootCodeSize
28 												- kSecondBootcodePartOffset;
29 static const int kPartitionOffsetOffset		= 506;
30 
31 static int kArgc;
32 static const char *const *kArgv;
33 
34 // usage
35 const char *kUsage =
36 "Usage: %s [ options ] <file> ...\n"
37 "\n"
38 "Makes the specified BFS partitions/devices bootable by writing boot code\n"
39 "into the first two sectors. It doesn't mark the partition(s) active.\n"
40 "\n"
41 "If a given <file> refers to a directory, the partition/device on which the\n"
42 "directory resides will be made bootable. If it refers to a regular file,\n"
43 "the file is considered a disk image and the boot code will be written to\n"
44 "it.\n"
45 "\n"
46 "Options:\n"
47 "  -h, --help    - Print this help text and exit.\n"
48 "\n"
49 "[compatibility]\n"
50 "  -alert        - Compatibility option. Ignored.\n"
51 "  -full         - Compatibility option. Ignored.\n"
52 "  -safe         - Compatibility option. Fail when specified.\n"
53 ;
54 
55 // print_usage
56 static void
57 print_usage(bool error)
58 {
59 	// get command name
60 	const char *commandName = NULL;
61 	if (kArgc > 0) {
62 		if (const char *lastSlash = strchr(kArgv[0], '/'))
63 			commandName = lastSlash + 1;
64 		else
65 			commandName = kArgv[0];
66 	}
67 
68 	if (!commandName || strlen(commandName) == 0)
69 		commandName = kCommandName;
70 
71 	// print usage
72 	fprintf((error ? stderr : stdout), kUsage, commandName, commandName,
73 		commandName);
74 }
75 
76 // print_usage_and_exit
77 static void
78 print_usage_and_exit(bool error)
79 {
80 	print_usage(error);
81 	exit(error ? 1 : 0);
82 }
83 
84 // write_boot_code_part
85 static void
86 write_boot_code_part(const char *fileName, int fd, const uint8 *bootCodeData,
87 	int offset, int size)
88 {
89 printf("writing %d bytes at offset %d\n", size, offset);
90 	ssize_t bytesWritten = write_pos(fd, offset, bootCodeData + offset, size);
91 	if (bytesWritten != size) {
92 		fprintf(stderr, "Error: Failed to write to \"%s\": %s\n", fileName,
93 			strerror(bytesWritten < 0 ? errno : B_ERROR));
94 	}
95 }
96 
97 // main
98 int
99 main(int argc, const char *const *argv)
100 {
101 	kArgc = argc;
102 	kArgv = argv;
103 
104 	if (argc < 2)
105 		print_usage_and_exit(true);
106 
107 	// parameters
108 	const char **files = new const char*[argc];
109 	int fileCount = 0;
110 
111 	// parse arguments
112 	for (int argi = 1; argi < argc;) {
113 		const char *arg = argv[argi++];
114 
115 		if (arg[0] == '-') {
116 			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
117 				print_usage_and_exit(false);
118 			} else if (strcmp(arg, "-alert") == 0) {
119 				// ignore
120 			} else if (strcmp(arg, "-full") == 0) {
121 				// ignore
122 			} else if (strcmp(arg, "-safe") == 0) {
123 				fprintf(stderr, "Error: Sorry, BeOS R3 isnt't supported!\n");
124 				exit(1);
125 			} else {
126 				print_usage_and_exit(true);
127 			}
128 
129 		} else {
130 			files[fileCount++] = arg;
131 		}
132 	}
133 
134 	// we need at least one file
135 	if (fileCount == 0)
136 		print_usage_and_exit(true);
137 
138 	// open our executable
139 	BFile resourcesFile;
140 	status_t error = resourcesFile.SetTo(argv[0], B_READ_ONLY);
141 	if (error != B_OK) {
142 		fprintf(stderr, "Error: Failed to open my resources: %s\n",
143 			strerror(error));
144 		exit(1);
145 	}
146 
147 	// open our resources
148 	BResources resources;
149 	error = resources.SetTo(&resourcesFile);
150 	if (error != B_OK) {
151 		fprintf(stderr, "Error: Failed to read my resources: %s\n",
152 			strerror(error));
153 		exit(1);
154 	}
155 
156 	// read the boot block from the resources
157 	size_t resourceSize;
158 	const void *resourceData = resources.LoadResource(B_RAW_TYPE, 666,
159 		&resourceSize);
160 	if (!resourceData) {
161 		fprintf(stderr,
162 			"Error: Failed to read the boot block from my resources!\n");
163 		exit(1);
164 	}
165 
166 	if (resourceSize != (size_t)kBootCodeSize) {
167 		fprintf(stderr,
168 			"Error: Something is fishy with my resources! The boot code "
169 			"doesn't have the correct size\n");
170 		exit(1);
171 	}
172 
173 	// clone the boot code data, so that we can modify it
174 	uint8 *bootCodeData = new uint8[kBootCodeSize];
175 	memcpy(bootCodeData, resourceData, kBootCodeSize);
176 
177 	// iterate through the files and make them bootable
178 	for (int i = 0; i < fileCount; i++) {
179 		const char *fileName = files[i];
180 		BEntry entry;
181 		error = entry.SetTo(fileName, true);
182 		if (error != B_OK) {
183 			fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
184 				fileName, strerror(error));
185 			exit(1);
186 		}
187 
188 		// get stat to check the type of the file
189 		struct stat st;
190 		error = entry.GetStat(&st);
191 		if (error != B_OK) {
192 			fprintf(stderr, "Error: Failed to stat \"%s\": %s\n",
193 				fileName, strerror(error));
194 			exit(1);
195 		}
196 
197 		bool noPartition = false;
198 		fs_info info;	// needs to be here (we use the device name later)
199 		if (S_ISDIR(st.st_mode)) {
200 			#ifdef __BEOS__
201 
202 			// a directory: get the device
203 			error = fs_stat_dev(st.st_dev, &info);
204 			if (error != B_OK) {
205 				fprintf(stderr, "Error: Failed to determine device for "
206 					"\"%s\": %s\n", fileName, strerror(error));
207 				exit(1);
208 			}
209 
210 			fileName = info.device_name;
211 
212 			#else
213 
214 			(void)info;
215 			fprintf(stderr, "Error: Only image files are supported on this "
216 				"platform!\n");
217 			exit(1);
218 
219 			#endif
220 
221 		} else if (S_ISREG(st.st_mode)) {
222 			// a regular file: fine
223 			noPartition = true;
224 		} else if (S_ISCHR(st.st_mode)) {
225 			// a device or partition
226 			#ifndef __BEOS__
227 
228 			fprintf(stderr, "Error: Only image files are supported on this "
229 				"platform!\n");
230 			exit(1);
231 
232 			#endif
233 		} else {
234 			fprintf(stderr, "Error: File type of \"%s\" is not unsupported\n",
235 				fileName);
236 			exit(1);
237 		}
238 
239 		// open the file
240 		int fd = open(fileName, O_RDWR);
241 		if (fd < 0) {
242 			fprintf(stderr, "Error: Failed to open \"%s\": %s\n", fileName,
243 				strerror(errno));
244 			exit(1);
245 		}
246 
247 		// get a partition info
248 		int64 partitionOffset = 0;
249 
250 		#ifdef __BEOS__
251 
252 		partition_info partitionInfo;
253 		if (ioctl(fd, B_GET_PARTITION_INFO, &partitionInfo) == 0) {
254 			// hard coded sector size: 512 bytes
255 			partitionOffset = partitionInfo.offset / 512;
256 		}
257 
258 		#endif	// __BEOS__
259 
260 		//  adjust the partition offset in the boot code data
261 		*(uint32*)(bootCodeData + kPartitionOffsetOffset)
262 			= B_HOST_TO_LENDIAN_INT32((uint32)partitionOffset);
263 
264 		// write the boot code
265 		printf("Writing boot code to \"%s\" ...\n", fileName);
266 
267 		write_boot_code_part(fileName, fd, bootCodeData, 0,
268 			kFirstBootCodePartSize);
269 		write_boot_code_part(fileName, fd, bootCodeData,
270 			kSecondBootcodePartOffset, kSecondBootcodePartSize);
271 
272 		close(fd);
273 	}
274 
275 	return 0;
276 }
277