1193323Sed/*
2193323Sed * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3193323Sed * Distributed under the terms of the MIT License.
4193323Sed */
5193323Sed
6193323Sed
7193323Sed#include <errno.h>
8193323Sed#include <fcntl.h>
9193323Sed#include <stdio.h>
10193323Sed#include <stdlib.h>
11193323Sed#include <string.h>
12193323Sed#include <unistd.h>
13193323Sed#include <sys/stat.h>
14193323Sed
15193323Sed#include <ByteOrder.h>
16224145Sdim#include <Entry.h>
17224145Sdim#include <File.h>
18224145Sdim#include <fs_info.h>
19224145Sdim#include <Resources.h>
20224145Sdim#include <TypeConstants.h>
21224145Sdim
22224145Sdim// Linux and FreeBSD support
23193323Sed#ifdef HAIKU_HOST_PLATFORM_LINUX
24193323Sed#	include <ctype.h>
25210299Sed#	include <linux/fs.h>
26210299Sed#	include <linux/hdreg.h>
27193323Sed#	include <sys/ioctl.h>
28224145Sdim
29224145Sdim#	define USE_PARTITION_MAP 1
30224145Sdim#elif HAIKU_HOST_PLATFORM_FREEBSD
31224145Sdim#	include <ctype.h>
32224145Sdim#	include <sys/disklabel.h>
33224145Sdim#	include <sys/disk.h>
34224145Sdim#	include <sys/ioctl.h>
35224145Sdim
36224145Sdim#	define USE_PARTITION_MAP 1
37224145Sdim#elif HAIKU_HOST_PLATFORM_DARWIN
38224145Sdim#	include <ctype.h>
39224145Sdim#	include <sys/disk.h>
40224145Sdim#	include <sys/ioctl.h>
41224145Sdim
42224145Sdim#	define USE_PARTITION_MAP 1
43224145Sdim#endif
44224145Sdim
45224145Sdim#ifdef HAIKU_TARGET_PLATFORM_HAIKU
46224145Sdim#	include <image.h>
47224145Sdim
48193323Sed#	include <DiskDevice.h>
49193323Sed#	include <DiskDeviceRoster.h>
50224145Sdim#	include <Drivers.h>
51224145Sdim#	include <Partition.h>
52224145Sdim#	include <Path.h>
53224145Sdim
54224145Sdim#	include "bfs_control.h"
55224145Sdim#endif
56224145Sdim
57224145Sdim#if USE_PARTITION_MAP
58224145Sdim#	include "guid.h"
59224145Sdim#	include "gpt_known_guids.h"
60224145Sdim#	include "Header.h"
61224145Sdim#	include "PartitionMap.h"
62224145Sdim#	include "PartitionMapParser.h"
63224145Sdim#endif
64224145Sdim
65224145Sdim
66224145Sdimstatic const char *kCommandName = "makebootable";
67224145Sdim
68224145Sdimstatic const int kBootCodeSize				= 1024;
69224145Sdimstatic const int kFirstBootCodePartSize		= 512;
70224145Sdimstatic const int kSecondBootCodePartOffset	= 676;
71224145Sdimstatic const int kSecondBootCodePartSize	= kBootCodeSize
72224145Sdim												- kSecondBootCodePartOffset;
73224145Sdimstatic const int kPartitionOffsetOffset		= 506;
74224145Sdim
75224145Sdimstatic int kArgc;
76224145Sdimstatic const char *const *kArgv;
77224145Sdim
78224145Sdim// usage
79224145Sdimconst char *kUsage =
80224145Sdim"Usage: %s [ options ] <file> ...\n"
81224145Sdim"\n"
82224145Sdim"Makes the specified BFS partitions/devices bootable by writing boot code\n"
83224145Sdim"into the first two sectors. It doesn't mark the partition(s) active.\n"
84224145Sdim"\n"
85224145Sdim"If a given <file> refers to a directory, the partition/device on which the\n"
86224145Sdim"directory resides will be made bootable. If it refers to a regular file,\n"
87224145Sdim"the file is considered a disk image and the boot code will be written to\n"
88224145Sdim"it.\n"
89224145Sdim"\n"
90224145Sdim"Options:\n"
91193323Sed"  -h, --help    - Print this help text and exit.\n"
92193323Sed"  --dry-run     - Do everything but actually writing the boot block to disk.\n"
93224145Sdim;
94210299Sed
95210299Sed
96224145Sdim// print_usage
97210299Sedstatic void
98210299Sedprint_usage(bool error)
99224145Sdim{
100224145Sdim	// get command name
101224145Sdim	const char *commandName = NULL;
102210299Sed	if (kArgc > 0) {
103210299Sed		if (const char *lastSlash = strchr(kArgv[0], '/'))
104210299Sed			commandName = lastSlash + 1;
105210299Sed		else
106210299Sed			commandName = kArgv[0];
107210299Sed	}
108210299Sed
109224145Sdim	if (!commandName || strlen(commandName) == 0)
110224145Sdim		commandName = kCommandName;
111210299Sed
112210299Sed	// print usage
113212904Sdim	fprintf((error ? stderr : stdout), kUsage, commandName, commandName,
114210299Sed		commandName);
115210299Sed}
116210299Sed
117210299Sed
118210299Sed// print_usage_and_exit
119210299Sedstatic void
120210299Sedprint_usage_and_exit(bool error)
121210299Sed{
122210299Sed	print_usage(error);
123210299Sed	exit(error ? 1 : 0);
124224145Sdim}
125210299Sed
126210299Sed
127210299Sed// read_boot_code_data
128210299Sedstatic uint8 *
129210299Sedread_boot_code_data(const char* programPath)
130210299Sed{
131210299Sed	// open our executable
132210299Sed	BFile executableFile;
133210299Sed	status_t error = executableFile.SetTo(programPath, B_READ_ONLY);
134210299Sed	if (error != B_OK) {
135210299Sed		fprintf(stderr, "Error: Failed to open my executable file (\"%s\": "
136210299Sed			"%s\n", programPath, strerror(error));
137210299Sed		exit(1);
138210299Sed	}
139210299Sed
140210299Sed	uint8 *bootCodeData = new uint8[kBootCodeSize];
141210299Sed
142210299Sed	// open our resources
143210299Sed	BResources resources;
144210299Sed	error = resources.SetTo(&executableFile);
145210299Sed	const void *resourceData = NULL;
146210299Sed	if (error == B_OK) {
147210299Sed		// read the boot block from the resources
148210299Sed		size_t resourceSize;
149210299Sed		resourceData = resources.LoadResource(B_RAW_TYPE, 666, &resourceSize);
150210299Sed
151210299Sed		if (resourceData && resourceSize != (size_t)kBootCodeSize) {
152210299Sed			resourceData = NULL;
153210299Sed			printf("Warning: Something is fishy with my resources! The boot "
154210299Sed				"code doesn't have the correct size. Trying the attribute "
155210299Sed				"instead ...\n");
156210299Sed		}
157210299Sed	}
158210299Sed
159210299Sed	if (resourceData) {
160210299Sed		// found boot data in the resources
161210299Sed		memcpy(bootCodeData, resourceData, kBootCodeSize);
162210299Sed	} else {
163210299Sed		// no boot data in the resources; try the attribute
164210299Sed		ssize_t bytesRead = executableFile.ReadAttr("BootCode", B_RAW_TYPE,
165210299Sed			0, bootCodeData, kBootCodeSize);
166210299Sed		if (bytesRead < 0) {
167210299Sed			fprintf(stderr, "Error: Failed to read boot code from resources "
168210299Sed				"or attribute.\n");
169210299Sed			exit(1);
170210299Sed		}
171210299Sed		if (bytesRead != kBootCodeSize) {
172210299Sed			fprintf(stderr, "Error: Failed to read boot code from resources, "
173210299Sed				"and the boot code in the attribute has the wrong size!\n");
174210299Sed			exit(1);
175210299Sed		}
176210299Sed	}
177210299Sed
178210299Sed	return bootCodeData;
179210299Sed}
180210299Sed
181210299Sed
182210299Sed// write_boot_code_part
183210299Sedstatic void
184210299Sedwrite_boot_code_part(const char *fileName, int fd, off_t imageOffset,
185210299Sed	const uint8 *bootCodeData, int offset, int size, bool dryRun)
186210299Sed{
187210299Sed	if (!dryRun) {
188210299Sed		ssize_t bytesWritten = write_pos(fd, imageOffset + offset,
189210299Sed			bootCodeData + offset, size);
190210299Sed		if (bytesWritten != size) {
191210299Sed			fprintf(stderr, "Error: Failed to write to \"%s\": %s\n", fileName,
192210299Sed				strerror(bytesWritten < 0 ? errno : B_ERROR));
193210299Sed		}
194210299Sed	}
195210299Sed}
196210299Sed
197210299Sed
198210299Sed#ifdef HAIKU_TARGET_PLATFORM_HAIKU
199210299Sed
200210299Sedstatic status_t
201210299Sedfind_own_image(image_info *info)
202210299Sed{
203210299Sed	int32 cookie = 0;
204210299Sed	while (get_next_image_info(B_CURRENT_TEAM, &cookie, info) == B_OK) {
205210299Sed		if (((addr_t)info->text <= (addr_t)find_own_image
206210299Sed			&& (addr_t)info->text + info->text_size
207210299Sed				> (addr_t)find_own_image)) {
208210299Sed			return B_OK;
209210299Sed		}
210210299Sed	}
211210299Sed
212210299Sed	return B_NAME_NOT_FOUND;
213210299Sed}
214224145Sdim
215210299Sed#endif
216210299Sed
217210299Sed
218210299Sed#if USE_PARTITION_MAP
219210299Sed
220210299Sedstatic void
221210299Seddump_partition_map(const PartitionMap& map)
222210299Sed{
223210299Sed	fprintf(stderr, "partitions:\n");
224210299Sed	int32 count = map.CountPartitions();
225210299Sed	for (int i = 0; i < count; i++) {
226210299Sed		const Partition* partition = map.PartitionAt(i);
227210299Sed		fprintf(stderr, "%2d: ", i);
228210299Sed		if (partition == NULL) {
229210299Sed			fprintf(stderr, "<null>\n");
230210299Sed			continue;
231210299Sed		}
232210299Sed
233210299Sed		if (partition->IsEmpty()) {
234210299Sed			fprintf(stderr, "<empty>\n");
235210299Sed			continue;
236210299Sed		}
237210299Sed
238210299Sed		fprintf(stderr, "offset: %16" B_PRIdOFF ", size: %16" B_PRIdOFF
239210299Sed			", type: %x%s\n", partition->Offset(), partition->Size(),
240210299Sed			partition->Type(), partition->IsExtended() ? " (extended)" : "");
241210299Sed	}
242210299Sed}
243224145Sdim
244210299Sed
245210299Sedstatic void
246210299Sedget_partition_offset(int deviceFD, off_t deviceStart, off_t deviceSize,
247224145Sdim		int blockSize, int partitionIndex, char *deviceName,
248224145Sdim		int64 &_partitionOffset)
249224145Sdim{
250224145Sdim	PartitionMapParser parser(deviceFD, deviceStart, deviceSize, blockSize);
251224145Sdim	PartitionMap map;
252224145Sdim	status_t error = parser.Parse(NULL, &map);
253224145Sdim	if (error == B_OK) {
254224145Sdim		Partition *partition = map.PartitionAt(partitionIndex - 1);
255224145Sdim		if (!partition || partition->IsEmpty()) {
256224145Sdim			fprintf(stderr, "Error: Invalid partition index %d.\n",
257224145Sdim				partitionIndex);
258224145Sdim			dump_partition_map(map);
259224145Sdim			exit(1);
260224145Sdim		}
261224145Sdim
262224145Sdim		if (partition->IsExtended()) {
263224145Sdim			fprintf(stderr, "Error: Partition %d is an extended "
264224145Sdim				"partition.\n", partitionIndex);
265224145Sdim			dump_partition_map(map);
266224145Sdim			exit(1);
267224145Sdim		}
268224145Sdim
269224145Sdim		_partitionOffset = partition->Offset();
270224145Sdim	} else {
271224145Sdim		// try again using GPT instead
272224145Sdim		EFI::Header gptHeader(deviceFD, deviceSize, blockSize);
273224145Sdim		error = gptHeader.InitCheck();
274224145Sdim		if (error == B_OK && partitionIndex < gptHeader.EntryCount()) {
275224145Sdim			gpt_partition_entry partition = gptHeader.EntryAt(partitionIndex - 1);
276224145Sdim
277224145Sdim			static_guid bfs_uuid = {0x42465331, 0x3BA3, 0x10F1,
278224145Sdim				0x802A4861696B7521LL};
279224145Sdim
280224145Sdim			if (!(bfs_uuid == partition.partition_type)) {
281224145Sdim				fprintf(stderr, "Error: Partition %d does not have the "
282224145Sdim					"BFS UUID.\n", partitionIndex);
283224145Sdim				exit(1);
284224145Sdim			}
285224145Sdim
286224145Sdim			_partitionOffset = partition.StartBlock() * blockSize;
287224145Sdim		} else {
288224145Sdim			fprintf(stderr, "Error: Parsing partition table on device "
289224145Sdim				"\"%s\" failed: %s\n", deviceName, strerror(error));
290224145Sdim			exit(1);
291224145Sdim		}
292224145Sdim	}
293224145Sdim
294224145Sdim	close(deviceFD);
295224145Sdim}
296224145Sdim
297224145Sdim#endif
298224145Sdim
299224145Sdim
300224145Sdim// main
301224145Sdimint
302224145Sdimmain(int argc, const char *const *argv)
303224145Sdim{
304224145Sdim	kArgc = argc;
305224145Sdim	kArgv = argv;
306224145Sdim
307224145Sdim	if (argc < 2)
308224145Sdim		print_usage_and_exit(true);
309224145Sdim
310224145Sdim	// parameters
311224145Sdim	const char **files = new const char*[argc];
312224145Sdim	int fileCount = 0;
313224145Sdim	bool dryRun = false;
314224145Sdim	off_t startOffset = 0;
315224145Sdim
316224145Sdim	// parse arguments
317224145Sdim	for (int argi = 1; argi < argc;) {
318224145Sdim		const char *arg = argv[argi++];
319224145Sdim
320224145Sdim		if (arg[0] == '-') {
321224145Sdim			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
322224145Sdim				print_usage_and_exit(false);
323224145Sdim			} else if (strcmp(arg, "--dry-run") == 0) {
324224145Sdim				dryRun = true;
325224145Sdim			} else if (strcmp(arg, "--start-offset") == 0) {
326224145Sdim				if (argi >= argc)
327224145Sdim					print_usage_and_exit(true);
328224145Sdim				startOffset = strtoll(argv[argi++], NULL, 0);
329224145Sdim			} else {
330224145Sdim				print_usage_and_exit(true);
331224145Sdim			}
332224145Sdim
333224145Sdim		} else {
334224145Sdim			files[fileCount++] = arg;
335224145Sdim		}
336224145Sdim	}
337224145Sdim
338224145Sdim	// we need at least one file
339224145Sdim	if (fileCount == 0)
340224145Sdim		print_usage_and_exit(true);
341224145Sdim
342224145Sdim	// read the boot code
343224145Sdim	uint8 *bootCodeData = NULL;
344224145Sdim#ifndef HAIKU_TARGET_PLATFORM_HAIKU
345224145Sdim	bootCodeData = read_boot_code_data(argv[0]);
346224145Sdim#else
347224145Sdim	image_info info;
348224145Sdim	if (find_own_image(&info) == B_OK)
349224145Sdim		bootCodeData = read_boot_code_data(info.name);
350224145Sdim#endif
351224145Sdim	if (!bootCodeData) {
352224145Sdim		fprintf(stderr, "Error: Failed to read \n");
353224145Sdim		exit(1);
354224145Sdim	}
355224145Sdim
356224145Sdim	// iterate through the files and make them bootable
357224145Sdim	status_t error;
358224145Sdim	for (int i = 0; i < fileCount; i++) {
359224145Sdim		const char *fileName = files[i];
360224145Sdim		BEntry entry;
361224145Sdim		error = entry.SetTo(fileName, true);
362224145Sdim		if (error != B_OK) {
363224145Sdim			fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
364224145Sdim				fileName, strerror(error));
365224145Sdim			exit(1);
366224145Sdim		}
367224145Sdim
368224145Sdim		// get stat to check the type of the file
369224145Sdim		struct stat st;
370224145Sdim		error = entry.GetStat(&st);
371224145Sdim		if (error != B_OK) {
372224145Sdim			fprintf(stderr, "Error: Failed to stat \"%s\": %s\n",
373224145Sdim				fileName, strerror(error));
374224145Sdim			exit(1);
375224145Sdim		}
376224145Sdim
377224145Sdim		bool noPartition = false;
378224145Sdim		int64 partitionOffset = 0;
379224145Sdim		fs_info info;	// needs to be here (we use the device name later)
380224145Sdim		if (S_ISDIR(st.st_mode)) {
381224145Sdim			#ifdef HAIKU_TARGET_PLATFORM_HAIKU
382224145Sdim
383224145Sdim				// a directory: get the device
384224145Sdim				error = fs_stat_dev(st.st_dev, &info);
385224145Sdim				if (error != B_OK) {
386224145Sdim					fprintf(stderr, "Error: Failed to determine device for "
387224145Sdim						"\"%s\": %s\n", fileName, strerror(error));
388224145Sdim					exit(1);
389224145Sdim				}
390224145Sdim
391224145Sdim				fileName = info.device_name;
392224145Sdim
393224145Sdim			#else
394224145Sdim
395224145Sdim				(void)info;
396224145Sdim				fprintf(stderr, "Error: Specifying directories not supported "
397224145Sdim					"on this platform!\n");
398224145Sdim				exit(1);
399224145Sdim
400224145Sdim			#endif
401224145Sdim
402224145Sdim		} else if (S_ISREG(st.st_mode)) {
403224145Sdim			// a regular file: fine
404224145Sdim			noPartition = true;
405224145Sdim		} else if (S_ISCHR(st.st_mode)) {
406224145Sdim			// character special: a device or partition under BeOS
407224145Sdim			// or under FreeBSD
408224145Sdim			#if !defined(HAIKU_TARGET_PLATFORM_HAIKU) \
409224145Sdim				&& !defined(HAIKU_HOST_PLATFORM_FREEBSD)
410224145Sdim
411224145Sdim				fprintf(stderr, "Error: Character special devices not "
412224145Sdim					"supported on this platform.\n");
413224145Sdim				exit(1);
414224145Sdim
415224145Sdim			#endif
416224145Sdim
417224145Sdim			#ifdef HAIKU_HOST_PLATFORM_FREEBSD
418224145Sdim
419224145Sdim				// chop off the trailing number
420224145Sdim				int fileNameLen = strlen(fileName);
421224145Sdim				int baseNameLen = -1;
422224145Sdim				for (int k = fileNameLen - 1; k >= 0; k--) {
423224145Sdim					if (!isdigit(fileName[k])) {
424224145Sdim						baseNameLen = k + 1;
425224145Sdim						break;
426224145Sdim					}
427224145Sdim				}
428224145Sdim
429224145Sdim				// Remove de 's' from 'ad2s2' slice device (partition for DOS
430224145Sdim				// users) to get 'ad2' base device
431224145Sdim				baseNameLen--;
432224145Sdim
433224145Sdim				if (baseNameLen < 0) {
434224145Sdim					// only digits?
435224145Sdim					fprintf(stderr, "Error: Failed to get base device name.\n");
436224145Sdim					exit(1);
437224145Sdim				}
438224145Sdim
439224145Sdim				if (baseNameLen < fileNameLen) {
440224145Sdim					// get base device name and partition index
441224145Sdim					char baseDeviceName[B_PATH_NAME_LENGTH];
442224145Sdim					int partitionIndex = atoi(fileName + baseNameLen + 1);
443224145Sdim						// Don't forget the 's' of slice :)
444224145Sdim					memcpy(baseDeviceName, fileName, baseNameLen);
445224145Sdim					baseDeviceName[baseNameLen] = '\0';
446224145Sdim
447224145Sdim					// open base device
448224145Sdim					int baseFD = open(baseDeviceName, O_RDONLY);
449224145Sdim					if (baseFD < 0) {
450224145Sdim						fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
451224145Sdim							baseDeviceName, strerror(errno));
452224145Sdim						exit(1);
453224145Sdim					}
454224145Sdim
455224145Sdim					// get device size
456224145Sdim					int64 deviceSize;
457224145Sdim					if (ioctl(baseFD, DIOCGMEDIASIZE, &deviceSize) == -1) {
458224145Sdim						fprintf(stderr, "Error: Failed to get device geometry "
459224145Sdim							"for \"%s\": %s\n", baseDeviceName,
460224145Sdim							strerror(errno));
461224145Sdim						exit(1);
462224145Sdim					}
463224145Sdim
464224145Sdim					// parse the partition map
465224145Sdim					// TODO: block size!
466224145Sdim					get_partition_offset(baseFD, 0, deviceSize, 512,
467224145Sdim						partitionIndex, baseDeviceName, partitionOffset);
468224145Sdim				} else {
469224145Sdim					// The given device is the base device. We'll write at
470224145Sdim					// offset 0.
471224145Sdim				}
472224145Sdim
473224145Sdim			#endif // HAIKU_HOST_PLATFORM_FREEBSD
474224145Sdim
475224145Sdim		} else if (S_ISBLK(st.st_mode)) {
476224145Sdim			// block device: a device or partition under Linux or Darwin
477224145Sdim			#ifdef HAIKU_HOST_PLATFORM_LINUX
478224145Sdim
479224145Sdim				// chop off the trailing number
480224145Sdim				int fileNameLen = strlen(fileName);
481224145Sdim				int baseNameLen = -1;
482224145Sdim				for (int k = fileNameLen - 1; k >= 0; k--) {
483224145Sdim					if (!isdigit(fileName[k])) {
484224145Sdim						baseNameLen = k + 1;
485224145Sdim						break;
486224145Sdim					}
487224145Sdim				}
488224145Sdim
489224145Sdim				if (baseNameLen < 0) {
490224145Sdim					// only digits?
491224145Sdim					fprintf(stderr, "Error: Failed to get base device name.\n");
492224145Sdim					exit(1);
493224145Sdim				}
494224145Sdim
495224145Sdim				if (baseNameLen < fileNameLen) {
496224145Sdim					// get base device name and partition index
497224145Sdim					char baseDeviceName[B_PATH_NAME_LENGTH];
498224145Sdim					int partitionIndex = atoi(fileName + baseNameLen);
499224145Sdim					memcpy(baseDeviceName, fileName, baseNameLen);
500224145Sdim					baseDeviceName[baseNameLen] = '\0';
501224145Sdim
502224145Sdim					// open base device
503224145Sdim					int baseFD = open(baseDeviceName, O_RDONLY);
504224145Sdim					if (baseFD < 0) {
505224145Sdim						fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
506224145Sdim							baseDeviceName, strerror(errno));
507224145Sdim						exit(1);
508224145Sdim					}
509224145Sdim
510224145Sdim					// get device size -- try BLKGETSIZE64, but, if it doesn't
511224145Sdim					// work, fall back to the obsolete HDIO_GETGEO
512224145Sdim					int64 deviceSize;
513224145Sdim					hd_geometry geometry;
514224145Sdim					if (ioctl(baseFD, BLKGETSIZE64, &deviceSize) == 0
515224145Sdim						&& deviceSize > 0) {
516224145Sdim						// looks good
517224145Sdim					} else if (ioctl(baseFD, HDIO_GETGEO, &geometry) == 0) {
518224145Sdim						deviceSize = (int64)geometry.heads * geometry.sectors
519224145Sdim							* geometry.cylinders * 512;
520224145Sdim					} else {
521224145Sdim						fprintf(stderr, "Error: Failed to get device geometry "
522224145Sdim							"for \"%s\": %s\n", baseDeviceName,
523224145Sdim							strerror(errno));
524224145Sdim						exit(1);
525224145Sdim					}
526224145Sdim
527224145Sdim					// parse the partition map
528224145Sdim					// TODO: block size!
529224145Sdim					get_partition_offset(baseFD, 0, deviceSize, 512,
530224145Sdim						partitionIndex, baseDeviceName, partitionOffset);
531224145Sdim				} else {
532224145Sdim					// The given device is the base device. We'll write at
533224145Sdim					// offset 0.
534224145Sdim				}
535224145Sdim
536224145Sdim			#elif defined(HAIKU_HOST_PLATFORM_DARWIN)
537224145Sdim				// chop off the trailing number
538224145Sdim				int fileNameLen = strlen(fileName);
539224145Sdim				int baseNameLen = fileNameLen - 2;
540224145Sdim
541224145Sdim				// get base device name and partition index
542224145Sdim				char baseDeviceName[B_PATH_NAME_LENGTH];
543224145Sdim				int partitionIndex = atoi(fileName + baseNameLen + 1);
544224145Sdim				memcpy(baseDeviceName, fileName, baseNameLen);
545224145Sdim				baseDeviceName[baseNameLen] = '\0';
546224145Sdim
547224145Sdim				// open base device
548224145Sdim				int baseFD = open(baseDeviceName, O_RDONLY);
549224145Sdim				if (baseFD < 0) {
550224145Sdim					fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
551224145Sdim							baseDeviceName, strerror(errno));
552224145Sdim					exit(1);
553224145Sdim				}
554224145Sdim
555224145Sdim				// get device size
556224145Sdim				int64 blockSize;
557224145Sdim				int64 blockCount;
558224145Sdim				int64 deviceSize;
559224145Sdim				if (ioctl(baseFD, DKIOCGETBLOCKSIZE, &blockSize) == -1) {
560224145Sdim					fprintf(stderr, "Error: Failed to get block size "
561224145Sdim							"for \"%s\": %s\n", baseDeviceName,
562224145Sdim							strerror(errno));
563224145Sdim					exit(1);
564224145Sdim				}
565224145Sdim				if (ioctl(baseFD, DKIOCGETBLOCKCOUNT, &blockCount) == -1) {
566224145Sdim					fprintf(stderr, "Error: Failed to get block count "
567224145Sdim							"for \"%s\": %s\n", baseDeviceName,
568224145Sdim							strerror(errno));
569224145Sdim					exit(1);
570224145Sdim				}
571224145Sdim
572224145Sdim				deviceSize = blockSize * blockCount;
573224145Sdim
574224145Sdim				// parse the partition map
575224145Sdim				get_partition_offset(baseFD, 0, deviceSize, 512,
576224145Sdim						partitionIndex, baseDeviceName, partitionOffset);
577224145Sdim			#else
578224145Sdim			// partitions are block devices under Haiku, but not under BeOS
579224145Sdim			#ifdef HAIKU_TARGET_PLATFORM_HAIKU
580224145Sdim				fprintf(stderr, "Error: Block devices not supported on this "
581224145Sdim					"platform!\n");
582224145Sdim				exit(1);
583224145Sdim			#endif	// HAIKU_TARGET_PLATFORM_HAIKU
584224145Sdim
585224145Sdim			#endif
586224145Sdim		} else {
587224145Sdim			fprintf(stderr, "Error: File type of \"%s\" is not supported.\n",
588224145Sdim				fileName);
589224145Sdim			exit(1);
590224145Sdim		}
591224145Sdim
592224145Sdim		// open the file
593224145Sdim		int fd = open(fileName, O_RDWR);
594224145Sdim		if (fd < 0) {
595224145Sdim			fprintf(stderr, "Error: Failed to open \"%s\": %s\n", fileName,
596224145Sdim				strerror(errno));
597224145Sdim			exit(1);
598224145Sdim		}
599224145Sdim
600224145Sdim		#ifdef HAIKU_TARGET_PLATFORM_HAIKU
601224145Sdim
602224145Sdim			// get a partition info
603224145Sdim			if (!noPartition
604224145Sdim				&& strlen(fileName) >= 3
605224145Sdim				&& strncmp("raw", fileName + strlen(fileName) - 3, 3)) {
606224145Sdim				partition_info partitionInfo;
607224145Sdim				if (ioctl(fd, B_GET_PARTITION_INFO, &partitionInfo,
608224145Sdim						sizeof(partitionInfo)) == 0) {
609224145Sdim					partitionOffset = partitionInfo.offset;
610224145Sdim				} else {
611224145Sdim					fprintf(stderr, "Error: Failed to get partition info: %s\n",
612224145Sdim						strerror(errno));
613224145Sdim					exit(1);
614224145Sdim				}
615224145Sdim			}
616224145Sdim
617224145Sdim		#endif	// HAIKU_TARGET_PLATFORM_HAIKU
618224145Sdim
619224145Sdim		// adjust the partition offset in the boot code data
620224145Sdim		// hard coded sector size: 512 bytes
621224145Sdim		*(uint32*)(bootCodeData + kPartitionOffsetOffset)
622224145Sdim			= B_HOST_TO_LENDIAN_INT32((uint32)(partitionOffset / 512));
623224145Sdim
624224145Sdim		// write the boot code
625224145Sdim		printf("Writing boot code to \"%s\" (partition offset: %" B_PRId64
626224145Sdim			" bytes, start offset = %" B_PRIdOFF ") "
627224145Sdim			"...\n", fileName, partitionOffset, startOffset);
628224145Sdim
629224145Sdim		write_boot_code_part(fileName, fd, startOffset, bootCodeData, 0,
630224145Sdim			kFirstBootCodePartSize, dryRun);
631224145Sdim		write_boot_code_part(fileName, fd, startOffset, bootCodeData,
632224145Sdim			kSecondBootCodePartOffset, kSecondBootCodePartSize,
633224145Sdim			dryRun);
634224145Sdim
635224145Sdim#ifdef HAIKU_TARGET_PLATFORM_HAIKU
636224145Sdim		// check if this partition is mounted
637224145Sdim		BDiskDeviceRoster roster;
638224145Sdim		BPartition* partition;
639224145Sdim		BDiskDevice device;
640224145Sdim		status_t status = roster.GetPartitionForPath(fileName, &device,
641224145Sdim			&partition);
642224145Sdim		if (status != B_OK) {
643224145Sdim			status = roster.GetFileDeviceForPath(fileName, &device);
644224145Sdim			if (status == B_OK)
645224145Sdim				partition = &device;
646224145Sdim		}
647224145Sdim		if (status == B_OK && partition->IsMounted() && !dryRun) {
648224145Sdim			// This partition is mounted, we need to tell BFS to update its
649224145Sdim			// boot block (we are using part of the same logical block).
650224145Sdim			BPath path;
651224145Sdim			status = partition->GetMountPoint(&path);
652224145Sdim			if (status == B_OK) {
653224145Sdim				update_boot_block update;
654224145Sdim				update.offset = kSecondBootCodePartOffset - 512;
655224145Sdim				update.data = bootCodeData + kSecondBootCodePartOffset;
656224145Sdim				update.length = kSecondBootCodePartSize;
657224145Sdim
658224145Sdim				int mountFD = open(path.Path(), O_RDONLY);
659224145Sdim				if (ioctl(mountFD, BFS_IOCTL_UPDATE_BOOT_BLOCK, &update,
660224145Sdim						sizeof(update_boot_block)) != 0) {
661224145Sdim					fprintf(stderr, "Could not update BFS boot block: %s\n",
662224145Sdim						strerror(errno));
663224145Sdim				}
664224145Sdim				close(mountFD);
665224145Sdim			} else {
666224145Sdim				fprintf(stderr, "Could not update BFS boot code while the "
667224145Sdim					"partition is mounted!\n");
668224145Sdim			}
669224145Sdim		}
670224145Sdim#endif	// HAIKU_TARGET_PLATFORM_HAIKU
671224145Sdim
672224145Sdim		close(fd);
673224145Sdim	}
674224145Sdim
675224145Sdim	delete[] files;
676224145Sdim
677224145Sdim	return 0;
678224145Sdim}
679224145Sdim