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:
Ingo Weinhold 2014-04-14 08:28:26 +02:00
parent 59881eaa16
commit 5c0f8450ac
7 changed files with 692 additions and 63 deletions

View File

@ -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"

View File

@ -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;
};

View File

@ -64,6 +64,7 @@ BootStaticLibrary boot_loader :
loader.cpp
main.cpp
menu.cpp
package_support.cpp
pager.cpp
partitions.cpp
RootFileSystem.cpp

View File

@ -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",

View 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;
}

View 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

View File

@ -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) {