hrev46394 adds 5 changesets to branch 'master' old head: 658491b8984a4e31607c9cebeaca0fdec77d50e2 new head: c04f3a625afa73d870e755e27e1ebdf9ea6c8038 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=c04f3a6+%5E658491b ---------------------------------------------------------------------------- 7e7f482: SinglyLinkedList: Missing include 435fb01: DoublyLinkedList: Add Sort() 6c7abe9: boot loader: Menu[Item] API improvements * Make Menu and MenuItem polymorphic. * MenuItem: - Make SetMarked() virtual, so it can be overridden. - Add SetSubmenu() and Supermenu(). - Delete the submenu in the destructor. * Menu: - Add Entered()/Exited() hooks. They frame the time the user navigates the menu or any of its submenus. The hooks allow for subclasses populating their item list dynamically. - Add SortItems(). * Update boot loader menu copyright text to include 2013, now that it is over soon. :-) f2620e4: boot loader: add_safe_mode_settings(): Make parameter const c04f3a6: 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. [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 16 files changed, 865 insertions(+), 71 deletions(-) headers/private/kernel/boot/PathBlacklist.h | 72 +++ headers/private/kernel/boot/menu.h | 16 +- headers/private/kernel/util/DoublyLinkedList.h | 29 ++ headers/private/kernel/util/SinglyLinkedList.h | 3 + .../packagefs/volume/PackageSettings.cpp | 60 ++- .../packagefs/volume/PackageSettings.h | 18 +- src/system/boot/loader/Jamfile | 1 + src/system/boot/loader/PathBlacklist.cpp | 180 ++++++++ .../loader/file_systems/packagefs/packagefs.cpp | 67 ++- .../loader/file_systems/packagefs/packagefs.h | 4 + src/system/boot/loader/load_driver_settings.cpp | 2 +- src/system/boot/loader/load_driver_settings.h | 2 +- src/system/boot/loader/main.cpp | 14 +- src/system/boot/loader/menu.cpp | 458 +++++++++++++++++-- src/system/boot/loader/menu.h | 6 +- src/system/boot/platform/generic/text_menu.cpp | 4 +- ############################################################################ Commit: 7e7f482590f37ee66831cb69f993eb42719e7b1c URL: http://cgit.haiku-os.org/haiku/commit/?id=7e7f482 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Wed Nov 20 13:06:32 2013 UTC SinglyLinkedList: Missing include ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/util/SinglyLinkedList.h b/headers/private/kernel/util/SinglyLinkedList.h index ed13417..9598a83 100644 --- a/headers/private/kernel/util/SinglyLinkedList.h +++ b/headers/private/kernel/util/SinglyLinkedList.h @@ -8,6 +8,9 @@ #define KERNEL_UTIL_SINGLY_LINKED_LIST_H +#include <SupportDefs.h> + + #ifdef __cplusplus // SinglyLinkedListLink ############################################################################ Commit: 435fb01509b257991ebedf9eae099e35b8be48f6 URL: http://cgit.haiku-os.org/haiku/commit/?id=435fb01 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Wed Nov 20 14:38:20 2013 UTC DoublyLinkedList: Add Sort() ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/util/DoublyLinkedList.h b/headers/private/kernel/util/DoublyLinkedList.h index 2321689..f796bfa 100644 --- a/headers/private/kernel/util/DoublyLinkedList.h +++ b/headers/private/kernel/util/DoublyLinkedList.h @@ -365,6 +365,10 @@ public: inline int32 Count() const; // O(n)! + template<typename Less> + void Sort(const Less& less); + // O(n^2) + inline Iterator GetIterator() { return Iterator(this); } inline ConstIterator GetIterator() const { return ConstIterator(this); } @@ -645,6 +649,31 @@ DOUBLY_LINKED_LIST_CLASS_NAME::Count() const return count; } + +DOUBLY_LINKED_LIST_TEMPLATE_LIST +template<typename Less> +void +DOUBLY_LINKED_LIST_CLASS_NAME::Sort(const Less& less) +{ + // selection sort + Element* tail = Head(); + while (tail != NULL) { + Element* leastElement = tail; + Element* element = tail; + while ((element = GetNext(element)) != NULL) { + if (less(element, leastElement)) + leastElement = element; + } + + if (leastElement != tail) { + Remove(leastElement); + InsertBefore(tail, leastElement); + } else + tail = GetNext(tail); + } +} + + // sGetLink DOUBLY_LINKED_LIST_TEMPLATE_LIST GetLink DOUBLY_LINKED_LIST_CLASS_NAME::sGetLink; ############################################################################ Commit: 6c7abe982939eb2f37df772014db96ad8b5f440a URL: http://cgit.haiku-os.org/haiku/commit/?id=6c7abe9 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Wed Nov 20 14:48:35 2013 UTC boot loader: Menu[Item] API improvements * Make Menu and MenuItem polymorphic. * MenuItem: - Make SetMarked() virtual, so it can be overridden. - Add SetSubmenu() and Supermenu(). - Delete the submenu in the destructor. * Menu: - Add Entered()/Exited() hooks. They frame the time the user navigates the menu or any of its submenus. The hooks allow for subclasses populating their item list dynamically. - Add SortItems(). * Update boot loader menu copyright text to include 2013, now that it is over soon. :-) ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/boot/menu.h b/headers/private/kernel/boot/menu.h index 6b7beb3..dc07cb3 100644 --- a/headers/private/kernel/boot/menu.h +++ b/headers/private/kernel/boot/menu.h @@ -30,12 +30,12 @@ class MenuItem : public DoublyLinkedListLinkImpl<MenuItem> { public: MenuItem(const char* label = NULL, Menu* subMenu = NULL); - ~MenuItem(); + virtual ~MenuItem(); void SetTarget(menu_item_hook target); menu_item_hook Target() const { return fTarget; } - void SetMarked(bool marked); + virtual void SetMarked(bool marked); bool IsMarked() const { return fIsMarked; } void Select(bool selected); @@ -57,7 +57,11 @@ public: char Shortcut() const { return fShortcut; } const char* Label() const { return fLabel; } + Menu* Submenu() const { return fSubMenu; } + void SetSubmenu(Menu* subMenu); + + Menu* Supermenu() const { return fMenu; } private: friend class Menu; @@ -93,10 +97,13 @@ enum menu_type { class Menu { public: Menu(menu_type type, const char* title = NULL); - ~Menu(); + virtual ~Menu(); menu_type Type() const { return fType; } + virtual void Entered(); + virtual void Exited(); + void Hide() { fIsHidden = true; } void Show() { fIsHidden = false; } bool IsHidden() const { return fIsHidden; } @@ -131,6 +138,9 @@ public: shortcut_hook FindShortcut(char key) const; MenuItem* FindItemByShortcut(char key); + void SortItems(bool (*less)(const MenuItem*, + const MenuItem*)); + void Run(); private: diff --git a/src/system/boot/loader/menu.cpp b/src/system/boot/loader/menu.cpp index 38241b6..58e2170 100644 --- a/src/system/boot/loader/menu.cpp +++ b/src/system/boot/loader/menu.cpp @@ -51,21 +51,18 @@ MenuItem::MenuItem(const char *label, Menu *subMenu) fIsEnabled(true), fType(MENU_ITEM_STANDARD), fMenu(NULL), - fSubMenu(subMenu), + fSubMenu(NULL), fData(NULL), fHelpText(NULL), fShortcut(0) { - if (subMenu != NULL) - subMenu->fSuperItem = this; + SetSubmenu(subMenu); } MenuItem::~MenuItem() { - if (fSubMenu != NULL) - fSubMenu->fSuperItem = NULL; - + delete fSubMenu; free(const_cast<char *>(fLabel)); } @@ -176,6 +173,16 @@ MenuItem::SetShortcut(char key) void +MenuItem::SetSubmenu(Menu* subMenu) +{ + fSubMenu = subMenu; + + if (fSubMenu != NULL) + fSubMenu->fSuperItem = this; +} + + +void MenuItem::SetMenu(Menu* menu) { fMenu = menu; @@ -210,6 +217,18 @@ Menu::~Menu() } +void +Menu::Entered() +{ +} + + +void +Menu::Exited() +{ +} + + MenuItem* Menu::ItemAt(int32 index) { @@ -398,6 +417,13 @@ Menu::FindItemByShortcut(char key) void +Menu::SortItems(bool (*less)(const MenuItem*, const MenuItem*)) +{ + fItems.Sort(less); +} + + +void Menu::Run() { platform_run_menu(this); diff --git a/src/system/boot/platform/generic/text_menu.cpp b/src/system/boot/platform/generic/text_menu.cpp index 502db27..605f8da 100644 --- a/src/system/boot/platform/generic/text_menu.cpp +++ b/src/system/boot/platform/generic/text_menu.cpp @@ -209,7 +209,7 @@ draw_menu(Menu *menu) print_centered(2, "Haiku Boot Loader"); console_set_color(kCopyrightColor, kBackgroundColor); - print_centered(4, "Copyright 2004-2012 Haiku Inc."); + print_centered(4, "Copyright 2004-2013 Haiku, Inc."); if (menu->Title()) { console_set_cursor(kOffsetX, kFirstLine - 2); @@ -390,6 +390,7 @@ static void run_menu(Menu* menu) { sMenuOffset = 0; + menu->Entered(); menu->Show(); draw_menu(menu); @@ -481,6 +482,7 @@ run_menu(Menu* menu) } menu->Hide(); + menu->Exited(); } ############################################################################ Commit: f2620e47140215edf4100aeab02e99688744cf26 URL: http://cgit.haiku-os.org/haiku/commit/?id=f2620e4 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Wed Nov 20 14:49:04 2013 UTC boot loader: add_safe_mode_settings(): Make parameter const ---------------------------------------------------------------------------- diff --git a/src/system/boot/loader/load_driver_settings.cpp b/src/system/boot/loader/load_driver_settings.cpp index cec73ca..65aca1d 100644 --- a/src/system/boot/loader/load_driver_settings.cpp +++ b/src/system/boot/loader/load_driver_settings.cpp @@ -125,7 +125,7 @@ add_stage2_driver_settings(stage2_args* args) status_t -add_safe_mode_settings(char* settings) +add_safe_mode_settings(const char* settings) { if (settings == NULL || settings[0] == '\0') return B_OK; diff --git a/src/system/boot/loader/load_driver_settings.h b/src/system/boot/loader/load_driver_settings.h index a5ad2f8..02ae17c 100644 --- a/src/system/boot/loader/load_driver_settings.h +++ b/src/system/boot/loader/load_driver_settings.h @@ -9,7 +9,7 @@ #include <boot/vfs.h> -extern status_t add_safe_mode_settings(char *buffer); +extern status_t add_safe_mode_settings(const char *buffer); extern status_t add_stage2_driver_settings(stage2_args *args); extern status_t load_driver_settings(stage2_args *args, Directory *volume); ############################################################################ Revision: hrev46394 Commit: c04f3a625afa73d870e755e27e1ebdf9ea6c8038 URL: http://cgit.haiku-os.org/haiku/commit/?id=c04f3a6 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Wed Nov 20 14:59:46 2013 UTC 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. ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/boot/PathBlacklist.h b/headers/private/kernel/boot/PathBlacklist.h new file mode 100644 index 0000000..851a785 --- /dev/null +++ b/headers/private/kernel/boot/PathBlacklist.h @@ -0,0 +1,72 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * 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 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 1868b7b..54b2984 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 a1654e6..eea56e1 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 <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; diff --git a/src/system/boot/loader/Jamfile b/src/system/boot/loader/Jamfile index 1b24a2e..1f2b0bd 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 0000000..82822cc --- /dev/null +++ b/src/system/boot/loader/PathBlacklist.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * 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; +} diff --git a/src/system/boot/loader/file_systems/packagefs/packagefs.cpp b/src/system/boot/loader/file_systems/packagefs/packagefs.cpp index 53d75ad..5223662 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 <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,19 +231,47 @@ struct PackageDirectory : PackageNode { if (strcmp(name, "..") == 0) return fParentDirectory; + return _LookupChild(name, strlen(name)); + } + + 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 (strcmp(child->Name(), name) == 0) + if (strncmp(child->Name(), name, nameLength) == 0 + && child->Name()[nameLength] == '\0') { return child; + } } return NULL; } private: - typedef DoublyLinkedList<PackageNode> NodeList; - -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()); + } +} + diff --git a/src/system/boot/loader/file_systems/packagefs/packagefs.h b/src/system/boot/loader/file_systems/packagefs/packagefs.h index 6c31dbd..be51060 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 <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 diff --git a/src/system/boot/loader/main.cpp b/src/system/boot/loader/main.cpp index 004c576..c0b8f31 100644 --- a/src/system/boot/loader/main.cpp +++ b/src/system/boot/loader/main.cpp @@ -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) diff --git a/src/system/boot/loader/menu.cpp b/src/system/boot/loader/menu.cpp index 58e2170..5b55168 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@xxxxxxxxxxxxxxxx. * Copyright 2011, Rene Gollent, rene@xxxxxxxxxxx. + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. * 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]; @@ -794,6 +1120,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")); return safeMenu; @@ -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 a400952..c10cf61 100644 --- a/src/system/boot/loader/menu.h +++ b/src/system/boot/loader/menu.h @@ -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 */