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:
parent
59881eaa16
commit
5c0f8450ac
@ -29,6 +29,7 @@
|
||||
#define BOOT_VOLUME_USER_SELECTED "user selected"
|
||||
#define BOOT_VOLUME_BOOTED_FROM_IMAGE "booted from image"
|
||||
#define BOOT_VOLUME_PACKAGED "packaged"
|
||||
#define BOOT_VOLUME_PACKAGES_STATE "packages state"
|
||||
#define BOOT_VOLUME_PARTITION_OFFSET "partition offset"
|
||||
#define BOOT_VOLUME_DISK_IDENTIFIER "disk identifier"
|
||||
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
struct file_map_run;
|
||||
struct stat;
|
||||
class PackageVolumeInfo;
|
||||
class PackageVolumeState;
|
||||
|
||||
/** This is the base class for all VFS nodes */
|
||||
|
||||
@ -114,7 +116,11 @@ public:
|
||||
BootVolume();
|
||||
~BootVolume();
|
||||
|
||||
status_t SetTo(Directory* rootDirectory);
|
||||
status_t SetTo(Directory* rootDirectory,
|
||||
PackageVolumeInfo* packageVolumeInfo
|
||||
= NULL,
|
||||
PackageVolumeState* packageVolumeState
|
||||
= NULL);
|
||||
void Unset();
|
||||
|
||||
bool IsValid() const
|
||||
@ -125,9 +131,16 @@ public:
|
||||
Directory* SystemDirectory() const
|
||||
{ return fSystemDirectory; }
|
||||
bool IsPackaged() const
|
||||
{ return fPackaged; }
|
||||
{ return fPackageVolumeInfo != NULL; }
|
||||
PackageVolumeInfo* GetPackageVolumeInfo() const
|
||||
{ return fPackageVolumeInfo; }
|
||||
PackageVolumeState* GetPackageVolumeState() const
|
||||
{ return fPackageVolumeState; }
|
||||
|
||||
private:
|
||||
status_t _SetTo(Directory* rootDirectory,
|
||||
PackageVolumeInfo* packageVolumeInfo,
|
||||
PackageVolumeState* packageVolumeState);
|
||||
int _OpenSystemPackage();
|
||||
|
||||
private:
|
||||
@ -136,8 +149,8 @@ private:
|
||||
Directory* fSystemDirectory;
|
||||
// "system" directory of the volume; if packaged the root
|
||||
// directory of the mounted packagefs
|
||||
bool fPackaged;
|
||||
// indicates whether the boot volume's system is packaged
|
||||
PackageVolumeInfo* fPackageVolumeInfo;
|
||||
PackageVolumeState* fPackageVolumeState;
|
||||
};
|
||||
|
||||
|
||||
|
@ -64,6 +64,7 @@ BootStaticLibrary boot_loader :
|
||||
loader.cpp
|
||||
main.cpp
|
||||
menu.cpp
|
||||
package_support.cpp
|
||||
pager.cpp
|
||||
partitions.cpp
|
||||
RootFileSystem.cpp
|
||||
|
@ -1,7 +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.
|
||||
* Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
|
||||
#include "load_driver_settings.h"
|
||||
#include "loader.h"
|
||||
#include "package_support.h"
|
||||
#include "pager.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 -
|
||||
|
||||
|
||||
@ -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
|
||||
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*
|
||||
add_boot_volume_menu(Directory* bootVolume)
|
||||
add_boot_volume_menu()
|
||||
{
|
||||
Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume");
|
||||
MenuItem* item;
|
||||
@ -1072,19 +1237,12 @@ add_boot_volume_menu(Directory* bootVolume)
|
||||
Directory* volume;
|
||||
while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
|
||||
// only list bootable volumes
|
||||
if (volume != bootVolume && !is_bootable(volume))
|
||||
if (volume != sBootVolume->RootDirectory() && !is_bootable(volume))
|
||||
continue;
|
||||
|
||||
char name[B_FILE_NAME_LENGTH];
|
||||
if (volume->GetName(name, sizeof(name)) == B_OK) {
|
||||
menu->AddItem(item = new(nothrow) MenuItem(name));
|
||||
item->SetTarget(user_menu_boot_volume);
|
||||
item->SetData(volume);
|
||||
|
||||
if (volume == bootVolume) {
|
||||
item->SetMarked(true);
|
||||
item->Select(true);
|
||||
}
|
||||
add_boot_volume_item(menu, volume, name);
|
||||
|
||||
count++;
|
||||
}
|
||||
@ -1425,7 +1583,7 @@ user_menu(BootVolume& _bootVolume, PathBlacklist& _pathBlacklist)
|
||||
|
||||
// Add 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
|
||||
menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
|
||||
|
351
src/system/boot/loader/package_support.cpp
Normal file
351
src/system/boot/loader/package_support.cpp
Normal file
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* 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;
|
||||
}
|
77
src/system/boot/loader/package_support.h
Normal file
77
src/system/boot/loader/package_support.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* 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 <syscall_utils.h>
|
||||
|
||||
#include "package_support.h"
|
||||
#include "RootFileSystem.h"
|
||||
#include "file_systems/packagefs/packagefs.h"
|
||||
|
||||
@ -418,7 +419,8 @@ BootVolume::BootVolume()
|
||||
:
|
||||
fRootDirectory(NULL),
|
||||
fSystemDirectory(NULL),
|
||||
fPackaged(false)
|
||||
fPackageVolumeInfo(NULL),
|
||||
fPackageVolumeState(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@ -430,7 +432,46 @@ BootVolume::~BootVolume()
|
||||
|
||||
|
||||
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();
|
||||
|
||||
@ -451,13 +492,32 @@ BootVolume::SetTo(Directory* rootDirectory)
|
||||
|
||||
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
|
||||
int packageFD = _OpenSystemPackage();
|
||||
fPackaged = packageFD >= 0;
|
||||
if (!fPackaged)
|
||||
return B_OK;
|
||||
if (packageFD < 0)
|
||||
return packageFD;
|
||||
|
||||
// the system is packaged -- mount the packagefs
|
||||
// mount packagefs
|
||||
Directory* packageRootDirectory;
|
||||
status_t error = packagefs_mount_file(packageFD, fSystemDirectory,
|
||||
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
|
||||
BootVolume::_OpenSystemPackage()
|
||||
{
|
||||
@ -505,29 +548,9 @@ BootVolume::_OpenSystemPackage()
|
||||
return -1;
|
||||
Directory* packageDirectory = (Directory*)packagesNode;
|
||||
|
||||
// search for the system package
|
||||
int fd = -1;
|
||||
void* cookie;
|
||||
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;
|
||||
// open the system package
|
||||
return open_from(packageDirectory, fPackageVolumeState->SystemPackage(),
|
||||
O_RDONLY);
|
||||
}
|
||||
|
||||
|
||||
@ -561,8 +584,13 @@ register_boot_file_system(BootVolume& bootVolume)
|
||||
|
||||
gBootVolume.SetInt64(BOOT_VOLUME_PARTITION_OFFSET,
|
||||
partition->offset);
|
||||
if (bootVolume.IsPackaged())
|
||||
|
||||
if (bootVolume.IsPackaged()) {
|
||||
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());
|
||||
if (device == NULL) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user