boot loader: Add safe mode blacklist submenu
It's a browser for the system package content, where entries can be selected to blacklist them. The selected entries are removed from the packagefs instance in the boot loader, so that e.g. selected drivers won't be picked up. The paths are also added to the safe mode driver settings and will be interpreted when the system packagefs instance is mounted by the kernel.
This commit is contained in:
parent
f2620e4714
commit
c04f3a625a
72
headers/private/kernel/boot/PathBlacklist.h
Normal file
72
headers/private/kernel/boot/PathBlacklist.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef KERNEL_BOOT_PATH_BLACKLIST_H
|
||||
#define KERNEL_BOOT_PATH_BLACKLIST_H
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <util/SinglyLinkedList.h>
|
||||
|
||||
|
||||
class BlacklistedPath : public SinglyLinkedListLinkImpl<BlacklistedPath> {
|
||||
public:
|
||||
BlacklistedPath();
|
||||
~BlacklistedPath();
|
||||
|
||||
bool SetTo(const char* path);
|
||||
|
||||
bool Append(const char* component);
|
||||
|
||||
const char* Path() const
|
||||
{ return fPath != NULL ? fPath : ""; }
|
||||
|
||||
size_t Length() const
|
||||
{ return fLength; }
|
||||
|
||||
bool operator==(const char* path) const
|
||||
{ return strcmp(Path(), path) == 0; }
|
||||
|
||||
private:
|
||||
bool _Resize(size_t length, bool keepData);
|
||||
|
||||
private:
|
||||
char* fPath;
|
||||
size_t fLength;
|
||||
size_t fCapacity;
|
||||
};
|
||||
|
||||
|
||||
class PathBlacklist {
|
||||
public:
|
||||
typedef SinglyLinkedList<BlacklistedPath>::Iterator Iterator;
|
||||
|
||||
public:
|
||||
PathBlacklist();
|
||||
~PathBlacklist();
|
||||
|
||||
bool Add(const char* path);
|
||||
void Remove(const char* path);
|
||||
bool Contains(const char* path) const;
|
||||
void MakeEmpty();
|
||||
|
||||
bool IsEmpty() const
|
||||
{ return fPaths.IsEmpty(); }
|
||||
|
||||
Iterator GetIterator() const
|
||||
{ return fPaths.GetIterator(); }
|
||||
|
||||
private:
|
||||
BlacklistedPath* _FindPath(const char* path) const;
|
||||
|
||||
private:
|
||||
typedef SinglyLinkedList<BlacklistedPath> PathList;
|
||||
|
||||
private:
|
||||
PathList fPaths;
|
||||
};
|
||||
|
||||
|
||||
#endif // KERNEL_BOOT_PATH_BLACKLIST_H
|
@ -16,6 +16,9 @@
|
||||
#include "DebugSupport.h"
|
||||
|
||||
|
||||
static const char* const kEntryBlacklistParameterName = "EntryBlacklist";
|
||||
|
||||
|
||||
// #pragma mark - PackageSettingsItem
|
||||
|
||||
|
||||
@ -39,14 +42,21 @@ PackageSettingsItem::~PackageSettingsItem()
|
||||
|
||||
|
||||
status_t
|
||||
PackageSettingsItem::Init(const driver_parameter& parameter)
|
||||
PackageSettingsItem::Init(const char* name)
|
||||
{
|
||||
if (!fName.SetTo(parameter.values[0]) || fEntries.Init() != B_OK)
|
||||
if (!fName.SetTo(name) || fEntries.Init() != B_OK)
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameter.parameter_count; i++) {
|
||||
const driver_parameter& subParameter = parameter.parameters[i];
|
||||
if (strcmp(subParameter.name, "EntryBlacklist") != 0)
|
||||
|
||||
status_t
|
||||
PackageSettingsItem::ApplySettings(const driver_parameter* parameters,
|
||||
int parameterCount)
|
||||
{
|
||||
for (int i = 0; i < parameterCount; i++) {
|
||||
const driver_parameter& subParameter = parameters[i];
|
||||
if (strcmp(subParameter.name, kEntryBlacklistParameterName) != 0)
|
||||
continue;
|
||||
|
||||
status_t error = _AddBlackListedEntries(subParameter);
|
||||
@ -165,6 +175,22 @@ PackageSettings::Load(dev_t mountPointDeviceID, ino_t mountPointNodeID,
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// First get the safe mode options. Those apply to the system package.
|
||||
if (mountType == PACKAGE_FS_MOUNT_TYPE_SYSTEM) {
|
||||
void* settingsHandle = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS);
|
||||
if (settingsHandle != NULL) {
|
||||
if (const driver_settings* settings
|
||||
= get_driver_settings(settingsHandle)) {
|
||||
error = _AddPackageSettingsItem("haiku", settings->parameters,
|
||||
settings->parameter_count);
|
||||
// abort only in case of serious issues (memory shortage)
|
||||
if (error == B_NO_MEMORY)
|
||||
return error;
|
||||
}
|
||||
unload_driver_settings(settingsHandle);
|
||||
}
|
||||
}
|
||||
|
||||
// get the mount point relative settings file path
|
||||
const char* settingsFilePath = mountType == PACKAGE_FS_MOUNT_TYPE_HOME
|
||||
? kUserSettingsGlobalDirectory "/packages"
|
||||
@ -201,7 +227,8 @@ PackageSettings::Load(dev_t mountPointDeviceID, ino_t mountPointNodeID,
|
||||
continue;
|
||||
}
|
||||
|
||||
error = _AddPackageSettingsItem(parameter);
|
||||
error = _AddPackageSettingsItem(parameter.values[0],
|
||||
parameter.parameters, parameter.parameter_count);
|
||||
// abort only in case of serious issues (memory shortage)
|
||||
if (error == B_NO_MEMORY)
|
||||
return error;
|
||||
@ -219,14 +246,21 @@ PackageSettings::PackageItemFor(const String& name) const
|
||||
|
||||
|
||||
status_t
|
||||
PackageSettings::_AddPackageSettingsItem(const driver_parameter& parameter)
|
||||
PackageSettings::_AddPackageSettingsItem(const char* name,
|
||||
const driver_parameter* parameters, int parameterCount)
|
||||
{
|
||||
PackageSettingsItem* packageItem = new(std::nothrow) PackageSettingsItem;
|
||||
if (packageItem == NULL || packageItem->Init(parameter) != B_OK) {
|
||||
delete packageItem;
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
// get/create the package item
|
||||
PackageSettingsItem* packageItem = fPackageItems.Lookup(StringKey(name));
|
||||
if (packageItem == NULL) {
|
||||
packageItem = new(std::nothrow) PackageSettingsItem;
|
||||
if (packageItem == NULL || packageItem->Init(name) != B_OK) {
|
||||
delete packageItem;
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
}
|
||||
|
||||
fPackageItems.Insert(packageItem);
|
||||
}
|
||||
|
||||
fPackageItems.Insert(packageItem);
|
||||
return B_OK;
|
||||
// apply the settings
|
||||
return packageItem->ApplySettings(parameters, parameterCount);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <packagefs.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
|
||||
#include "String.h"
|
||||
#include "StringKey.h"
|
||||
|
||||
|
||||
struct driver_parameter;
|
||||
@ -102,7 +102,10 @@ public:
|
||||
PackageSettingsItem();
|
||||
~PackageSettingsItem();
|
||||
|
||||
status_t Init(const driver_parameter& parameter);
|
||||
status_t Init(const char* name);
|
||||
status_t ApplySettings(
|
||||
const driver_parameter* parameters,
|
||||
int parameterCount);
|
||||
|
||||
const String& Name() const
|
||||
{ return fName; }
|
||||
@ -158,10 +161,10 @@ private:
|
||||
|
||||
|
||||
struct PackageSettingsItemHashDefinition {
|
||||
typedef String KeyType;
|
||||
typedef StringKey KeyType;
|
||||
typedef PackageSettingsItem ValueType;
|
||||
|
||||
size_t HashKey(const String& key) const
|
||||
size_t HashKey(const StringKey& key) const
|
||||
{
|
||||
return key.Hash();
|
||||
}
|
||||
@ -171,7 +174,7 @@ struct PackageSettingsItemHashDefinition {
|
||||
return HashKey(value->Name());
|
||||
}
|
||||
|
||||
bool Compare(const String& key, const PackageSettingsItem* value) const
|
||||
bool Compare(const StringKey& key, const PackageSettingsItem* value) const
|
||||
{
|
||||
return key == value->Name();
|
||||
}
|
||||
@ -199,8 +202,9 @@ private:
|
||||
PackageItemTable;
|
||||
|
||||
private:
|
||||
status_t _AddPackageSettingsItem(
|
||||
const driver_parameter& parameter);
|
||||
status_t _AddPackageSettingsItem(const char* name,
|
||||
const driver_parameter* parameters,
|
||||
int parameterCount);
|
||||
|
||||
private:
|
||||
PackageItemTable fPackageItems;
|
||||
|
@ -56,6 +56,7 @@ UsePrivateHeaders shared storage ;
|
||||
|
||||
|
||||
BootStaticLibrary boot_loader :
|
||||
PathBlacklist.cpp
|
||||
elf.cpp
|
||||
heap.cpp
|
||||
kernel_args.cpp
|
||||
|
180
src/system/boot/loader/PathBlacklist.cpp
Normal file
180
src/system/boot/loader/PathBlacklist.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <boot/PathBlacklist.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
// #pragma mark - BlacklistedPath
|
||||
|
||||
|
||||
BlacklistedPath::BlacklistedPath()
|
||||
:
|
||||
fPath(NULL),
|
||||
fLength(0),
|
||||
fCapacity(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BlacklistedPath::~BlacklistedPath()
|
||||
{
|
||||
free(fPath);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BlacklistedPath::SetTo(const char* path)
|
||||
{
|
||||
size_t length = strlen(path);
|
||||
if (length > 0 && path[length - 1] == '/')
|
||||
length--;
|
||||
|
||||
if (!_Resize(length, false))
|
||||
return false;
|
||||
|
||||
if (length > 0) {
|
||||
memcpy(fPath, path, length);
|
||||
fPath[length] = '\0';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BlacklistedPath::Append(const char* component)
|
||||
{
|
||||
size_t componentLength = strlen(component);
|
||||
if (componentLength > 0 && component[componentLength - 1] == '/')
|
||||
componentLength--;
|
||||
if (componentLength == 0)
|
||||
return true;
|
||||
|
||||
size_t oldLength = fLength;
|
||||
size_t length = (fLength > 0 ? fLength + 1 : 0) + componentLength;
|
||||
if (!_Resize(length, true))
|
||||
return false;
|
||||
|
||||
if (oldLength > 0)
|
||||
fPath[oldLength++] = '/';
|
||||
memcpy(fPath + oldLength, component, componentLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BlacklistedPath::_Resize(size_t length, bool keepData)
|
||||
{
|
||||
if (length == 0) {
|
||||
free(fPath);
|
||||
fPath = NULL;
|
||||
fLength = 0;
|
||||
fCapacity = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (length < fCapacity) {
|
||||
fPath[length] = '\0';
|
||||
fLength = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t capacity = std::max(length + 1, 2 * fCapacity);
|
||||
capacity = std::max(capacity, size_t(32));
|
||||
|
||||
char* path;
|
||||
if (fLength > 0 && keepData) {
|
||||
path = (char*)realloc(fPath, capacity);
|
||||
if (path == NULL)
|
||||
return false;
|
||||
} else {
|
||||
path = (char*)malloc(capacity);
|
||||
if (path == NULL)
|
||||
return false;
|
||||
free(fPath);
|
||||
}
|
||||
|
||||
fPath = path;
|
||||
fPath[length] = '\0';
|
||||
fLength = length;
|
||||
fCapacity = capacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - PathBlacklist
|
||||
|
||||
|
||||
PathBlacklist::PathBlacklist()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PathBlacklist::~PathBlacklist()
|
||||
{
|
||||
MakeEmpty();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PathBlacklist::Add(const char* path)
|
||||
{
|
||||
BlacklistedPath* blacklistedPath = _FindPath(path);
|
||||
if (blacklistedPath != NULL)
|
||||
return true;
|
||||
|
||||
blacklistedPath = new(std::nothrow) BlacklistedPath;
|
||||
if (blacklistedPath == NULL || !blacklistedPath->SetTo(path)) {
|
||||
delete blacklistedPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
fPaths.Add(blacklistedPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PathBlacklist::Remove(const char* path)
|
||||
{
|
||||
BlacklistedPath* blacklistedPath = _FindPath(path);
|
||||
if (blacklistedPath != NULL) {
|
||||
fPaths.Remove(blacklistedPath);
|
||||
delete blacklistedPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PathBlacklist::Contains(const char* path) const
|
||||
{
|
||||
return _FindPath(path) != NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PathBlacklist::MakeEmpty()
|
||||
{
|
||||
while (BlacklistedPath* blacklistedPath = fPaths.RemoveHead())
|
||||
delete blacklistedPath;
|
||||
}
|
||||
|
||||
|
||||
BlacklistedPath*
|
||||
PathBlacklist::_FindPath(const char* path) const
|
||||
{
|
||||
for (PathList::Iterator it = fPaths.GetIterator(); it.HasNext();) {
|
||||
BlacklistedPath* blacklistedPath = it.Next();
|
||||
if (*blacklistedPath == path)
|
||||
return blacklistedPath;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <Referenceable.h>
|
||||
|
||||
#include <boot/PathBlacklist.h>
|
||||
#include <boot/platform.h>
|
||||
|
||||
#include "PackageSettingsItem.h"
|
||||
@ -46,10 +47,10 @@ using namespace BPackageKit::BHPKG;
|
||||
using BPackageKit::BHPKG::BPrivate::PackageFileHeapReader;
|
||||
using BPackageKit::BHPKG::BPrivate::PackageReaderImpl;
|
||||
|
||||
using PackageFS::PackageSettingsItem;
|
||||
using namespace PackageFS;
|
||||
|
||||
|
||||
namespace {
|
||||
namespace PackageFS {
|
||||
|
||||
|
||||
struct PackageDirectory;
|
||||
@ -120,6 +121,10 @@ struct PackageNode : DoublyLinkedListLinkImpl<PackageNode> {
|
||||
return fModifiedTime;
|
||||
}
|
||||
|
||||
virtual void RemoveEntry(const char* path)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
PackageVolume* fVolume;
|
||||
PackageDirectory* fParentDirectory;
|
||||
@ -226,18 +231,46 @@ struct PackageDirectory : PackageNode {
|
||||
if (strcmp(name, "..") == 0)
|
||||
return fParentDirectory;
|
||||
|
||||
for (NodeList::Iterator it = fEntries.GetIterator();
|
||||
PackageNode* child = it.Next();) {
|
||||
if (strcmp(child->Name(), name) == 0)
|
||||
return child;
|
||||
}
|
||||
return _LookupChild(name, strlen(name));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
virtual void RemoveEntry(const char* path)
|
||||
{
|
||||
const char* componentEnd = strchr(path, '/');
|
||||
if (componentEnd == NULL)
|
||||
componentEnd = path + strlen(path);
|
||||
|
||||
PackageNode* child = _LookupChild(path, componentEnd - path);
|
||||
if (child == NULL)
|
||||
return;
|
||||
|
||||
if (*componentEnd == '\0') {
|
||||
// last path component -- delete the child
|
||||
fEntries.Remove(child);
|
||||
delete child;
|
||||
} else {
|
||||
// must be a directory component -- continue resolving the path
|
||||
child->RemoveEntry(componentEnd + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef DoublyLinkedList<PackageNode> NodeList;
|
||||
|
||||
private:
|
||||
PackageNode* _LookupChild(const char* name, size_t nameLength)
|
||||
{
|
||||
for (NodeList::Iterator it = fEntries.GetIterator();
|
||||
PackageNode* child = it.Next();) {
|
||||
if (strncmp(child->Name(), name, nameLength) == 0
|
||||
&& child->Name()[nameLength] == '\0') {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
NodeList fEntries;
|
||||
};
|
||||
@ -641,6 +674,11 @@ struct Directory : ::Directory {
|
||||
fDirectory->Volume()->ReleaseReference();
|
||||
}
|
||||
|
||||
void RemoveEntry(const char* path)
|
||||
{
|
||||
fDirectory->RemoveEntry(path);
|
||||
}
|
||||
|
||||
virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
|
||||
size_t bufferSize)
|
||||
{
|
||||
@ -789,7 +827,7 @@ create_node(PackageNode* packageNode, ::Node*& _node)
|
||||
}
|
||||
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace PackageFS
|
||||
|
||||
|
||||
status_t
|
||||
@ -836,3 +874,18 @@ packagefs_mount_file(int fd, ::Directory* systemDirectory,
|
||||
_mountedDirectory = static_cast< ::Directory*>(rootNode);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
packagefs_apply_path_blacklist(::Directory* systemDirectory,
|
||||
const PathBlacklist& pathBlacklist)
|
||||
{
|
||||
PackageFS::Directory* directory
|
||||
= static_cast<PackageFS::Directory*>(systemDirectory);
|
||||
|
||||
for (PathBlacklist::Iterator it = pathBlacklist.GetIterator();
|
||||
BlacklistedPath* path = it.Next();) {
|
||||
directory->RemoveEntry(path->Path());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
class PathBlacklist;
|
||||
class Directory;
|
||||
class Node;
|
||||
|
||||
@ -18,5 +19,8 @@ class Node;
|
||||
status_t packagefs_mount_file(int fd, Directory* systemDirectory,
|
||||
Directory*& _mountedDirectory);
|
||||
|
||||
void packagefs_apply_path_blacklist(Directory* systemDirectory,
|
||||
const PathBlacklist& pathBlacklist);
|
||||
|
||||
|
||||
#endif // BOOT_LOADER_FILE_SYSTEMS_PACKAGEFS_H
|
||||
|
@ -12,8 +12,11 @@
|
||||
#include <boot/vfs.h>
|
||||
#include <boot/platform.h>
|
||||
#include <boot/heap.h>
|
||||
#include <boot/PathBlacklist.h>
|
||||
#include <boot/stdio.h>
|
||||
|
||||
#include "file_systems/packagefs/packagefs.h"
|
||||
|
||||
|
||||
//#define TRACE_MAIN
|
||||
#ifdef TRACE_MAIN
|
||||
@ -53,6 +56,7 @@ main(stage2_args *args)
|
||||
bool mountedAllVolumes = false;
|
||||
|
||||
BootVolume bootVolume;
|
||||
PathBlacklist pathBlacklist;
|
||||
|
||||
if (get_boot_file_system(args, bootVolume) != B_OK
|
||||
|| (platform_boot_options() & BOOT_OPTION_MENU) != 0) {
|
||||
@ -69,7 +73,7 @@ main(stage2_args *args)
|
||||
|
||||
mountedAllVolumes = true;
|
||||
|
||||
if (user_menu(bootVolume) < B_OK) {
|
||||
if (user_menu(bootVolume, pathBlacklist) < B_OK) {
|
||||
// user requested to quit the loader
|
||||
goto out;
|
||||
}
|
||||
@ -91,7 +95,8 @@ main(stage2_args *args)
|
||||
mountedAllVolumes = true;
|
||||
}
|
||||
|
||||
if (user_menu(bootVolume) < B_OK || !bootVolume.IsValid()) {
|
||||
if (user_menu(bootVolume, pathBlacklist) != B_OK
|
||||
|| !bootVolume.IsValid()) {
|
||||
// user requested to quit the loader
|
||||
goto out;
|
||||
}
|
||||
@ -101,6 +106,11 @@ main(stage2_args *args)
|
||||
// is already loaded at this point and we definitely
|
||||
// know our boot volume, too
|
||||
if (status == B_OK) {
|
||||
if (bootVolume.IsPackaged()) {
|
||||
packagefs_apply_path_blacklist(bootVolume.SystemDirectory(),
|
||||
pathBlacklist);
|
||||
}
|
||||
|
||||
register_boot_file_system(bootVolume);
|
||||
|
||||
if ((platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) == 0)
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Copyright 2011, Rene Gollent, rene@gollent.com.
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@ -14,7 +15,9 @@
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <boot/menu.h>
|
||||
#include <boot/PathBlacklist.h>
|
||||
#include <boot/stage2.h>
|
||||
#include <boot/vfs.h>
|
||||
#include <boot/platform.h>
|
||||
@ -22,6 +25,7 @@
|
||||
#include <boot/stdio.h>
|
||||
#include <safemode.h>
|
||||
#include <util/ring_buffer.h>
|
||||
#include <util/SinglyLinkedList.h>
|
||||
|
||||
#include "kernel_debug_config.h"
|
||||
|
||||
@ -31,7 +35,7 @@
|
||||
#include "RootFileSystem.h"
|
||||
|
||||
|
||||
#define TRACE_MENU
|
||||
//#define TRACE_MENU
|
||||
#ifdef TRACE_MENU
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
@ -39,7 +43,11 @@
|
||||
#endif
|
||||
|
||||
|
||||
static char sSafeModeOptionsBuffer[2048];
|
||||
// only set while in user_menu()
|
||||
static Menu* sMainMenu = NULL;
|
||||
static Menu* sBlacklistRootMenu = NULL;
|
||||
static BootVolume* sBootVolume = NULL;
|
||||
static PathBlacklist* sPathBlacklist;
|
||||
|
||||
|
||||
MenuItem::MenuItem(const char *label, Menu *subMenu)
|
||||
@ -489,22 +497,340 @@ size_to_string(off_t size, char* buffer, size_t bufferSize)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - blacklist menu
|
||||
|
||||
|
||||
class BlacklistMenuItem : public MenuItem {
|
||||
public:
|
||||
BlacklistMenuItem(char* label, Node* node, Menu* subMenu)
|
||||
:
|
||||
MenuItem(label, subMenu),
|
||||
fNode(node),
|
||||
fSubMenu(subMenu)
|
||||
{
|
||||
fNode->Acquire();
|
||||
SetType(MENU_ITEM_MARKABLE);
|
||||
}
|
||||
|
||||
~BlacklistMenuItem()
|
||||
{
|
||||
fNode->Release();
|
||||
|
||||
// make sure the submenu is destroyed
|
||||
SetSubmenu(fSubMenu);
|
||||
}
|
||||
|
||||
bool IsDirectoryItem() const
|
||||
{
|
||||
return fNode->Type() == S_IFDIR;
|
||||
}
|
||||
|
||||
bool GetPath(BlacklistedPath& _path) const
|
||||
{
|
||||
Menu* menu = Supermenu();
|
||||
if (menu != NULL && menu != sBlacklistRootMenu
|
||||
&& menu->Superitem() != NULL) {
|
||||
return static_cast<BlacklistMenuItem*>(menu->Superitem())
|
||||
->GetPath(_path)
|
||||
&& _path.Append(Label());
|
||||
}
|
||||
|
||||
return _path.SetTo(Label());
|
||||
}
|
||||
|
||||
void UpdateBlacklisted()
|
||||
{
|
||||
BlacklistedPath path;
|
||||
if (GetPath(path))
|
||||
_SetMarked(sPathBlacklist->Contains(path.Path()), false);
|
||||
}
|
||||
|
||||
virtual void SetMarked(bool marked)
|
||||
{
|
||||
_SetMarked(marked, true);
|
||||
}
|
||||
|
||||
static bool Less(const MenuItem* a, const MenuItem* b)
|
||||
{
|
||||
const BlacklistMenuItem* item1
|
||||
= static_cast<const BlacklistMenuItem*>(a);
|
||||
const BlacklistMenuItem* item2
|
||||
= static_cast<const BlacklistMenuItem*>(b);
|
||||
|
||||
// directories come first
|
||||
if (item1->IsDirectoryItem() != item2->IsDirectoryItem())
|
||||
return item1->IsDirectoryItem();
|
||||
|
||||
// compare the labels
|
||||
return strcasecmp(item1->Label(), item2->Label()) < 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void _SetMarked(bool marked, bool updateBlacklist)
|
||||
{
|
||||
if (marked == IsMarked())
|
||||
return;
|
||||
|
||||
// For directories toggle the availability of the submenu.
|
||||
if (IsDirectoryItem())
|
||||
SetSubmenu(marked ? NULL : fSubMenu);
|
||||
|
||||
if (updateBlacklist) {
|
||||
BlacklistedPath path;
|
||||
if (GetPath(path)) {
|
||||
if (marked)
|
||||
sPathBlacklist->Add(path.Path());
|
||||
else
|
||||
sPathBlacklist->Remove(path.Path());
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem::SetMarked(marked);
|
||||
}
|
||||
|
||||
private:
|
||||
Node* fNode;
|
||||
Menu* fSubMenu;
|
||||
};
|
||||
|
||||
|
||||
class BlacklistMenu : public Menu {
|
||||
public:
|
||||
BlacklistMenu()
|
||||
:
|
||||
Menu(STANDARD_MENU, "Mark the entries to blacklist"),
|
||||
fDirectory(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
~BlacklistMenu()
|
||||
{
|
||||
SetDirectory(NULL);
|
||||
}
|
||||
|
||||
virtual void Entered()
|
||||
{
|
||||
_DeleteItems();
|
||||
|
||||
if (fDirectory == NULL)
|
||||
return;
|
||||
|
||||
void* cookie;
|
||||
if (fDirectory->Open(&cookie, O_RDONLY) == B_OK) {
|
||||
Node* node;
|
||||
while (fDirectory->GetNextNode(cookie, &node) == B_OK) {
|
||||
BlacklistMenuItem* item = _CreateItem(node);
|
||||
node->Release();
|
||||
if (item == NULL)
|
||||
break;
|
||||
|
||||
AddItem(item);
|
||||
|
||||
item->UpdateBlacklisted();
|
||||
}
|
||||
fDirectory->Close(cookie);
|
||||
}
|
||||
|
||||
SortItems(&BlacklistMenuItem::Less);
|
||||
}
|
||||
|
||||
virtual void Exited()
|
||||
{
|
||||
_DeleteItems();
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetDirectory(Directory* directory)
|
||||
{
|
||||
if (fDirectory != NULL)
|
||||
fDirectory->Release();
|
||||
|
||||
fDirectory = directory;
|
||||
|
||||
if (fDirectory != NULL)
|
||||
fDirectory->Acquire();
|
||||
}
|
||||
|
||||
private:
|
||||
static BlacklistMenuItem* _CreateItem(Node* node)
|
||||
{
|
||||
// Get the node name and duplicate it, so we can use it as a label.
|
||||
char name[B_FILE_NAME_LENGTH];
|
||||
if (node->GetName(name, sizeof(name)) != B_OK)
|
||||
return NULL;
|
||||
|
||||
// append '/' to directory labels
|
||||
bool isDirectory = node->Type() == S_IFDIR;
|
||||
if (isDirectory)
|
||||
strlcat(name, "/", sizeof(name));
|
||||
|
||||
// If this is a directory, create the submenu.
|
||||
BlacklistMenu* subMenu = NULL;
|
||||
if (isDirectory) {
|
||||
subMenu = new(std::nothrow) BlacklistMenu;
|
||||
if (subMenu != NULL)
|
||||
subMenu->SetDirectory(static_cast<Directory*>(node));
|
||||
|
||||
}
|
||||
ObjectDeleter<BlacklistMenu> subMenuDeleter(subMenu);
|
||||
|
||||
// create the menu item
|
||||
BlacklistMenuItem* item = new(std::nothrow) BlacklistMenuItem(name,
|
||||
node, subMenu);
|
||||
if (item == NULL)
|
||||
return NULL;
|
||||
|
||||
subMenuDeleter.Detach();
|
||||
return item;
|
||||
}
|
||||
|
||||
void _DeleteItems()
|
||||
{
|
||||
int32 count = CountItems();
|
||||
for (int32 i = 0; i < count; i++)
|
||||
delete RemoveItemAt(0);
|
||||
}
|
||||
|
||||
private:
|
||||
Directory* fDirectory;
|
||||
};
|
||||
|
||||
|
||||
class BlacklistRootMenu : public BlacklistMenu {
|
||||
public:
|
||||
BlacklistRootMenu()
|
||||
:
|
||||
BlacklistMenu()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Entered()
|
||||
{
|
||||
// Get the system directory, but only if this is a packaged Haiku.
|
||||
// Otherwise blacklisting isn't supported.
|
||||
if (sBootVolume != NULL && sBootVolume->IsValid()
|
||||
&& sBootVolume->IsPackaged()) {
|
||||
SetDirectory(sBootVolume->SystemDirectory());
|
||||
} else
|
||||
SetDirectory(NULL);
|
||||
|
||||
BlacklistMenu::Entered();
|
||||
}
|
||||
|
||||
virtual void Exited()
|
||||
{
|
||||
BlacklistMenu::Exited();
|
||||
SetDirectory(NULL);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
class StringBuffer {
|
||||
public:
|
||||
StringBuffer()
|
||||
:
|
||||
fBuffer(NULL),
|
||||
fLength(0),
|
||||
fCapacity(0)
|
||||
{
|
||||
}
|
||||
|
||||
~StringBuffer()
|
||||
{
|
||||
free(fBuffer);
|
||||
}
|
||||
|
||||
const char* String() const
|
||||
{
|
||||
return fBuffer != NULL ? fBuffer : "";
|
||||
}
|
||||
|
||||
size_t Length() const
|
||||
{
|
||||
return fLength;
|
||||
}
|
||||
|
||||
bool Append(const char* toAppend)
|
||||
{
|
||||
return Append(toAppend, strlen(toAppend));
|
||||
}
|
||||
|
||||
bool Append(const char* toAppend, size_t length)
|
||||
{
|
||||
size_t oldLength = fLength;
|
||||
if (!_Resize(fLength + length))
|
||||
return false;
|
||||
|
||||
memcpy(fBuffer + oldLength, toAppend, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _Resize(size_t newLength)
|
||||
{
|
||||
if (newLength >= fCapacity) {
|
||||
size_t newCapacity = std::max(fCapacity, size_t(32));
|
||||
while (newLength >= newCapacity)
|
||||
newCapacity *= 2;
|
||||
|
||||
char* buffer = (char*)realloc(fBuffer, newCapacity);
|
||||
if (buffer == NULL)
|
||||
return false;
|
||||
|
||||
fBuffer = buffer;
|
||||
fCapacity = newCapacity;
|
||||
}
|
||||
|
||||
fBuffer[newLength] = '\0';
|
||||
fLength = newLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
char* fBuffer;
|
||||
size_t fLength;
|
||||
size_t fCapacity;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static StringBuffer sSafeModeOptionsBuffer;
|
||||
|
||||
|
||||
static MenuItem*
|
||||
get_continue_booting_menu_item()
|
||||
{
|
||||
// It's the last item in the main menu.
|
||||
if (sMainMenu == NULL || sMainMenu->CountItems() == 0)
|
||||
return NULL;
|
||||
return sMainMenu->ItemAt(sMainMenu->CountItems() - 1);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
user_menu_boot_volume(Menu* menu, MenuItem* item)
|
||||
{
|
||||
Menu* super = menu->Supermenu();
|
||||
if (super == NULL) {
|
||||
MenuItem* bootItem = get_continue_booting_menu_item();
|
||||
if (bootItem == NULL) {
|
||||
// huh?
|
||||
return true;
|
||||
}
|
||||
|
||||
MenuItem *bootItem = super->ItemAt(super->CountItems() - 1);
|
||||
bootItem->SetEnabled(true);
|
||||
bootItem->Select(true);
|
||||
bootItem->SetData(item->Data());
|
||||
if (sBootVolume->IsValid() && sBootVolume->RootDirectory() == item->Data())
|
||||
return true;
|
||||
|
||||
sPathBlacklist->MakeEmpty();
|
||||
|
||||
bool valid = sBootVolume->SetTo((Directory*)item->Data()) == B_OK;
|
||||
|
||||
bootItem->SetEnabled(valid);
|
||||
if (valid)
|
||||
bootItem->Select(true);
|
||||
|
||||
gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
|
||||
return true;
|
||||
@ -653,10 +979,10 @@ debug_menu_add_advanced_option(Menu* menu, MenuItem* item)
|
||||
|
||||
if (size > 0) {
|
||||
buffer[size] = '\n';
|
||||
size_t pos = strlen(sSafeModeOptionsBuffer);
|
||||
if (pos + size + 1 < sizeof(sSafeModeOptionsBuffer))
|
||||
strlcat(sSafeModeOptionsBuffer, buffer,
|
||||
sizeof(sSafeModeOptionsBuffer));
|
||||
if (!sSafeModeOptionsBuffer.Append(buffer)) {
|
||||
dprintf("debug_menu_add_advanced_option(): failed to append option "
|
||||
"to buffer\n");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -699,7 +1025,7 @@ add_boot_volume_menu(Directory* bootVolume)
|
||||
Directory* volume;
|
||||
while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
|
||||
// only list bootable volumes
|
||||
if (!is_bootable(volume))
|
||||
if (volume != bootVolume && !is_bootable(volume))
|
||||
continue;
|
||||
|
||||
char name[B_FILE_NAME_LENGTH];
|
||||
@ -793,6 +1119,13 @@ add_safe_mode_menu()
|
||||
|
||||
platform_add_menus(safeMenu);
|
||||
|
||||
safeMenu->AddSeparatorItem();
|
||||
sBlacklistRootMenu = new(std::nothrow) BlacklistRootMenu;
|
||||
safeMenu->AddItem(item = new(std::nothrow) MenuItem("Blacklist entries",
|
||||
sBlacklistRootMenu));
|
||||
item->SetHelpText("Allows to select system files that shall be ignored. "
|
||||
"Useful e.g. to disable drivers temporarily.");
|
||||
|
||||
safeMenu->AddSeparatorItem();
|
||||
safeMenu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
|
||||
|
||||
@ -962,18 +1295,43 @@ add_debug_menu()
|
||||
static void
|
||||
apply_safe_mode_options(Menu* menu)
|
||||
{
|
||||
int32 pos = strlen(sSafeModeOptionsBuffer);
|
||||
size_t bufferSize = sizeof(sSafeModeOptionsBuffer);
|
||||
|
||||
MenuItemIterator iterator = menu->ItemIterator();
|
||||
while (MenuItem* item = iterator.Next()) {
|
||||
if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked()
|
||||
|| item->Data() == NULL || (uint32)pos >= bufferSize)
|
||||
|| item->Data() == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t totalBytes = snprintf(sSafeModeOptionsBuffer + pos,
|
||||
bufferSize - pos, "%s true\n", (const char*)item->Data());
|
||||
pos += std::min(totalBytes, bufferSize - pos - 1);
|
||||
char buffer[256];
|
||||
if (snprintf(buffer, sizeof(buffer), "%s true\n",
|
||||
(const char*)item->Data()) >= (int)sizeof(buffer)
|
||||
|| !sSafeModeOptionsBuffer.Append(buffer)) {
|
||||
dprintf("apply_safe_mode_options(): failed to append option to "
|
||||
"buffer\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
apply_safe_mode_path_blacklist()
|
||||
{
|
||||
if (sPathBlacklist->IsEmpty())
|
||||
return;
|
||||
|
||||
bool success = sSafeModeOptionsBuffer.Append("EntryBlacklist {\n");
|
||||
|
||||
for (PathBlacklist::Iterator it = sPathBlacklist->GetIterator();
|
||||
BlacklistedPath* path = it.Next();) {
|
||||
success &= sSafeModeOptionsBuffer.Append(path->Path());
|
||||
success &= sSafeModeOptionsBuffer.Append("\n", 1);
|
||||
}
|
||||
|
||||
success &= sSafeModeOptionsBuffer.Append("}\n");
|
||||
|
||||
if (!success) {
|
||||
dprintf("apply_safe_mode_options(): failed to append path "
|
||||
"blacklist to buffer\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -987,17 +1345,21 @@ user_menu_reboot(Menu* menu, MenuItem* item)
|
||||
|
||||
|
||||
status_t
|
||||
user_menu(BootVolume& _bootVolume)
|
||||
user_menu(BootVolume& _bootVolume, PathBlacklist& _pathBlacklist)
|
||||
{
|
||||
|
||||
Menu* menu = new(std::nothrow) Menu(MAIN_MENU);
|
||||
|
||||
sMainMenu = menu;
|
||||
sBootVolume = &_bootVolume;
|
||||
sPathBlacklist = &_pathBlacklist;
|
||||
|
||||
Menu* safeModeMenu = NULL;
|
||||
Menu* debugMenu = NULL;
|
||||
MenuItem* item;
|
||||
|
||||
TRACE(("user_menu: enter\n"));
|
||||
|
||||
memset(sSafeModeOptionsBuffer, 0, sizeof(sSafeModeOptionsBuffer));
|
||||
|
||||
// Add boot volume
|
||||
menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume",
|
||||
add_boot_volume_menu(_bootVolume.RootDirectory())));
|
||||
@ -1028,18 +1390,18 @@ user_menu(BootVolume& _bootVolume)
|
||||
|
||||
menu->Run();
|
||||
|
||||
// See if a new boot device has been selected, and propagate that back
|
||||
if (item->Data() != NULL)
|
||||
_bootVolume.SetTo((Directory*)item->Data());
|
||||
|
||||
apply_safe_mode_options(safeModeMenu);
|
||||
apply_safe_mode_options(debugMenu);
|
||||
add_safe_mode_settings(sSafeModeOptionsBuffer);
|
||||
apply_safe_mode_path_blacklist();
|
||||
add_safe_mode_settings(sSafeModeOptionsBuffer.String());
|
||||
delete menu;
|
||||
|
||||
|
||||
TRACE(("user_menu: leave\n"));
|
||||
|
||||
sMainMenu = NULL;
|
||||
sBlacklistRootMenu = NULL;
|
||||
sBootVolume = NULL;
|
||||
sPathBlacklist = NULL;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,11 @@
|
||||
#include <boot/vfs.h>
|
||||
|
||||
|
||||
extern status_t user_menu(BootVolume& _bootVolume);
|
||||
class PathBlacklist;
|
||||
|
||||
|
||||
extern status_t user_menu(BootVolume& _bootVolume,
|
||||
PathBlacklist& _pathBlacklist);
|
||||
|
||||
|
||||
#endif /* MENU_H */
|
||||
|
Loading…
Reference in New Issue
Block a user