xref: /haiku/src/apps/bootmanager/LegacyBootMenu.cpp (revision 546208a53940a26c6379c48a7854ade1a8250fc5)
15675f44eSAxel Dörfler /*
25675f44eSAxel Dörfler  * Copyright 2008-2011, Haiku, Inc. All rights reserved.
35675f44eSAxel Dörfler  * Distributed under the terms of the MIT License.
45675f44eSAxel Dörfler  *
55675f44eSAxel Dörfler  * Authors:
649c044abSAxel Dörfler  *		Axel Dörfler, axeld@pinc-software.de
75675f44eSAxel Dörfler  *		Michael Pfeiffer <laplace@users.sourceforge.net>
85675f44eSAxel Dörfler  */
95675f44eSAxel Dörfler 
105675f44eSAxel Dörfler 
115675f44eSAxel Dörfler #include "LegacyBootMenu.h"
125675f44eSAxel Dörfler 
135675f44eSAxel Dörfler #include <new>
1449c044abSAxel Dörfler 
1549c044abSAxel Dörfler #include <errno.h>
165675f44eSAxel Dörfler #include <stdio.h>
175675f44eSAxel Dörfler 
185675f44eSAxel Dörfler #include <Catalog.h>
195675f44eSAxel Dörfler #include <DataIO.h>
205675f44eSAxel Dörfler #include <DiskDevice.h>
215675f44eSAxel Dörfler #include <DiskDeviceRoster.h>
225675f44eSAxel Dörfler #include <DiskDeviceVisitor.h>
2349c044abSAxel Dörfler #include <Drivers.h>
245675f44eSAxel Dörfler #include <File.h>
255675f44eSAxel Dörfler #include <Partition.h>
265675f44eSAxel Dörfler #include <Path.h>
275675f44eSAxel Dörfler #include <String.h>
285675f44eSAxel Dörfler #include <UTF8.h>
295675f44eSAxel Dörfler 
3049c044abSAxel Dörfler #include "BootDrive.h"
315675f44eSAxel Dörfler #include "BootLoader.h"
325675f44eSAxel Dörfler 
335675f44eSAxel Dörfler 
34d3dd01a5SAxel Dörfler /*
35d3dd01a5SAxel Dörfler 	Note: for testing, the best way is to create a small file image, and
36d3dd01a5SAxel Dörfler 	register it, for example via the "diskimage" tool (mountvolume might also
37d3dd01a5SAxel Dörfler 	work).
38d3dd01a5SAxel Dörfler 	You can then create partitions on it via DriveSetup, or simply copy an
39d3dd01a5SAxel Dörfler 	existing drive to it, for example via:
40d3dd01a5SAxel Dörfler 		$ dd if=/dev/disk/ata/0/master/raw of=test.image bs=1M count=10
41d3dd01a5SAxel Dörfler 
42d3dd01a5SAxel Dörfler 	It will automatically appear in BootManager once it is registered, and,
43d3dd01a5SAxel Dörfler 	depending on its parition layout, you can then install the boot menu on
44d3dd01a5SAxel Dörfler 	it, and directly test it via qemu.
45d3dd01a5SAxel Dörfler */
46d3dd01a5SAxel Dörfler 
47d3dd01a5SAxel Dörfler 
48*546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT
49*546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "LegacyBootMenu"
505675f44eSAxel Dörfler 
515675f44eSAxel Dörfler 
525675f44eSAxel Dörfler struct MasterBootRecord {
535675f44eSAxel Dörfler 	uint8 bootLoader[440];
545675f44eSAxel Dörfler 	uint8 diskSignature[4];
555675f44eSAxel Dörfler 	uint8 reserved[2];
565675f44eSAxel Dörfler 	uint8 partition[64];
575675f44eSAxel Dörfler 	uint8 signature[2];
585675f44eSAxel Dörfler };
595675f44eSAxel Dörfler 
605675f44eSAxel Dörfler 
615675f44eSAxel Dörfler class LittleEndianMallocIO : public BMallocIO {
625675f44eSAxel Dörfler public:
635675f44eSAxel Dörfler 			bool				WriteInt8(int8 value);
645675f44eSAxel Dörfler 			bool				WriteInt16(int16 value);
655675f44eSAxel Dörfler 			bool				WriteInt32(int32 value);
665675f44eSAxel Dörfler 			bool				WriteInt64(int64 value);
675675f44eSAxel Dörfler 			bool				WriteString(const char* value);
685675f44eSAxel Dörfler 			bool				Align(int16 alignment);
695675f44eSAxel Dörfler 			bool				Fill(int16 size, int8 fillByte);
705675f44eSAxel Dörfler };
715675f44eSAxel Dörfler 
725675f44eSAxel Dörfler 
7349c044abSAxel Dörfler class PartitionVisitor : public BDiskDeviceVisitor {
745675f44eSAxel Dörfler public:
7549c044abSAxel Dörfler 								PartitionVisitor();
765675f44eSAxel Dörfler 
775675f44eSAxel Dörfler 	virtual	bool				Visit(BDiskDevice* device);
785675f44eSAxel Dörfler 	virtual	bool				Visit(BPartition* partition, int32 level);
795675f44eSAxel Dörfler 
805675f44eSAxel Dörfler 			bool				HasPartitions() const;
815675f44eSAxel Dörfler 			off_t				FirstOffset() const;
825675f44eSAxel Dörfler 
835675f44eSAxel Dörfler private:
8449c044abSAxel Dörfler 			off_t				fFirstOffset;
8549c044abSAxel Dörfler };
8649c044abSAxel Dörfler 
8749c044abSAxel Dörfler 
8849c044abSAxel Dörfler class PartitionRecorder : public BDiskDeviceVisitor {
8949c044abSAxel Dörfler public:
9049c044abSAxel Dörfler 								PartitionRecorder(BMessage& settings,
9149c044abSAxel Dörfler 									int8 biosDrive);
9249c044abSAxel Dörfler 
9349c044abSAxel Dörfler 	virtual	bool				Visit(BDiskDevice* device);
9449c044abSAxel Dörfler 	virtual	bool				Visit(BPartition* partition, int32 level);
9549c044abSAxel Dörfler 
9649c044abSAxel Dörfler 			bool				FoundPartitions() const;
975675f44eSAxel Dörfler 
985675f44eSAxel Dörfler private:
9949c044abSAxel Dörfler 			BMessage&			fSettings;
10049c044abSAxel Dörfler 			int32				fUnnamedIndex;
10149c044abSAxel Dörfler 			int8				fBIOSDrive;
10249c044abSAxel Dörfler 			bool				fFound;
1035675f44eSAxel Dörfler };
1045675f44eSAxel Dörfler 
1055675f44eSAxel Dörfler 
1065675f44eSAxel Dörfler static const uint32 kBlockSize = 512;
1075675f44eSAxel Dörfler static const uint32 kNumberOfBootLoaderBlocks = 4;
1085675f44eSAxel Dörfler 	// The number of blocks required to store the
1095675f44eSAxel Dörfler 	// MBR including the Haiku boot loader.
1105675f44eSAxel Dörfler 
1115675f44eSAxel Dörfler static const uint32 kMBRSignature = 0xAA55;
1125675f44eSAxel Dörfler 
1135675f44eSAxel Dörfler static const int32 kMaxBootMenuItemLength = 70;
1145675f44eSAxel Dörfler 
1155675f44eSAxel Dörfler 
1165675f44eSAxel Dörfler bool
1175675f44eSAxel Dörfler LittleEndianMallocIO::WriteInt8(int8 value)
1185675f44eSAxel Dörfler {
1195675f44eSAxel Dörfler 	return Write(&value, sizeof(value)) == sizeof(value);
1205675f44eSAxel Dörfler }
1215675f44eSAxel Dörfler 
1225675f44eSAxel Dörfler 
1235675f44eSAxel Dörfler bool
1245675f44eSAxel Dörfler LittleEndianMallocIO::WriteInt16(int16 value)
1255675f44eSAxel Dörfler {
1265675f44eSAxel Dörfler 	return WriteInt8(value & 0xff)
1275675f44eSAxel Dörfler 		&& WriteInt8(value >> 8);
1285675f44eSAxel Dörfler }
1295675f44eSAxel Dörfler 
1305675f44eSAxel Dörfler 
1315675f44eSAxel Dörfler bool
1325675f44eSAxel Dörfler LittleEndianMallocIO::WriteInt32(int32 value)
1335675f44eSAxel Dörfler {
1345675f44eSAxel Dörfler 	return WriteInt8(value & 0xff)
1355675f44eSAxel Dörfler 		&& WriteInt8(value >> 8)
1365675f44eSAxel Dörfler 		&& WriteInt8(value >> 16)
1375675f44eSAxel Dörfler 		&& WriteInt8(value >> 24);
1385675f44eSAxel Dörfler }
1395675f44eSAxel Dörfler 
1405675f44eSAxel Dörfler 
1415675f44eSAxel Dörfler bool
1425675f44eSAxel Dörfler LittleEndianMallocIO::WriteInt64(int64 value)
1435675f44eSAxel Dörfler {
1445675f44eSAxel Dörfler 	return WriteInt32(value) && WriteInt32(value >> 32);
1455675f44eSAxel Dörfler }
1465675f44eSAxel Dörfler 
1475675f44eSAxel Dörfler 
1485675f44eSAxel Dörfler bool
1495675f44eSAxel Dörfler LittleEndianMallocIO::WriteString(const char* value)
1505675f44eSAxel Dörfler {
1515675f44eSAxel Dörfler 	int len = strlen(value) + 1;
1525675f44eSAxel Dörfler 	return WriteInt8(len)
1535675f44eSAxel Dörfler 		&& Write(value, len) == len;
1545675f44eSAxel Dörfler }
1555675f44eSAxel Dörfler 
1565675f44eSAxel Dörfler 
1575675f44eSAxel Dörfler bool
1585675f44eSAxel Dörfler LittleEndianMallocIO::Align(int16 alignment)
1595675f44eSAxel Dörfler {
1605675f44eSAxel Dörfler 	if ((Position() % alignment) == 0)
1615675f44eSAxel Dörfler 		return true;
1625675f44eSAxel Dörfler 	return Fill(alignment - (Position() % alignment), 0);
1635675f44eSAxel Dörfler }
1645675f44eSAxel Dörfler 
1655675f44eSAxel Dörfler 
1665675f44eSAxel Dörfler bool
1675675f44eSAxel Dörfler LittleEndianMallocIO::Fill(int16 size, int8 fillByte)
1685675f44eSAxel Dörfler {
1695675f44eSAxel Dörfler 	for (int i = 0; i < size; i ++) {
1705675f44eSAxel Dörfler 		if (!WriteInt8(fillByte))
1715675f44eSAxel Dörfler 			return false;
1725675f44eSAxel Dörfler 	}
1735675f44eSAxel Dörfler 	return true;
1745675f44eSAxel Dörfler }
1755675f44eSAxel Dörfler 
1765675f44eSAxel Dörfler 
1775675f44eSAxel Dörfler // #pragma mark -
1785675f44eSAxel Dörfler 
1795675f44eSAxel Dörfler 
18049c044abSAxel Dörfler PartitionVisitor::PartitionVisitor()
18149c044abSAxel Dörfler 	:
18249c044abSAxel Dörfler 	fFirstOffset(LONGLONG_MAX)
18349c044abSAxel Dörfler {
18449c044abSAxel Dörfler }
18549c044abSAxel Dörfler 
18649c044abSAxel Dörfler 
18749c044abSAxel Dörfler bool
18849c044abSAxel Dörfler PartitionVisitor::Visit(BDiskDevice* device)
18949c044abSAxel Dörfler {
19049c044abSAxel Dörfler 	return false;
19149c044abSAxel Dörfler }
19249c044abSAxel Dörfler 
19349c044abSAxel Dörfler 
19449c044abSAxel Dörfler bool
19549c044abSAxel Dörfler PartitionVisitor::Visit(BPartition* partition, int32 level)
19649c044abSAxel Dörfler {
19749c044abSAxel Dörfler 	if (partition->Offset() < fFirstOffset)
19849c044abSAxel Dörfler 		fFirstOffset = partition->Offset();
19949c044abSAxel Dörfler 
20049c044abSAxel Dörfler 	return false;
20149c044abSAxel Dörfler }
20249c044abSAxel Dörfler 
20349c044abSAxel Dörfler 
20449c044abSAxel Dörfler bool
20549c044abSAxel Dörfler PartitionVisitor::HasPartitions() const
20649c044abSAxel Dörfler {
20749c044abSAxel Dörfler 	return fFirstOffset != LONGLONG_MAX;
20849c044abSAxel Dörfler }
20949c044abSAxel Dörfler 
21049c044abSAxel Dörfler 
21149c044abSAxel Dörfler off_t
21249c044abSAxel Dörfler PartitionVisitor::FirstOffset() const
21349c044abSAxel Dörfler {
21449c044abSAxel Dörfler 	return fFirstOffset;
21549c044abSAxel Dörfler }
21649c044abSAxel Dörfler 
21749c044abSAxel Dörfler 
21849c044abSAxel Dörfler // #pragma mark -
21949c044abSAxel Dörfler 
22049c044abSAxel Dörfler 
22149c044abSAxel Dörfler PartitionRecorder::PartitionRecorder(BMessage& settings, int8 biosDrive)
2225675f44eSAxel Dörfler 	:
2235675f44eSAxel Dörfler 	fSettings(settings),
22449c044abSAxel Dörfler 	fUnnamedIndex(0),
22549c044abSAxel Dörfler 	fBIOSDrive(biosDrive),
22649c044abSAxel Dörfler 	fFound(false)
2275675f44eSAxel Dörfler {
2285675f44eSAxel Dörfler }
2295675f44eSAxel Dörfler 
2305675f44eSAxel Dörfler 
2315675f44eSAxel Dörfler bool
2325675f44eSAxel Dörfler PartitionRecorder::Visit(BDiskDevice* device)
2335675f44eSAxel Dörfler {
2345675f44eSAxel Dörfler 	return false;
2355675f44eSAxel Dörfler }
2365675f44eSAxel Dörfler 
2375675f44eSAxel Dörfler 
2385675f44eSAxel Dörfler bool
2395675f44eSAxel Dörfler PartitionRecorder::Visit(BPartition* partition, int32 level)
2405675f44eSAxel Dörfler {
2415675f44eSAxel Dörfler 	if (partition->ContainsPartitioningSystem())
2425675f44eSAxel Dörfler 		return false;
2435675f44eSAxel Dörfler 
2445675f44eSAxel Dörfler 	BPath partitionPath;
2455675f44eSAxel Dörfler 	partition->GetPath(&partitionPath);
2465675f44eSAxel Dörfler 
2475675f44eSAxel Dörfler 	BString buffer;
2485675f44eSAxel Dörfler 	const char* name = partition->ContentName();
2495675f44eSAxel Dörfler 	if (name == NULL) {
2505675f44eSAxel Dörfler 		BString number;
25149c044abSAxel Dörfler 		number << ++fUnnamedIndex;
2525675f44eSAxel Dörfler 		buffer << B_TRANSLATE_COMMENT("Unnamed %d",
2535675f44eSAxel Dörfler 			"Default name of a partition whose name could not be read from "
2545675f44eSAxel Dörfler 			"disk; characters in codepage 437 are allowed only");
2555675f44eSAxel Dörfler 		buffer.ReplaceFirst("%d", number);
2565675f44eSAxel Dörfler 		name = buffer.String();
2575675f44eSAxel Dörfler 	}
2585675f44eSAxel Dörfler 
2595675f44eSAxel Dörfler 	const char* type = partition->Type();
2605675f44eSAxel Dörfler 	if (type == NULL)
2615675f44eSAxel Dörfler 		type = B_TRANSLATE_COMMENT("Unknown", "Text is shown for an unknown "
2625675f44eSAxel Dörfler 			"partition type");
2635675f44eSAxel Dörfler 
2645675f44eSAxel Dörfler 	BMessage message;
2655675f44eSAxel Dörfler 	// Data as required by BootLoader.h
2665675f44eSAxel Dörfler 	message.AddBool("show", true);
2675675f44eSAxel Dörfler 	message.AddString("name", name);
2685675f44eSAxel Dörfler 	message.AddString("type", type);
2695675f44eSAxel Dörfler 	message.AddString("path", partitionPath.Path());
27049c044abSAxel Dörfler 	if (fBIOSDrive != 0)
27149c044abSAxel Dörfler 		message.AddInt8("drive", fBIOSDrive);
2725675f44eSAxel Dörfler 	message.AddInt64("size", partition->Size());
27349c044abSAxel Dörfler 	message.AddInt64("offset", partition->Offset());
2745675f44eSAxel Dörfler 
27549c044abSAxel Dörfler 	fSettings.AddMessage("partition", &message);
27649c044abSAxel Dörfler 	fFound = true;
2775675f44eSAxel Dörfler 
2785675f44eSAxel Dörfler 	return false;
2795675f44eSAxel Dörfler }
2805675f44eSAxel Dörfler 
2815675f44eSAxel Dörfler 
28249c044abSAxel Dörfler bool
28349c044abSAxel Dörfler PartitionRecorder::FoundPartitions() const
28449c044abSAxel Dörfler {
28549c044abSAxel Dörfler 	return fFound;
28649c044abSAxel Dörfler }
28749c044abSAxel Dörfler 
28849c044abSAxel Dörfler 
2895675f44eSAxel Dörfler // #pragma mark -
2905675f44eSAxel Dörfler 
2915675f44eSAxel Dörfler 
2925675f44eSAxel Dörfler LegacyBootMenu::LegacyBootMenu()
2935675f44eSAxel Dörfler {
2945675f44eSAxel Dörfler }
2955675f44eSAxel Dörfler 
2965675f44eSAxel Dörfler 
2975675f44eSAxel Dörfler LegacyBootMenu::~LegacyBootMenu()
2985675f44eSAxel Dörfler {
2995675f44eSAxel Dörfler }
3005675f44eSAxel Dörfler 
3015675f44eSAxel Dörfler 
3025675f44eSAxel Dörfler bool
30349c044abSAxel Dörfler LegacyBootMenu::IsInstalled(const BootDrive& drive)
3045675f44eSAxel Dörfler {
30549c044abSAxel Dörfler 	// TODO: detect bootman
3065675f44eSAxel Dörfler 	return false;
3075675f44eSAxel Dörfler }
3085675f44eSAxel Dörfler 
3095675f44eSAxel Dörfler 
3105675f44eSAxel Dörfler status_t
31149c044abSAxel Dörfler LegacyBootMenu::CanBeInstalled(const BootDrive& drive)
31249c044abSAxel Dörfler {
31349c044abSAxel Dörfler 	BDiskDevice device;
31449c044abSAxel Dörfler 	status_t status = drive.GetDiskDevice(device);
31549c044abSAxel Dörfler 	if (status != B_OK)
31649c044abSAxel Dörfler 		return status;
31749c044abSAxel Dörfler 
31849c044abSAxel Dörfler 	PartitionVisitor visitor;
31949c044abSAxel Dörfler 	device.VisitEachDescendant(&visitor);
32049c044abSAxel Dörfler 
32149c044abSAxel Dörfler 	// Enough space to write boot menu to drive?
32249c044abSAxel Dörfler 	if (!visitor.HasPartitions() || visitor.FirstOffset() < sizeof(kBootLoader))
32349c044abSAxel Dörfler 		return B_PARTITION_TOO_SMALL;
32449c044abSAxel Dörfler 
32549c044abSAxel Dörfler 	return B_OK;
32649c044abSAxel Dörfler }
32749c044abSAxel Dörfler 
32849c044abSAxel Dörfler 
32949c044abSAxel Dörfler status_t
33049c044abSAxel Dörfler LegacyBootMenu::CollectPartitions(const BootDrive& drive, BMessage& settings)
3315675f44eSAxel Dörfler {
3325675f44eSAxel Dörfler 	status_t status = B_ERROR;
3335675f44eSAxel Dörfler 
33449c044abSAxel Dörfler 	// Remove previous partitions, if any
33549c044abSAxel Dörfler 	settings.RemoveName("partition");
33649c044abSAxel Dörfler 
3375675f44eSAxel Dörfler 	BDiskDeviceRoster diskDeviceRoster;
3385675f44eSAxel Dörfler 	BDiskDevice device;
33949c044abSAxel Dörfler 	bool partitionsFound = false;
34049c044abSAxel Dörfler 
3415675f44eSAxel Dörfler 	while (diskDeviceRoster.GetNextDevice(&device) == B_OK) {
3425675f44eSAxel Dörfler 		BPath path;
3435675f44eSAxel Dörfler 		status_t status = device.GetPath(&path);
3445675f44eSAxel Dörfler 		if (status != B_OK)
3455675f44eSAxel Dörfler 			continue;
3465675f44eSAxel Dörfler 
34749c044abSAxel Dörfler 		// Skip not from BIOS bootable drives that are not the target disk
34849c044abSAxel Dörfler 		int8 biosDrive = 0;
34949c044abSAxel Dörfler 		if (path != drive.Path()
35049c044abSAxel Dörfler 			&& _GetBIOSDrive(path.Path(), biosDrive) != B_OK)
3515675f44eSAxel Dörfler 			continue;
3525675f44eSAxel Dörfler 
35349c044abSAxel Dörfler 		PartitionRecorder recorder(settings, biosDrive);
3545675f44eSAxel Dörfler 		device.VisitEachDescendant(&recorder);
3555675f44eSAxel Dörfler 
35649c044abSAxel Dörfler 		partitionsFound |= recorder.FoundPartitions();
3575675f44eSAxel Dörfler 	}
3585675f44eSAxel Dörfler 
35949c044abSAxel Dörfler 	return partitionsFound ? B_OK : status;
3605675f44eSAxel Dörfler }
3615675f44eSAxel Dörfler 
3625675f44eSAxel Dörfler 
3635675f44eSAxel Dörfler status_t
36449c044abSAxel Dörfler LegacyBootMenu::Install(const BootDrive& drive, BMessage& settings)
3655675f44eSAxel Dörfler {
3665675f44eSAxel Dörfler 	int32 defaultPartitionIndex;
36749c044abSAxel Dörfler 	if (settings.FindInt32("defaultPartition", &defaultPartitionIndex) != B_OK)
3685675f44eSAxel Dörfler 		return B_BAD_VALUE;
3695675f44eSAxel Dörfler 
3705675f44eSAxel Dörfler 	int32 timeout;
37149c044abSAxel Dörfler 	if (settings.FindInt32("timeout", &timeout) != B_OK)
3725675f44eSAxel Dörfler 		return B_BAD_VALUE;
3735675f44eSAxel Dörfler 
37449c044abSAxel Dörfler 	int fd = open(drive.Path(), O_RDWR);
3755675f44eSAxel Dörfler 	if (fd < 0)
3765675f44eSAxel Dörfler 		return B_IO_ERROR;
3775675f44eSAxel Dörfler 
3785675f44eSAxel Dörfler 	MasterBootRecord oldMBR;
3795675f44eSAxel Dörfler 	if (read(fd, &oldMBR, sizeof(oldMBR)) != sizeof(oldMBR)) {
3805675f44eSAxel Dörfler 		close(fd);
3815675f44eSAxel Dörfler 		return B_IO_ERROR;
3825675f44eSAxel Dörfler 	}
3835675f44eSAxel Dörfler 
3845675f44eSAxel Dörfler 	if (!_IsValid(&oldMBR)) {
3855675f44eSAxel Dörfler 		close(fd);
3865675f44eSAxel Dörfler 		return B_BAD_VALUE;
3875675f44eSAxel Dörfler 	}
3885675f44eSAxel Dörfler 
3895675f44eSAxel Dörfler 	LittleEndianMallocIO newBootLoader;
3905675f44eSAxel Dörfler 	ssize_t size = sizeof(kBootLoader);
3915675f44eSAxel Dörfler 	if (newBootLoader.Write(kBootLoader, size) != size) {
3925675f44eSAxel Dörfler 		close(fd);
3935675f44eSAxel Dörfler 		return B_NO_MEMORY;
3945675f44eSAxel Dörfler 	}
3955675f44eSAxel Dörfler 
3965675f44eSAxel Dörfler 	MasterBootRecord* newMBR = (MasterBootRecord*)newBootLoader.Buffer();
3975675f44eSAxel Dörfler 	_CopyPartitionTable(newMBR, &oldMBR);
3985675f44eSAxel Dörfler 
3995675f44eSAxel Dörfler 	int menuEntries = 0;
4005675f44eSAxel Dörfler 	int defaultMenuEntry = 0;
4015675f44eSAxel Dörfler 	BMessage partition;
4025675f44eSAxel Dörfler 	int32 index;
40349c044abSAxel Dörfler 	for (index = 0; settings.FindMessage("partition", index,
4045675f44eSAxel Dörfler 			&partition) == B_OK; index ++) {
4055675f44eSAxel Dörfler 		bool show;
4065675f44eSAxel Dörfler 		partition.FindBool("show", &show);
4075675f44eSAxel Dörfler 		if (!show)
4085675f44eSAxel Dörfler 			continue;
4095675f44eSAxel Dörfler 		if (index == defaultPartitionIndex)
4105675f44eSAxel Dörfler 			defaultMenuEntry = menuEntries;
4115675f44eSAxel Dörfler 
4125675f44eSAxel Dörfler 		menuEntries ++;
4135675f44eSAxel Dörfler 	}
4145675f44eSAxel Dörfler 	newBootLoader.WriteInt16(menuEntries);
4155675f44eSAxel Dörfler 	newBootLoader.WriteInt16(defaultMenuEntry);
4165675f44eSAxel Dörfler 	newBootLoader.WriteInt16(timeout);
4175675f44eSAxel Dörfler 
41849c044abSAxel Dörfler 	for (index = 0; settings.FindMessage("partition", index,
4195675f44eSAxel Dörfler 			&partition) == B_OK; index ++) {
4205675f44eSAxel Dörfler 		bool show;
4215675f44eSAxel Dörfler 		BString name;
4225675f44eSAxel Dörfler 		BString path;
4235675f44eSAxel Dörfler 		int64 offset;
4245675f44eSAxel Dörfler 		int8 drive;
4255675f44eSAxel Dörfler 		partition.FindBool("show", &show);
4265675f44eSAxel Dörfler 		partition.FindString("name", &name);
4275675f44eSAxel Dörfler 		partition.FindString("path", &path);
4285675f44eSAxel Dörfler 		// LegacyBootMenu specific data
4295675f44eSAxel Dörfler 		partition.FindInt64("offset", &offset);
4305675f44eSAxel Dörfler 		partition.FindInt8("drive", &drive);
4315675f44eSAxel Dörfler 		if (!show)
4325675f44eSAxel Dörfler 			continue;
4335675f44eSAxel Dörfler 
4345675f44eSAxel Dörfler 		BString biosName;
4355675f44eSAxel Dörfler 		_ConvertToBIOSText(name.String(), biosName);
4365675f44eSAxel Dörfler 
4375675f44eSAxel Dörfler 		newBootLoader.WriteString(biosName.String());
4385675f44eSAxel Dörfler 		newBootLoader.WriteInt8(drive);
4395675f44eSAxel Dörfler 		newBootLoader.WriteInt64(offset / kBlockSize);
4405675f44eSAxel Dörfler 	}
4415675f44eSAxel Dörfler 
4425675f44eSAxel Dörfler 	if (!newBootLoader.Align(kBlockSize)) {
4435675f44eSAxel Dörfler 		close(fd);
4445675f44eSAxel Dörfler 		return B_ERROR;
4455675f44eSAxel Dörfler 	}
4465675f44eSAxel Dörfler 
4475675f44eSAxel Dörfler 	lseek(fd, 0, SEEK_SET);
4485675f44eSAxel Dörfler 	const uint8* buffer = (const uint8*)newBootLoader.Buffer();
4495675f44eSAxel Dörfler 	status_t status = _WriteBlocks(fd, buffer, newBootLoader.Position());
4505675f44eSAxel Dörfler 	close(fd);
4515675f44eSAxel Dörfler 	return status;
4525675f44eSAxel Dörfler }
4535675f44eSAxel Dörfler 
4545675f44eSAxel Dörfler 
4555675f44eSAxel Dörfler status_t
4565675f44eSAxel Dörfler LegacyBootMenu::SaveMasterBootRecord(BMessage* settings, BFile* file)
4575675f44eSAxel Dörfler {
4585675f44eSAxel Dörfler 	BString path;
4595675f44eSAxel Dörfler 
4605675f44eSAxel Dörfler 	if (settings->FindString("disk", &path) != B_OK)
4615675f44eSAxel Dörfler 		return B_BAD_VALUE;
4625675f44eSAxel Dörfler 
4635675f44eSAxel Dörfler 	int fd = open(path.String(), O_RDONLY);
4645675f44eSAxel Dörfler 	if (fd < 0)
4655675f44eSAxel Dörfler 		return B_IO_ERROR;
4665675f44eSAxel Dörfler 
4675675f44eSAxel Dörfler 	ssize_t size = kBlockSize * kNumberOfBootLoaderBlocks;
4685675f44eSAxel Dörfler 	uint8* buffer = new(std::nothrow) uint8[size];
4695675f44eSAxel Dörfler 	if (buffer == NULL) {
4705675f44eSAxel Dörfler 		close(fd);
4715675f44eSAxel Dörfler 		return B_NO_MEMORY;
4725675f44eSAxel Dörfler 	}
4735675f44eSAxel Dörfler 
4745675f44eSAxel Dörfler 	status_t status = _ReadBlocks(fd, buffer, size);
4755675f44eSAxel Dörfler 	if (status != B_OK) {
4765675f44eSAxel Dörfler 		close(fd);
4775675f44eSAxel Dörfler 		delete[] buffer;
4785675f44eSAxel Dörfler 		return B_IO_ERROR;
4795675f44eSAxel Dörfler 	}
4805675f44eSAxel Dörfler 
4815675f44eSAxel Dörfler 	MasterBootRecord* mbr = (MasterBootRecord*)buffer;
4825675f44eSAxel Dörfler 	if (!_IsValid(mbr)) {
4835675f44eSAxel Dörfler 		close(fd);
4845675f44eSAxel Dörfler 		delete[] buffer;
4855675f44eSAxel Dörfler 		return B_BAD_VALUE;
4865675f44eSAxel Dörfler 	}
4875675f44eSAxel Dörfler 
4885675f44eSAxel Dörfler 	if (file->Write(buffer, size) != size)
4895675f44eSAxel Dörfler 		status = B_IO_ERROR;
4905675f44eSAxel Dörfler 	delete[] buffer;
4915675f44eSAxel Dörfler 	close(fd);
4925675f44eSAxel Dörfler 	return status;
4935675f44eSAxel Dörfler }
4945675f44eSAxel Dörfler 
4955675f44eSAxel Dörfler 
4965675f44eSAxel Dörfler status_t
4975675f44eSAxel Dörfler LegacyBootMenu::RestoreMasterBootRecord(BMessage* settings, BFile* file)
4985675f44eSAxel Dörfler {
4995675f44eSAxel Dörfler 	BString path;
5005675f44eSAxel Dörfler 	if (settings->FindString("disk", &path) != B_OK)
5015675f44eSAxel Dörfler 		return B_BAD_VALUE;
5025675f44eSAxel Dörfler 
5035675f44eSAxel Dörfler 	int fd = open(path.String(), O_RDWR);
5045675f44eSAxel Dörfler 	if (fd < 0)
5055675f44eSAxel Dörfler 		return B_IO_ERROR;
5065675f44eSAxel Dörfler 
5075675f44eSAxel Dörfler 	MasterBootRecord oldMBR;
5085675f44eSAxel Dörfler 	if (read(fd, &oldMBR, sizeof(oldMBR)) != sizeof(oldMBR)) {
5095675f44eSAxel Dörfler 		close(fd);
5105675f44eSAxel Dörfler 		return B_IO_ERROR;
5115675f44eSAxel Dörfler 	}
5125675f44eSAxel Dörfler 	if (!_IsValid(&oldMBR)) {
5135675f44eSAxel Dörfler 		close(fd);
5145675f44eSAxel Dörfler 		return B_BAD_VALUE;
5155675f44eSAxel Dörfler 	}
5165675f44eSAxel Dörfler 
5175675f44eSAxel Dörfler 	lseek(fd, 0, SEEK_SET);
5185675f44eSAxel Dörfler 
5195675f44eSAxel Dörfler 	size_t size = kBlockSize * kNumberOfBootLoaderBlocks;
5205675f44eSAxel Dörfler 	uint8* buffer = new(std::nothrow) uint8[size];
5215675f44eSAxel Dörfler 	if (buffer == NULL) {
5225675f44eSAxel Dörfler 		close(fd);
5235675f44eSAxel Dörfler 		return B_NO_MEMORY;
5245675f44eSAxel Dörfler 	}
5255675f44eSAxel Dörfler 
5265675f44eSAxel Dörfler 	if (file->Read(buffer, size) != (ssize_t)size) {
5275675f44eSAxel Dörfler 		close(fd);
5285675f44eSAxel Dörfler 		delete[] buffer;
5295675f44eSAxel Dörfler 		return B_IO_ERROR;
5305675f44eSAxel Dörfler 	}
5315675f44eSAxel Dörfler 
5325675f44eSAxel Dörfler 	MasterBootRecord* newMBR = (MasterBootRecord*)buffer;
5335675f44eSAxel Dörfler 	if (!_IsValid(newMBR)) {
5345675f44eSAxel Dörfler 		close(fd);
5355675f44eSAxel Dörfler 		delete[] buffer;
5365675f44eSAxel Dörfler 		return B_BAD_VALUE;
5375675f44eSAxel Dörfler 	}
5385675f44eSAxel Dörfler 
5395675f44eSAxel Dörfler 	_CopyPartitionTable(newMBR, &oldMBR);
5405675f44eSAxel Dörfler 
5415675f44eSAxel Dörfler 	status_t status = _WriteBlocks(fd, buffer, size);
5425675f44eSAxel Dörfler 	delete[] buffer;
5435675f44eSAxel Dörfler 	close(fd);
5445675f44eSAxel Dörfler 	return status;
5455675f44eSAxel Dörfler }
5465675f44eSAxel Dörfler 
5475675f44eSAxel Dörfler 
5485675f44eSAxel Dörfler status_t
5495675f44eSAxel Dörfler LegacyBootMenu::GetDisplayText(const char* text, BString& displayText)
5505675f44eSAxel Dörfler {
5515675f44eSAxel Dörfler 	BString biosText;
5525675f44eSAxel Dörfler 	if (!_ConvertToBIOSText(text, biosText)) {
5535675f44eSAxel Dörfler 		displayText = "???";
5545675f44eSAxel Dörfler 		return B_ERROR;
5555675f44eSAxel Dörfler 	}
5565675f44eSAxel Dörfler 
5575675f44eSAxel Dörfler 	// convert back to UTF-8
5585675f44eSAxel Dörfler 	int32 biosTextLength = biosText.Length();
5595675f44eSAxel Dörfler 	int32 bufferLength = strlen(text);
5605675f44eSAxel Dörfler 	char* buffer = displayText.LockBuffer(bufferLength + 1);
5615675f44eSAxel Dörfler 	int32 state = 0;
5625675f44eSAxel Dörfler 	if (convert_to_utf8(B_MS_DOS_CONVERSION,
5635675f44eSAxel Dörfler 		biosText.String(), &biosTextLength,
5645675f44eSAxel Dörfler 		buffer, &bufferLength, &state) != B_OK) {
5655675f44eSAxel Dörfler 		displayText.UnlockBuffer(0);
5665675f44eSAxel Dörfler 		displayText = "???";
5675675f44eSAxel Dörfler 		return B_ERROR;
5685675f44eSAxel Dörfler 	}
5695675f44eSAxel Dörfler 
5705675f44eSAxel Dörfler 	buffer[bufferLength] = '\0';
5715675f44eSAxel Dörfler 	displayText.UnlockBuffer(bufferLength);
5725675f44eSAxel Dörfler 	return B_OK;
5735675f44eSAxel Dörfler }
5745675f44eSAxel Dörfler 
5755675f44eSAxel Dörfler 
5765675f44eSAxel Dörfler bool
5775675f44eSAxel Dörfler LegacyBootMenu::_ConvertToBIOSText(const char* text, BString& biosText)
5785675f44eSAxel Dörfler {
5795675f44eSAxel Dörfler 	// convert text in UTF-8 to 'code page 437'
5805675f44eSAxel Dörfler 	int32 textLength = strlen(text);
5815675f44eSAxel Dörfler 
5825675f44eSAxel Dörfler 	int32 biosTextLength = textLength;
5835675f44eSAxel Dörfler 	char* buffer = biosText.LockBuffer(biosTextLength + 1);
5845675f44eSAxel Dörfler 	if (buffer == NULL) {
5855675f44eSAxel Dörfler 		biosText.UnlockBuffer(0);
5865675f44eSAxel Dörfler 		return false;
5875675f44eSAxel Dörfler 	}
5885675f44eSAxel Dörfler 
5895675f44eSAxel Dörfler 	int32 state = 0;
5905675f44eSAxel Dörfler 	if (convert_from_utf8(B_MS_DOS_CONVERSION, text, &textLength,
5915675f44eSAxel Dörfler 		buffer, &biosTextLength, &state) != B_OK) {
5925675f44eSAxel Dörfler 		biosText.UnlockBuffer(0);
5935675f44eSAxel Dörfler 		return false;
5945675f44eSAxel Dörfler 	}
5955675f44eSAxel Dörfler 
5965675f44eSAxel Dörfler 	buffer[biosTextLength] = '\0';
5975675f44eSAxel Dörfler 	biosText.UnlockBuffer(biosTextLength);
5985675f44eSAxel Dörfler 	return biosTextLength < kMaxBootMenuItemLength;
5995675f44eSAxel Dörfler }
6005675f44eSAxel Dörfler 
6015675f44eSAxel Dörfler 
60249c044abSAxel Dörfler status_t
60349c044abSAxel Dörfler LegacyBootMenu::_GetBIOSDrive(const char* device, int8& drive)
6045675f44eSAxel Dörfler {
6055675f44eSAxel Dörfler 	int fd = open(device, O_RDONLY);
6065675f44eSAxel Dörfler 	if (fd < 0)
60749c044abSAxel Dörfler 		return errno;
60849c044abSAxel Dörfler 
60949c044abSAxel Dörfler 	status_t status = ioctl(fd, B_GET_BIOS_DRIVE_ID, drive, 1);
6105675f44eSAxel Dörfler 	close(fd);
61149c044abSAxel Dörfler 	return status;
6125675f44eSAxel Dörfler }
6135675f44eSAxel Dörfler 
6145675f44eSAxel Dörfler 
6155675f44eSAxel Dörfler status_t
6165675f44eSAxel Dörfler LegacyBootMenu::_ReadBlocks(int fd, uint8* buffer, size_t size)
6175675f44eSAxel Dörfler {
6185675f44eSAxel Dörfler 	if (size % kBlockSize != 0) {
6195675f44eSAxel Dörfler 		fprintf(stderr, "_ReadBlocks buffer size must be a multiple of %d\n",
6205675f44eSAxel Dörfler 			(int)kBlockSize);
6215675f44eSAxel Dörfler 		return B_BAD_VALUE;
6225675f44eSAxel Dörfler 	}
6235675f44eSAxel Dörfler 	const size_t blocks = size / kBlockSize;
6245675f44eSAxel Dörfler 	uint8* block = buffer;
6255675f44eSAxel Dörfler 	for (size_t i = 0; i < blocks; i ++, block += kBlockSize) {
6265675f44eSAxel Dörfler 		if (read(fd, block, kBlockSize) != (ssize_t)kBlockSize)
6275675f44eSAxel Dörfler 			return B_IO_ERROR;
6285675f44eSAxel Dörfler 	}
6295675f44eSAxel Dörfler 	return B_OK;
6305675f44eSAxel Dörfler }
6315675f44eSAxel Dörfler 
6325675f44eSAxel Dörfler 
6335675f44eSAxel Dörfler status_t
6345675f44eSAxel Dörfler LegacyBootMenu::_WriteBlocks(int fd, const uint8* buffer, size_t size)
6355675f44eSAxel Dörfler {
6365675f44eSAxel Dörfler 	if (size % kBlockSize != 0) {
6375675f44eSAxel Dörfler 		fprintf(stderr, "_WriteBlocks buffer size must be a multiple of %d\n",
6385675f44eSAxel Dörfler 			(int)kBlockSize);
6395675f44eSAxel Dörfler 		return B_BAD_VALUE;
6405675f44eSAxel Dörfler 	}
6415675f44eSAxel Dörfler 	const size_t blocks = size / kBlockSize;
6425675f44eSAxel Dörfler 	const uint8* block = buffer;
6435675f44eSAxel Dörfler 	for (size_t i = 0; i < blocks; i ++, block += kBlockSize) {
6445675f44eSAxel Dörfler 		if (write(fd, block, kBlockSize) != (ssize_t)kBlockSize)
6455675f44eSAxel Dörfler 			return B_IO_ERROR;
6465675f44eSAxel Dörfler 	}
6475675f44eSAxel Dörfler 	return B_OK;
6485675f44eSAxel Dörfler }
6495675f44eSAxel Dörfler 
6505675f44eSAxel Dörfler 
6515675f44eSAxel Dörfler void
6525675f44eSAxel Dörfler LegacyBootMenu::_CopyPartitionTable(MasterBootRecord* destination,
6535675f44eSAxel Dörfler 		const MasterBootRecord* source)
6545675f44eSAxel Dörfler {
6555675f44eSAxel Dörfler 	memcpy(destination->diskSignature, source->diskSignature,
6565675f44eSAxel Dörfler 		sizeof(source->diskSignature) + sizeof(source->reserved)
6575675f44eSAxel Dörfler 			+ sizeof(source->partition));
6585675f44eSAxel Dörfler }
6595675f44eSAxel Dörfler 
6605675f44eSAxel Dörfler 
6615675f44eSAxel Dörfler bool
6625675f44eSAxel Dörfler LegacyBootMenu::_IsValid(const MasterBootRecord* mbr)
6635675f44eSAxel Dörfler {
6645675f44eSAxel Dörfler 	return mbr->signature[0] == (kMBRSignature & 0xff)
6655675f44eSAxel Dörfler 		&& mbr->signature[1] == (kMBRSignature >> 8);
6665675f44eSAxel Dörfler }
667