/*
* Copyright 2008-2011, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Michael Pfeiffer <laplace@users.sourceforge.net>
*/
#include "BootManagerController.h"
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <File.h>
#include <FindDirectory.h>
#include <Locale.h>
#include <Path.h>
#include <String.h>
#include "BootDrive.h"
#include "DefaultPartitionPage.h"
#include "DescriptionPage.h"
#include "DrivesPage.h"
#include "FileSelectionPage.h"
#include "LegacyBootMenu.h"
#include "PartitionsPage.h"
#include "WizardView.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "BootManagerController"
BootManagerController::BootManagerController()
:
fBootDrive(NULL),
fBootMenu(NULL)
{
// set defaults
fSettings.AddBool("install", true);
fSettings.AddInt32("defaultPartition", 0);
fSettings.AddInt32("timeout", -1);
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) {
path.Append("bootman/MBR");
fSettings.AddString("file", path.Path());
// create directory
BPath parent;
if (path.GetParent(&parent) == B_OK) {
BDirectory directory;
directory.CreateDirectory(parent.Path(), NULL);
}
} else {
fSettings.AddString("file", "");
}
// That's the only boot menu we support at the moment.
fBootMenus.AddItem(new LegacyBootMenu());
}
BootManagerController::~BootManagerController()
{
}
void
BootManagerController::Previous(WizardView* wizard)
{
if (CurrentState() != kStateEntry)
WizardController::Previous(wizard);
else {
fSettings.ReplaceBool("install", false);
WizardController::Next(wizard);
}
}
int32
BootManagerController::InitialState()
{
return kStateEntry;
}
int32
BootManagerController::NextState(int32 state)
{
switch (state) {
case kStateEntry:
{
const char* path;
if (fSettings.FindString("disk", &path) != B_OK)
return kStateErrorEntry;
delete fBootDrive;
fBootDrive = new BootDrive(path);
fBootMenu = fBootDrive->InstalledMenu(fBootMenus);
if (fSettings.FindBool("install")) {
int32 nextState = fBootMenu != NULL
? kStatePartitions : kStateSaveMBR;
// TODO: call BootDrive::AddSupportedMenus() once we support
// more than one type of boot menu - we'll probably need a
// requester to choose from them then as well.
if (fBootMenu == NULL)
fBootMenu = fBootMenus.ItemAt(0);
fCollectPartitionsStatus = fBootMenu->CollectPartitions(
*fBootDrive, fSettings);
return nextState;
}
return kStateUninstall;
}
case kStateErrorEntry:
be_app->PostMessage(B_QUIT_REQUESTED);
break;
case kStateSaveMBR:
if (_SaveMBR())
return kStateMBRSaved;
break;
case kStateMBRSaved:
return kStatePartitions;
case kStatePartitions:
if (_HasSelectedPartitions())
return kStateDefaultPartitions;
break;
case kStateDefaultPartitions:
return kStateInstallSummary;
case kStateInstallSummary:
if (_WriteBootMenu())
return kStateInstalled;
break;
case kStateInstalled:
be_app->PostMessage(B_QUIT_REQUESTED);
break;
case kStateUninstall:
if (_RestoreMBR())
return kStateUninstalled;
break;
case kStateUninstalled:
be_app->PostMessage(B_QUIT_REQUESTED);
break;
}
// cannot leave the current state/page
return -1;
}
bool
BootManagerController::_HasSelectedPartitions()
{
BMessage message;
for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK;
i++) {
bool show;
if (message.FindBool("show", &show) == B_OK && show)
return true;
}
BAlert* alert = new BAlert("info",
B_TRANSLATE("At least one partition must be selected!"),
B_TRANSLATE_COMMENT("OK", "Button"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return false;
}
bool
BootManagerController::_WriteBootMenu()
{
BAlert* alert = new BAlert("confirm", B_TRANSLATE("About to write the "
"boot menu to disk. Are you sure you want to continue?"),
B_TRANSLATE_COMMENT("Write boot menu", "Button"),
B_TRANSLATE_COMMENT("Back", "Button"), NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
if (alert->Go() == 1)
return false;
fWriteBootMenuStatus = fBootMenu->Install(*fBootDrive, fSettings);
return true;
}
bool
BootManagerController::_SaveMBR()
{
BString path;
fSettings.FindString("file", &path);
BFile file(path.String(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
fSaveMBRStatus = fBootMenu->SaveMasterBootRecord(&fSettings, &file);
return true;
}
bool
BootManagerController::_RestoreMBR()
{
BString disk;
BString path;
fSettings.FindString("disk", &disk);
fSettings.FindString("file", &path);
BString message;
message << B_TRANSLATE_COMMENT("About to restore the Master Boot Record "
"(MBR) of %disk from %file. Do you wish to continue?",
"Don't translate the place holders: %disk and %file");
message.ReplaceFirst("%disk", disk);
message.ReplaceFirst("%file", path);
BAlert* alert = new BAlert("confirm", message.String(),
B_TRANSLATE_COMMENT("Restore MBR", "Button"),
B_TRANSLATE_COMMENT("Back", "Button"),
NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
if (alert->Go() == 1)
return false;
BFile file(path.String(), B_READ_ONLY);
fRestoreMBRStatus = fBootMenu->RestoreMasterBootRecord(&fSettings, &file);
return true;
}
WizardPageView*
BootManagerController::CreatePage(int32 state, WizardView* wizard)
{
WizardPageView* page = NULL;
BRect frame(0, 0, 300, 250);
switch (state) {
case kStateEntry:
fSettings.ReplaceBool("install", true);
page = new DrivesPage(wizard, fBootMenus, &fSettings, "drives");
break;
case kStateErrorEntry:
page = _CreateErrorEntryPage();
wizard->SetPreviousButtonHidden(true);
wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button"));
break;
case kStateSaveMBR:
page = _CreateSaveMBRPage(frame);
wizard->SetPreviousButtonHidden(false);
break;
case kStateMBRSaved:
page = _CreateMBRSavedPage();
break;
case kStatePartitions:
page = new PartitionsPage(&fSettings, "partitions");
wizard->SetPreviousButtonHidden(false);
break;
case kStateDefaultPartitions:
page = new DefaultPartitionPage(&fSettings, frame, "default");
break;
case kStateInstallSummary:
page = _CreateInstallSummaryPage();
break;
case kStateInstalled:
page = _CreateInstalledPage();
wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button"));
break;
case kStateUninstall:
page = _CreateUninstallPage(frame);
wizard->SetPreviousButtonHidden(false);
break;
case kStateUninstalled:
// TODO prevent overwriting MBR after clicking "Previous"
page = _CreateUninstalledPage();
wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button"));
break;
}
return page;
}
WizardPageView*
BootManagerController::_CreateErrorEntryPage()
{
BString description;
if (fCollectPartitionsStatus == B_ENTRY_NOT_FOUND) {
description << B_TRANSLATE_COMMENT("Partition table not compatible",
"Title") << "\n\n"
<< B_TRANSLATE("The partition table of the first hard disk is not "
"compatible with Boot Manager.\n"
"Boot Manager only works with IBM PC MBR partitions.");
} else if (fCollectPartitionsStatus == B_PARTITION_TOO_SMALL) {
description << B_TRANSLATE_COMMENT("First partition starts too early",
"Title") << "\n\n"
<< B_TRANSLATE("The first partition on the disk starts too early "
"and does not leave enough space free for a boot menu.\n"
"Boot Manager needs 2 KiB available space before the first "
"partition.");
} else {
description << B_TRANSLATE_COMMENT("Error reading partition table",
"Title") << "\n\n"
<< B_TRANSLATE("Boot Manager is unable to read the partition "
"table!");
}
return new DescriptionPage("errorEntry", description.String(), true);
}
WizardPageView*
BootManagerController::_CreateSaveMBRPage(BRect frame)
{
BString description;
BString disk;
fSettings.FindString("disk", &disk);
description << B_TRANSLATE_COMMENT("Backup Master Boot Record", "Title")
<< "\n" << B_TRANSLATE("The Master Boot Record (MBR) of the boot "
"device:\n"
"\t%s\n"
"will now be saved to disk. Please select a file to "
"save the MBR into.\n\n"
"If something goes wrong with the installation or if "
"you later wish to remove the boot menu, simply run the "
"bootman program and choose the 'Uninstall' option.");
description.ReplaceFirst("%s", disk);
return new FileSelectionPage(&fSettings, frame, "saveMBR",
description.String(),
B_SAVE_PANEL);
}
WizardPageView*
BootManagerController::_CreateMBRSavedPage()
{
BString description;
BString file;
fSettings.FindString("file", &file);
if (fSaveMBRStatus == B_OK) {
description << B_TRANSLATE_COMMENT("Old Master Boot Record saved",
"Title") << "\n"
<< B_TRANSLATE("The old Master Boot Record was successfully "
"saved to %s.") << "\n";
} else {
description << B_TRANSLATE_COMMENT("Old Master Boot Record backup "
"failure", "Title") << "\n"
<< B_TRANSLATE("The old Master Boot Record could not be saved "
"to %s. You can continue the installation but there will be no "
"way to uninstall the boot menu.") << "\n";
}
description.ReplaceFirst("%s", file);
return new DescriptionPage("summary", description.String(), true);
}
WizardPageView*
BootManagerController::_CreateInstallSummaryPage()
{
BString description;
BString disk;
fSettings.FindString("disk", &disk);
description << B_TRANSLATE_COMMENT("Summary", "Title") << "\n"
<< B_TRANSLATE("About to write the following boot menu to the boot "
"disk (%s). Please verify the information below before continuing.")
<< "\n\n";
description.ReplaceFirst("%s", disk);
BMessage message;
for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK;
i++) {
bool show;
if (message.FindBool("show", &show) != B_OK || !show)
continue;
BString name;
BString path;
message.FindString("name", &name);
message.FindString("path", &path);
BString displayName;
if (fBootMenu->GetDisplayText(name.String(), displayName) == B_OK)
description << displayName << "\t(" << path << ")\n";
else
description << name << "\t(" << path << ")\n";
}
return new DescriptionPage("summary", description.String(), true);
}
WizardPageView*
BootManagerController::_CreateInstalledPage()
{
BString description;
if (fWriteBootMenuStatus == B_OK) {
description << B_TRANSLATE_COMMENT("Installation of boot menu "
"completed", "Title") << "\n"
<< B_TRANSLATE("The boot manager has been successfully installed "
"on your system.");
} else {
description << B_TRANSLATE_COMMENT("Installation of boot menu failed",
"Title") << "\n"
<< B_TRANSLATE("An error occurred writing the boot menu. "
"The Master Boot Record might be destroyed, "
"you should restore the MBR now!");
}
return new DescriptionPage("done", description, true);
}
WizardPageView*
BootManagerController::_CreateUninstallPage(BRect frame)
{
BString description;
description << B_TRANSLATE_COMMENT("Uninstall boot manager", "Title")
<< "\n\n"
<< B_TRANSLATE("Please locate the Master Boot Record (MBR) save file "
"to restore from. This is the file that was created when the "
"boot manager was first installed.");
return new FileSelectionPage(&fSettings, frame, "restoreMBR",
description.String(), B_OPEN_PANEL);
}
WizardPageView*
BootManagerController::_CreateUninstalledPage()
{
BString description;
BString disk;
BString file;
fSettings.FindString("disk", &disk);
fSettings.FindString("file", &file);
if (fRestoreMBRStatus == B_OK) {
description << B_TRANSLATE_COMMENT("Uninstallation of boot menu "
"completed", "Title") << "\n"
<< B_TRANSLATE("The Master Boot Record of the boot device "
"(%DISK) has been successfully restored from %FILE.");
description.ReplaceFirst("%DISK", disk);
description.ReplaceFirst("%FILE", file);
} else {
description << B_TRANSLATE_COMMENT("Uninstallation of boot menu "
"failed", "Title") << "\n"
<< B_TRANSLATE("The Master Boot Record could not be restored!");
}
return new DescriptionPage("summary", description.String(), true);
}
↑ V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fCollectPartitionsStatus, fWriteBootMenuStatus, fSaveMBRStatus, fRestoreMBRStatus.
↑ V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.
↑ V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.