Boot loader: Add support for choosing an old packages state
For potential boot volumes with older packages states the respective item in the boot volume menu now has a sub menu for selecting a state. The boot loader functionality for this feature is complete -- i.e. the respective kernel is loaded and the name of the old state is added to the kernel args -- but kernel packagefs and package daemon support is still missing.
This commit is contained in:
@@ -29,6 +29,7 @@
|
|||||||
#define BOOT_VOLUME_USER_SELECTED "user selected"
|
#define BOOT_VOLUME_USER_SELECTED "user selected"
|
||||||
#define BOOT_VOLUME_BOOTED_FROM_IMAGE "booted from image"
|
#define BOOT_VOLUME_BOOTED_FROM_IMAGE "booted from image"
|
||||||
#define BOOT_VOLUME_PACKAGED "packaged"
|
#define BOOT_VOLUME_PACKAGED "packaged"
|
||||||
|
#define BOOT_VOLUME_PACKAGES_STATE "packages state"
|
||||||
#define BOOT_VOLUME_PARTITION_OFFSET "partition offset"
|
#define BOOT_VOLUME_PARTITION_OFFSET "partition offset"
|
||||||
#define BOOT_VOLUME_DISK_IDENTIFIER "disk identifier"
|
#define BOOT_VOLUME_DISK_IDENTIFIER "disk identifier"
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
struct file_map_run;
|
struct file_map_run;
|
||||||
struct stat;
|
struct stat;
|
||||||
|
class PackageVolumeInfo;
|
||||||
|
class PackageVolumeState;
|
||||||
|
|
||||||
/** This is the base class for all VFS nodes */
|
/** This is the base class for all VFS nodes */
|
||||||
|
|
||||||
@@ -114,7 +116,11 @@ public:
|
|||||||
BootVolume();
|
BootVolume();
|
||||||
~BootVolume();
|
~BootVolume();
|
||||||
|
|
||||||
status_t SetTo(Directory* rootDirectory);
|
status_t SetTo(Directory* rootDirectory,
|
||||||
|
PackageVolumeInfo* packageVolumeInfo
|
||||||
|
= NULL,
|
||||||
|
PackageVolumeState* packageVolumeState
|
||||||
|
= NULL);
|
||||||
void Unset();
|
void Unset();
|
||||||
|
|
||||||
bool IsValid() const
|
bool IsValid() const
|
||||||
@@ -125,9 +131,16 @@ public:
|
|||||||
Directory* SystemDirectory() const
|
Directory* SystemDirectory() const
|
||||||
{ return fSystemDirectory; }
|
{ return fSystemDirectory; }
|
||||||
bool IsPackaged() const
|
bool IsPackaged() const
|
||||||
{ return fPackaged; }
|
{ return fPackageVolumeInfo != NULL; }
|
||||||
|
PackageVolumeInfo* GetPackageVolumeInfo() const
|
||||||
|
{ return fPackageVolumeInfo; }
|
||||||
|
PackageVolumeState* GetPackageVolumeState() const
|
||||||
|
{ return fPackageVolumeState; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
status_t _SetTo(Directory* rootDirectory,
|
||||||
|
PackageVolumeInfo* packageVolumeInfo,
|
||||||
|
PackageVolumeState* packageVolumeState);
|
||||||
int _OpenSystemPackage();
|
int _OpenSystemPackage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -136,8 +149,8 @@ private:
|
|||||||
Directory* fSystemDirectory;
|
Directory* fSystemDirectory;
|
||||||
// "system" directory of the volume; if packaged the root
|
// "system" directory of the volume; if packaged the root
|
||||||
// directory of the mounted packagefs
|
// directory of the mounted packagefs
|
||||||
bool fPackaged;
|
PackageVolumeInfo* fPackageVolumeInfo;
|
||||||
// indicates whether the boot volume's system is packaged
|
PackageVolumeState* fPackageVolumeState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ BootStaticLibrary boot_loader :
|
|||||||
loader.cpp
|
loader.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
menu.cpp
|
menu.cpp
|
||||||
|
package_support.cpp
|
||||||
pager.cpp
|
pager.cpp
|
||||||
partitions.cpp
|
partitions.cpp
|
||||||
RootFileSystem.cpp
|
RootFileSystem.cpp
|
||||||
|
|||||||
+170
-12
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2013, Axel Dörfler, [email protected].
|
* Copyright 2003-2013, Axel Dörfler, [email protected].
|
||||||
* Copyright 2011, Rene Gollent, [email protected].
|
* Copyright 2011, Rene Gollent, [email protected].
|
||||||
* Copyright 2013, Ingo Weinhold, [email protected].
|
* Copyright 2013-2014, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "load_driver_settings.h"
|
#include "load_driver_settings.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
#include "package_support.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "RootFileSystem.h"
|
#include "RootFileSystem.h"
|
||||||
|
|
||||||
@@ -764,6 +765,77 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - boot volume menu
|
||||||
|
|
||||||
|
|
||||||
|
class BootVolumeMenuItem : public MenuItem {
|
||||||
|
public:
|
||||||
|
BootVolumeMenuItem(const char* volumeName)
|
||||||
|
:
|
||||||
|
MenuItem(volumeName),
|
||||||
|
fStateChoiceText(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~BootVolumeMenuItem()
|
||||||
|
{
|
||||||
|
UpdateStateName(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateStateName(PackageVolumeState* volumeState)
|
||||||
|
{
|
||||||
|
free(fStateChoiceText);
|
||||||
|
fStateChoiceText = NULL;
|
||||||
|
|
||||||
|
if (volumeState != NULL && volumeState->Name() != NULL) {
|
||||||
|
char nameBuffer[128];
|
||||||
|
snprintf(nameBuffer, sizeof(nameBuffer), "%s (%s)", Label(),
|
||||||
|
volumeState->DisplayName());
|
||||||
|
fStateChoiceText = strdup(nameBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Supermenu()->SetChoiceText(
|
||||||
|
fStateChoiceText != NULL ? fStateChoiceText : Label());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* fStateChoiceText;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PackageVolumeStateMenuItem : public MenuItem {
|
||||||
|
public:
|
||||||
|
PackageVolumeStateMenuItem(const char* label, PackageVolumeInfo* volumeInfo,
|
||||||
|
PackageVolumeState* volumeState)
|
||||||
|
:
|
||||||
|
MenuItem(label),
|
||||||
|
fVolumeInfo(volumeInfo),
|
||||||
|
fVolumeState(volumeState)
|
||||||
|
{
|
||||||
|
fVolumeInfo->AcquireReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
~PackageVolumeStateMenuItem()
|
||||||
|
{
|
||||||
|
fVolumeInfo->ReleaseReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageVolumeInfo* VolumeInfo() const
|
||||||
|
{
|
||||||
|
return fVolumeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageVolumeState* VolumeState() const
|
||||||
|
{
|
||||||
|
return fVolumeState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PackageVolumeInfo* fVolumeInfo;
|
||||||
|
PackageVolumeState* fVolumeState;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@@ -876,6 +948,42 @@ user_menu_boot_volume(Menu* menu, MenuItem* item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
user_menu_boot_volume_state(Menu* menu, MenuItem* _item)
|
||||||
|
{
|
||||||
|
MenuItem* bootItem = get_continue_booting_menu_item();
|
||||||
|
if (bootItem == NULL) {
|
||||||
|
// huh?
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageVolumeStateMenuItem* item = static_cast<PackageVolumeStateMenuItem*>(
|
||||||
|
_item);
|
||||||
|
if (sBootVolume->IsValid() && sBootVolume->GetPackageVolumeState() != NULL
|
||||||
|
&& sBootVolume->GetPackageVolumeState() == item->VolumeState()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BootVolumeMenuItem* volumeItem = static_cast<BootVolumeMenuItem*>(
|
||||||
|
item->Supermenu()->Superitem());
|
||||||
|
volumeItem->SetMarked(true);
|
||||||
|
volumeItem->Select(true);
|
||||||
|
volumeItem->UpdateStateName(item->VolumeState());
|
||||||
|
|
||||||
|
sPathBlacklist->MakeEmpty();
|
||||||
|
|
||||||
|
bool valid = sBootVolume->SetTo((Directory*)item->Data(),
|
||||||
|
item->VolumeInfo(), item->VolumeState()) == B_OK;
|
||||||
|
|
||||||
|
bootItem->SetEnabled(valid);
|
||||||
|
if (valid)
|
||||||
|
bootItem->Select(true);
|
||||||
|
|
||||||
|
gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
debug_menu_display_current_log(Menu* menu, MenuItem* item)
|
debug_menu_display_current_log(Menu* menu, MenuItem* item)
|
||||||
{
|
{
|
||||||
@@ -1060,8 +1168,65 @@ debug_menu_save_previous_syslog(Menu* menu, MenuItem* item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_boot_volume_item(Menu* menu, Directory* volume, const char* name)
|
||||||
|
{
|
||||||
|
BReference<PackageVolumeInfo> volumeInfo;
|
||||||
|
PackageVolumeState* selectedState = NULL;
|
||||||
|
if (volume == sBootVolume->RootDirectory()) {
|
||||||
|
volumeInfo.SetTo(sBootVolume->GetPackageVolumeInfo());
|
||||||
|
selectedState = sBootVolume->GetPackageVolumeState();
|
||||||
|
} else {
|
||||||
|
volumeInfo.SetTo(new(std::nothrow) PackageVolumeInfo);
|
||||||
|
if (volumeInfo->SetTo(volume, "system/packages") == B_OK)
|
||||||
|
selectedState = volumeInfo->States().Head();
|
||||||
|
else
|
||||||
|
volumeInfo.Unset();
|
||||||
|
}
|
||||||
|
|
||||||
|
BootVolumeMenuItem* item = new(nothrow) BootVolumeMenuItem(name);
|
||||||
|
menu->AddItem(item);
|
||||||
|
|
||||||
|
Menu* subMenu = NULL;
|
||||||
|
|
||||||
|
if (volumeInfo != NULL) {
|
||||||
|
subMenu = new(std::nothrow) Menu(CHOICE_MENU, "Select Haiku version");
|
||||||
|
|
||||||
|
for (PackageVolumeStateList::ConstIterator it
|
||||||
|
= volumeInfo->States().GetIterator();
|
||||||
|
PackageVolumeState* state = it.Next();) {
|
||||||
|
PackageVolumeStateMenuItem* stateItem
|
||||||
|
= new(nothrow) PackageVolumeStateMenuItem(state->DisplayName(),
|
||||||
|
volumeInfo, state);
|
||||||
|
subMenu->AddItem(stateItem);
|
||||||
|
stateItem->SetTarget(user_menu_boot_volume_state);
|
||||||
|
stateItem->SetData(volume);
|
||||||
|
|
||||||
|
if (state == selectedState) {
|
||||||
|
stateItem->SetMarked(true);
|
||||||
|
stateItem->Select(true);
|
||||||
|
item->UpdateStateName(stateItem->VolumeState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subMenu != NULL && subMenu->CountItems() > 1) {
|
||||||
|
item->SetSubmenu(subMenu);
|
||||||
|
} else {
|
||||||
|
delete subMenu;
|
||||||
|
item->SetTarget(user_menu_boot_volume);
|
||||||
|
item->SetData(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volume == sBootVolume->RootDirectory()) {
|
||||||
|
item->SetMarked(true);
|
||||||
|
item->Select(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static Menu*
|
static Menu*
|
||||||
add_boot_volume_menu(Directory* bootVolume)
|
add_boot_volume_menu()
|
||||||
{
|
{
|
||||||
Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume");
|
Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume");
|
||||||
MenuItem* item;
|
MenuItem* item;
|
||||||
@@ -1072,19 +1237,12 @@ add_boot_volume_menu(Directory* bootVolume)
|
|||||||
Directory* volume;
|
Directory* volume;
|
||||||
while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
|
while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
|
||||||
// only list bootable volumes
|
// only list bootable volumes
|
||||||
if (volume != bootVolume && !is_bootable(volume))
|
if (volume != sBootVolume->RootDirectory() && !is_bootable(volume))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
char name[B_FILE_NAME_LENGTH];
|
char name[B_FILE_NAME_LENGTH];
|
||||||
if (volume->GetName(name, sizeof(name)) == B_OK) {
|
if (volume->GetName(name, sizeof(name)) == B_OK) {
|
||||||
menu->AddItem(item = new(nothrow) MenuItem(name));
|
add_boot_volume_item(menu, volume, name);
|
||||||
item->SetTarget(user_menu_boot_volume);
|
|
||||||
item->SetData(volume);
|
|
||||||
|
|
||||||
if (volume == bootVolume) {
|
|
||||||
item->SetMarked(true);
|
|
||||||
item->Select(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
@@ -1425,7 +1583,7 @@ user_menu(BootVolume& _bootVolume, PathBlacklist& _pathBlacklist)
|
|||||||
|
|
||||||
// Add boot volume
|
// Add boot volume
|
||||||
menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume",
|
menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume",
|
||||||
add_boot_volume_menu(_bootVolume.RootDirectory())));
|
add_boot_volume_menu()));
|
||||||
|
|
||||||
// Add safe mode
|
// Add safe mode
|
||||||
menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
|
menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
|
||||||
|
|||||||
@@ -0,0 +1,351 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "package_support.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <AutoDeleter.h>
|
||||||
|
#include <boot/vfs.h>
|
||||||
|
#include <package/PackagesDirectoryDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TRACE_PACKAGE_SUPPORT
|
||||||
|
#ifdef TRACE_PACKAGE_SUPPORT
|
||||||
|
# define TRACE(...) dprintf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define TRACE(...) do {} while (false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char* const kAdministrativeDirectory
|
||||||
|
= PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
|
||||||
|
static const char* const kActivatedPackagesFile
|
||||||
|
= PACKAGES_DIRECTORY_ACTIVATION_FILE;
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_system_package(const char* name)
|
||||||
|
{
|
||||||
|
// The name must end with ".hpkg".
|
||||||
|
size_t nameLength = strlen(name);
|
||||||
|
if (nameLength < 6 || strcmp(name + nameLength - 5, ".hpkg") != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The name must either be "haiku.hpkg" or start with "haiku-".
|
||||||
|
return strcmp(name, "haiku.hpkg") == 0 || strncmp(name, "haiku-", 6) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - PackageVolumeState
|
||||||
|
|
||||||
|
|
||||||
|
PackageVolumeState::PackageVolumeState()
|
||||||
|
:
|
||||||
|
fName(NULL),
|
||||||
|
fDisplayName(NULL),
|
||||||
|
fSystemPackage(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PackageVolumeState::~PackageVolumeState()
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
PackageVolumeState::SetTo(const char* stateName)
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
|
||||||
|
if (stateName != NULL) {
|
||||||
|
fName = strdup(stateName);
|
||||||
|
if (fName == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// Derive the display name from the directory name: Chop off the leading
|
||||||
|
// "state_" and replace underscores by spaces.
|
||||||
|
fDisplayName = strncmp(stateName, "state_", 6) == 0
|
||||||
|
? strdup(stateName + 6) : strdup(stateName);
|
||||||
|
if (fDisplayName == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
char* remainder = fDisplayName;
|
||||||
|
while (char* underscore = strchr(remainder, '_')) {
|
||||||
|
*underscore = ' ';
|
||||||
|
remainder = underscore + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PackageVolumeState::Unset()
|
||||||
|
{
|
||||||
|
free(fName);
|
||||||
|
fName = NULL;
|
||||||
|
|
||||||
|
free(fDisplayName);
|
||||||
|
fDisplayName = NULL;
|
||||||
|
|
||||||
|
free(fSystemPackage);
|
||||||
|
fSystemPackage = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
PackageVolumeState::DisplayName() const
|
||||||
|
{
|
||||||
|
return fDisplayName != NULL ? fDisplayName : "Latest version";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
PackageVolumeState::SetSystemPackage(const char* package)
|
||||||
|
{
|
||||||
|
if (fSystemPackage != NULL)
|
||||||
|
free(fSystemPackage);
|
||||||
|
|
||||||
|
fSystemPackage = strdup(package);
|
||||||
|
return fSystemPackage != NULL ? B_OK : B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PackageVolumeState::GetPackagePath(const char* name, char* path,
|
||||||
|
size_t pathSize)
|
||||||
|
{
|
||||||
|
if (fName == NULL) {
|
||||||
|
// the current state -- packages are directly in the packages directory
|
||||||
|
strlcpy(path, name, pathSize);
|
||||||
|
} else {
|
||||||
|
// an old state
|
||||||
|
snprintf(path, pathSize, "%s/%s/%s", kAdministrativeDirectory, fName,
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ bool
|
||||||
|
PackageVolumeState::IsNewer(const PackageVolumeState* a,
|
||||||
|
const PackageVolumeState* b)
|
||||||
|
{
|
||||||
|
if (b->fName == NULL)
|
||||||
|
return false;
|
||||||
|
if (a->fName == NULL)
|
||||||
|
return true;
|
||||||
|
return strcmp(a->fName, b->fName) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - PackageVolumeInfo
|
||||||
|
|
||||||
|
|
||||||
|
PackageVolumeInfo::PackageVolumeInfo()
|
||||||
|
:
|
||||||
|
BReferenceable(),
|
||||||
|
fStates()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PackageVolumeInfo::~PackageVolumeInfo()
|
||||||
|
{
|
||||||
|
while (PackageVolumeState* state = fStates.RemoveHead())
|
||||||
|
delete state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
PackageVolumeInfo::SetTo(Directory* baseDirectory, const char* packagesPath)
|
||||||
|
{
|
||||||
|
TRACE("PackageVolumeInfo::SetTo()\n");
|
||||||
|
|
||||||
|
// get the packages directory
|
||||||
|
DIR* dir = open_directory(baseDirectory, packagesPath);
|
||||||
|
if (dir == NULL) {
|
||||||
|
TRACE("PackageVolumeInfo::SetTo(): failed to open packages directory: "
|
||||||
|
"%s\n", strerror(errno));
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
CObjectDeleter<DIR, int> dirCloser(dir, &closedir);
|
||||||
|
|
||||||
|
Directory* packagesDirectory = directory_from(dir);
|
||||||
|
packagesDirectory->Acquire();
|
||||||
|
|
||||||
|
// add the current state
|
||||||
|
PackageVolumeState* state = _AddState(NULL);
|
||||||
|
if (state == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
status_t error = _InitState(packagesDirectory, dir, state);
|
||||||
|
if (error != B_OK) {
|
||||||
|
TRACE("PackageVolumeInfo::SetTo(): failed to init current state: "
|
||||||
|
"%s\n", strerror(error));
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the packages/administrative directory to find old state
|
||||||
|
// directories.
|
||||||
|
if (DIR* administrativeDir = open_directory(packagesDirectory,
|
||||||
|
kAdministrativeDirectory)) {
|
||||||
|
while (dirent* entry = readdir(administrativeDir)) {
|
||||||
|
if (strncmp(entry->d_name, "state_", 6) == 0) {
|
||||||
|
TRACE(" old state directory \"%s\"\n", entry->d_name);
|
||||||
|
_AddState(entry->d_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(administrativeDir);
|
||||||
|
|
||||||
|
fStates.Sort(&PackageVolumeState::IsNewer);
|
||||||
|
|
||||||
|
// initialize the old states
|
||||||
|
for (state = fStates.GetNext(state); state != NULL;) {
|
||||||
|
PackageVolumeState* nextState = fStates.GetNext(state);
|
||||||
|
if (state->Name()) {
|
||||||
|
error = _InitState(packagesDirectory, dir, state);
|
||||||
|
if (error != B_OK) {
|
||||||
|
TRACE("PackageVolumeInfo::SetTo(): failed to init state "
|
||||||
|
"\"%s\": %s\n", state->Name(), strerror(error));
|
||||||
|
fStates.Remove(state);
|
||||||
|
delete state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = nextState;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("PackageVolumeInfo::SetTo(): failed to open administrative "
|
||||||
|
"directory: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PackageVolumeState*
|
||||||
|
PackageVolumeInfo::_AddState(const char* stateName)
|
||||||
|
{
|
||||||
|
PackageVolumeState* state = new(std::nothrow) PackageVolumeState;
|
||||||
|
if (state == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (state->SetTo(stateName) != B_OK) {
|
||||||
|
delete state;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fStates.Add(state);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
PackageVolumeInfo::_InitState(Directory* packagesDirectory, DIR* dir,
|
||||||
|
PackageVolumeState* state)
|
||||||
|
{
|
||||||
|
// find the system package
|
||||||
|
char systemPackageName[B_FILE_NAME_LENGTH];
|
||||||
|
status_t error = _ParseActivatedPackagesFile(packagesDirectory, state,
|
||||||
|
systemPackageName, sizeof(systemPackageName));
|
||||||
|
if (error == B_OK) {
|
||||||
|
// check, if package exists
|
||||||
|
for (PackageVolumeState* otherState = state; otherState != NULL;
|
||||||
|
otherState = fStates.GetPrevious(otherState)) {
|
||||||
|
char packagePath[B_PATH_NAME_LENGTH];
|
||||||
|
otherState->GetPackagePath(systemPackageName, packagePath,
|
||||||
|
sizeof(packagePath));
|
||||||
|
struct stat st;
|
||||||
|
if (get_stat(packagesDirectory, packagePath, st) == B_OK
|
||||||
|
&& S_ISREG(st.st_mode)) {
|
||||||
|
state->SetSystemPackage(packagePath);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("PackageVolumeInfo::_InitState(): failed to parse "
|
||||||
|
"activated-packages: %s\n", strerror(error));
|
||||||
|
|
||||||
|
// No or invalid activated-packages file. That is OK for the current
|
||||||
|
// state. We'll iterate through the packages directory to find the
|
||||||
|
// system package. We don't do that for old states, though.
|
||||||
|
if (state->Name() != NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
while (dirent* entry = readdir(dir)) {
|
||||||
|
// The name must end with ".hpkg".
|
||||||
|
if (is_system_package(entry->d_name)) {
|
||||||
|
state->SetSystemPackage(entry->d_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->SystemPackage() == NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
PackageVolumeInfo::_ParseActivatedPackagesFile(Directory* packagesDirectory,
|
||||||
|
PackageVolumeState* state, char* packageName, size_t packageNameSize)
|
||||||
|
{
|
||||||
|
// open the activated-packages file
|
||||||
|
char path[3 * B_FILE_NAME_LENGTH + 2];
|
||||||
|
snprintf(path, sizeof(path), "%s/%s/%s",
|
||||||
|
kAdministrativeDirectory, state->Name() != NULL ? state->Name() : "",
|
||||||
|
kActivatedPackagesFile);
|
||||||
|
int fd = open_from(packagesDirectory, path, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
return fd;
|
||||||
|
FileDescriptorCloser fdCloser(fd);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) != 0)
|
||||||
|
return errno;
|
||||||
|
if (!S_ISREG(st.st_mode))
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
// read the file until we find the system package line
|
||||||
|
size_t remainingBytes = 0;
|
||||||
|
for (;;) {
|
||||||
|
ssize_t bytesRead = read(fd, path + remainingBytes,
|
||||||
|
sizeof(path) - remainingBytes - 1);
|
||||||
|
if (bytesRead <= 0)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
remainingBytes += bytesRead;
|
||||||
|
path[remainingBytes] = '\0';
|
||||||
|
|
||||||
|
char* line = path;
|
||||||
|
while (char* lineEnd = strchr(line, '\n')) {
|
||||||
|
*lineEnd = '\0';
|
||||||
|
if (is_system_package(line)) {
|
||||||
|
return strlcpy(packageName, line, packageNameSize)
|
||||||
|
< packageNameSize
|
||||||
|
? B_OK : B_NAME_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
line = lineEnd + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move the remainder to the start of the buffer
|
||||||
|
if (line < path + remainingBytes) {
|
||||||
|
size_t left = path + remainingBytes - line;
|
||||||
|
memmove(path, line, left);
|
||||||
|
remainingBytes = left;
|
||||||
|
} else
|
||||||
|
remainingBytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef PACKAGE_SUPPORT_H
|
||||||
|
#define PACKAGE_SUPPORT_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include <Referenceable.h>
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Directory;
|
||||||
|
|
||||||
|
|
||||||
|
class PackageVolumeState : public DoublyLinkedListLinkImpl<PackageVolumeState> {
|
||||||
|
public:
|
||||||
|
PackageVolumeState();
|
||||||
|
~PackageVolumeState();
|
||||||
|
|
||||||
|
status_t SetTo(const char* stateName);
|
||||||
|
void Unset();
|
||||||
|
|
||||||
|
const char* Name() const
|
||||||
|
{ return fName; }
|
||||||
|
const char* DisplayName() const;
|
||||||
|
|
||||||
|
const char* SystemPackage() const
|
||||||
|
{ return fSystemPackage; }
|
||||||
|
status_t SetSystemPackage(const char* package);
|
||||||
|
|
||||||
|
void GetPackagePath(const char* name, char* path,
|
||||||
|
size_t pathSize);
|
||||||
|
|
||||||
|
static bool IsNewer(const PackageVolumeState* a,
|
||||||
|
const PackageVolumeState* b);
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* fName;
|
||||||
|
char* fDisplayName;
|
||||||
|
char* fSystemPackage;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<PackageVolumeState> PackageVolumeStateList;
|
||||||
|
|
||||||
|
|
||||||
|
class PackageVolumeInfo : public BReferenceable {
|
||||||
|
public:
|
||||||
|
typedef PackageVolumeStateList StateList;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PackageVolumeInfo();
|
||||||
|
~PackageVolumeInfo();
|
||||||
|
|
||||||
|
status_t SetTo(Directory* baseDirectory,
|
||||||
|
const char* packagesPath);
|
||||||
|
|
||||||
|
const StateList& States() const
|
||||||
|
{ return fStates; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PackageVolumeState* _AddState(const char* stateName);
|
||||||
|
status_t _InitState(Directory* packagesDirectory,
|
||||||
|
DIR* dir, PackageVolumeState* state);
|
||||||
|
status_t _ParseActivatedPackagesFile(
|
||||||
|
Directory* packagesDirectory,
|
||||||
|
PackageVolumeState* state,
|
||||||
|
char* packageName, size_t packageNameSize);
|
||||||
|
|
||||||
|
private:
|
||||||
|
StateList fStates;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // PACKAGE_SUPPORT_H
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <boot/stage2.h>
|
#include <boot/stage2.h>
|
||||||
#include <syscall_utils.h>
|
#include <syscall_utils.h>
|
||||||
|
|
||||||
|
#include "package_support.h"
|
||||||
#include "RootFileSystem.h"
|
#include "RootFileSystem.h"
|
||||||
#include "file_systems/packagefs/packagefs.h"
|
#include "file_systems/packagefs/packagefs.h"
|
||||||
|
|
||||||
@@ -418,7 +419,8 @@ BootVolume::BootVolume()
|
|||||||
:
|
:
|
||||||
fRootDirectory(NULL),
|
fRootDirectory(NULL),
|
||||||
fSystemDirectory(NULL),
|
fSystemDirectory(NULL),
|
||||||
fPackaged(false)
|
fPackageVolumeInfo(NULL),
|
||||||
|
fPackageVolumeState(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +432,46 @@ BootVolume::~BootVolume()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BootVolume::SetTo(Directory* rootDirectory)
|
BootVolume::SetTo(Directory* rootDirectory,
|
||||||
|
PackageVolumeInfo* packageVolumeInfo,
|
||||||
|
PackageVolumeState* packageVolumeState)
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
|
||||||
|
status_t error = _SetTo(rootDirectory, packageVolumeInfo,
|
||||||
|
packageVolumeState);
|
||||||
|
if (error != B_OK)
|
||||||
|
Unset();
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BootVolume::Unset()
|
||||||
|
{
|
||||||
|
if (fRootDirectory != NULL) {
|
||||||
|
fRootDirectory->Release();
|
||||||
|
fRootDirectory = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fSystemDirectory != NULL) {
|
||||||
|
fSystemDirectory->Release();
|
||||||
|
fSystemDirectory = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fPackageVolumeInfo != NULL) {
|
||||||
|
fPackageVolumeInfo->ReleaseReference();
|
||||||
|
fPackageVolumeInfo = NULL;
|
||||||
|
fPackageVolumeState = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
BootVolume::_SetTo(Directory* rootDirectory,
|
||||||
|
PackageVolumeInfo* packageVolumeInfo,
|
||||||
|
PackageVolumeState* packageVolumeState)
|
||||||
{
|
{
|
||||||
Unset();
|
Unset();
|
||||||
|
|
||||||
@@ -451,13 +492,32 @@ BootVolume::SetTo(Directory* rootDirectory)
|
|||||||
|
|
||||||
fSystemDirectory = static_cast<Directory*>(systemNode);
|
fSystemDirectory = static_cast<Directory*>(systemNode);
|
||||||
|
|
||||||
|
if (packageVolumeInfo == NULL) {
|
||||||
|
// get a package volume info
|
||||||
|
BReference<PackageVolumeInfo> packageVolumeInfoReference(
|
||||||
|
new(std::nothrow) PackageVolumeInfo);
|
||||||
|
status_t error = packageVolumeInfoReference->SetTo(fSystemDirectory,
|
||||||
|
"packages");
|
||||||
|
if (error != B_OK) {
|
||||||
|
// apparently not packaged
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
fPackageVolumeInfo = packageVolumeInfoReference.Detach();
|
||||||
|
} else {
|
||||||
|
fPackageVolumeInfo = packageVolumeInfo;
|
||||||
|
fPackageVolumeInfo->AcquireReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
fPackageVolumeState = packageVolumeState != NULL
|
||||||
|
? packageVolumeState : fPackageVolumeInfo->States().Head();
|
||||||
|
|
||||||
// try opening the system package
|
// try opening the system package
|
||||||
int packageFD = _OpenSystemPackage();
|
int packageFD = _OpenSystemPackage();
|
||||||
fPackaged = packageFD >= 0;
|
if (packageFD < 0)
|
||||||
if (!fPackaged)
|
return packageFD;
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
// the system is packaged -- mount the packagefs
|
// mount packagefs
|
||||||
Directory* packageRootDirectory;
|
Directory* packageRootDirectory;
|
||||||
status_t error = packagefs_mount_file(packageFD, fSystemDirectory,
|
status_t error = packagefs_mount_file(packageFD, fSystemDirectory,
|
||||||
packageRootDirectory);
|
packageRootDirectory);
|
||||||
@@ -474,23 +534,6 @@ BootVolume::SetTo(Directory* rootDirectory)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
BootVolume::Unset()
|
|
||||||
{
|
|
||||||
if (fRootDirectory != NULL) {
|
|
||||||
fRootDirectory->Release();
|
|
||||||
fRootDirectory = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fSystemDirectory != NULL) {
|
|
||||||
fSystemDirectory->Release();
|
|
||||||
fSystemDirectory = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fPackaged = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
BootVolume::_OpenSystemPackage()
|
BootVolume::_OpenSystemPackage()
|
||||||
{
|
{
|
||||||
@@ -505,29 +548,9 @@ BootVolume::_OpenSystemPackage()
|
|||||||
return -1;
|
return -1;
|
||||||
Directory* packageDirectory = (Directory*)packagesNode;
|
Directory* packageDirectory = (Directory*)packagesNode;
|
||||||
|
|
||||||
// search for the system package
|
// open the system package
|
||||||
int fd = -1;
|
return open_from(packageDirectory, fPackageVolumeState->SystemPackage(),
|
||||||
void* cookie;
|
O_RDONLY);
|
||||||
if (packageDirectory->Open(&cookie, O_RDONLY) == B_OK) {
|
|
||||||
char name[B_FILE_NAME_LENGTH];
|
|
||||||
while (packageDirectory->GetNextEntry(cookie, name, sizeof(name))
|
|
||||||
== B_OK) {
|
|
||||||
// The name must end with ".hpkg".
|
|
||||||
size_t nameLength = strlen(name);
|
|
||||||
if (nameLength < 6 || strcmp(name + nameLength - 5, ".hpkg") != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// The name must either be "haiku.hpkg" or start with "haiku-".
|
|
||||||
if (strcmp(name, "haiku.hpkg") == 0
|
|
||||||
|| strncmp(name, "haiku-", 6) == 0) {
|
|
||||||
fd = open_from(packageDirectory, name, O_RDONLY);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
packageDirectory->Close(cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -561,8 +584,13 @@ register_boot_file_system(BootVolume& bootVolume)
|
|||||||
|
|
||||||
gBootVolume.SetInt64(BOOT_VOLUME_PARTITION_OFFSET,
|
gBootVolume.SetInt64(BOOT_VOLUME_PARTITION_OFFSET,
|
||||||
partition->offset);
|
partition->offset);
|
||||||
if (bootVolume.IsPackaged())
|
|
||||||
|
if (bootVolume.IsPackaged()) {
|
||||||
gBootVolume.SetBool(BOOT_VOLUME_PACKAGED, true);
|
gBootVolume.SetBool(BOOT_VOLUME_PACKAGED, true);
|
||||||
|
PackageVolumeState* state = bootVolume.GetPackageVolumeState();
|
||||||
|
if (state->Name() != NULL)
|
||||||
|
gBootVolume.AddString(BOOT_VOLUME_PACKAGES_STATE, state->Name());
|
||||||
|
}
|
||||||
|
|
||||||
Node *device = get_node_from(partition->FD());
|
Node *device = get_node_from(partition->FD());
|
||||||
if (device == NULL) {
|
if (device == NULL) {
|
||||||
|
|||||||
Reference in New Issue
Block a user