diff --git a/headers/private/kernel/boot/PathBlacklist.h b/headers/private/kernel/boot/PathBlacklist.h new file mode 100644 index 0000000000..851a785ca5 --- /dev/null +++ b/headers/private/kernel/boot/PathBlacklist.h @@ -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 + +#include + + +class BlacklistedPath : public SinglyLinkedListLinkImpl { +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::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 PathList; + +private: + PathList fPaths; +}; + + +#endif // KERNEL_BOOT_PATH_BLACKLIST_H diff --git a/src/add-ons/kernel/file_systems/packagefs/volume/PackageSettings.cpp b/src/add-ons/kernel/file_systems/packagefs/volume/PackageSettings.cpp index 1868b7bd2b..54b2984887 100644 --- a/src/add-ons/kernel/file_systems/packagefs/volume/PackageSettings.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/volume/PackageSettings.cpp @@ -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); } diff --git a/src/add-ons/kernel/file_systems/packagefs/volume/PackageSettings.h b/src/add-ons/kernel/file_systems/packagefs/volume/PackageSettings.h index a1654e6487..eea56e1271 100644 --- a/src/add-ons/kernel/file_systems/packagefs/volume/PackageSettings.h +++ b/src/add-ons/kernel/file_systems/packagefs/volume/PackageSettings.h @@ -9,7 +9,7 @@ #include #include -#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; diff --git a/src/system/boot/loader/Jamfile b/src/system/boot/loader/Jamfile index 1b24a2edc6..1f2b0bded4 100644 --- a/src/system/boot/loader/Jamfile +++ b/src/system/boot/loader/Jamfile @@ -56,6 +56,7 @@ UsePrivateHeaders shared storage ; BootStaticLibrary boot_loader : + PathBlacklist.cpp elf.cpp heap.cpp kernel_args.cpp diff --git a/src/system/boot/loader/PathBlacklist.cpp b/src/system/boot/loader/PathBlacklist.cpp new file mode 100644 index 0000000000..82822ccf86 --- /dev/null +++ b/src/system/boot/loader/PathBlacklist.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include + + +// #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; +} diff --git a/src/system/boot/loader/file_systems/packagefs/packagefs.cpp b/src/system/boot/loader/file_systems/packagefs/packagefs.cpp index 53d75ad5be..5223662c80 100644 --- a/src/system/boot/loader/file_systems/packagefs/packagefs.cpp +++ b/src/system/boot/loader/file_systems/packagefs/packagefs.cpp @@ -23,6 +23,7 @@ #include +#include #include #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 { 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 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(systemDirectory); + + for (PathBlacklist::Iterator it = pathBlacklist.GetIterator(); + BlacklistedPath* path = it.Next();) { + directory->RemoveEntry(path->Path()); + } +} + diff --git a/src/system/boot/loader/file_systems/packagefs/packagefs.h b/src/system/boot/loader/file_systems/packagefs/packagefs.h index 6c31dbd0ec..be51060c68 100644 --- a/src/system/boot/loader/file_systems/packagefs/packagefs.h +++ b/src/system/boot/loader/file_systems/packagefs/packagefs.h @@ -11,6 +11,7 @@ #include +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 diff --git a/src/system/boot/loader/main.cpp b/src/system/boot/loader/main.cpp index 004c576f7b..c0b8f31490 100644 --- a/src/system/boot/loader/main.cpp +++ b/src/system/boot/loader/main.cpp @@ -12,8 +12,11 @@ #include #include #include +#include #include +#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) diff --git a/src/system/boot/loader/menu.cpp b/src/system/boot/loader/menu.cpp index 58e2170965..5b55168230 100644 --- a/src/system/boot/loader/menu.cpp +++ b/src/system/boot/loader/menu.cpp @@ -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 +#include #include +#include #include #include #include @@ -22,6 +25,7 @@ #include #include #include +#include #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(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(a); + const BlacklistMenuItem* item2 + = static_cast(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(node)); + + } + ObjectDeleter 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; } - diff --git a/src/system/boot/loader/menu.h b/src/system/boot/loader/menu.h index a400952fd1..c10cf61da3 100644 --- a/src/system/boot/loader/menu.h +++ b/src/system/boot/loader/menu.h @@ -9,7 +9,11 @@ #include -extern status_t user_menu(BootVolume& _bootVolume); +class PathBlacklist; + + +extern status_t user_menu(BootVolume& _bootVolume, + PathBlacklist& _pathBlacklist); #endif /* MENU_H */