packagefs: Initial support for booting into old states

If an old state is specified via mount parameters we load the packages
as specified in its activated-packages file.

The interface for the package daemon, the package daemon itself, and
the package kit are still to be adjusted, so ATM some PM components
might be a bit confused when an old state was booted.
This commit is contained in:
Ingo Weinhold
2014-04-18 23:31:40 +02:00
parent 6b3a5caf99
commit 5d55f327ed
4 changed files with 247 additions and 32 deletions
@@ -17,6 +17,7 @@
PackagesDirectory::PackagesDirectory() PackagesDirectory::PackagesDirectory()
: :
fStateName(),
fPath(NULL), fPath(NULL),
fDirFD(-1), fDirFD(-1),
fNodeRef(), fNodeRef(),
@@ -34,7 +35,20 @@ PackagesDirectory::~PackagesDirectory()
} }
status_t PackagesDirectory::Init(const char* path, dev_t mountPointDeviceID, /*static*/ bool
PackagesDirectory::IsNewer(const PackagesDirectory* a,
const PackagesDirectory* b)
{
if (b->fStateName.IsEmpty())
return false;
if (a->fStateName.IsEmpty())
return true;
return strcmp(a->fStateName, b->fStateName) > 0;
}
status_t
PackagesDirectory::Init(const char* path, dev_t mountPointDeviceID,
ino_t mountPointNodeID, struct stat& _st) ino_t mountPointNodeID, struct stat& _st)
{ {
// Open the directory. We want the path be interpreted depending on from // Open the directory. We want the path be interpreted depending on from
@@ -56,14 +70,41 @@ status_t PackagesDirectory::Init(const char* path, dev_t mountPointDeviceID,
"packages", &vnode); "packages", &vnode);
} }
if (error != B_OK) { if (error != B_OK) {
ERROR("Failed to open package domain \"%s\"\n", strerror(error)); ERROR("Failed to open packages directory \"%s\"\n", strerror(error));
RETURN_ERROR(error); RETURN_ERROR(error);
} }
return _Init(vnode, _st);
}
status_t
PackagesDirectory::InitOldState(dev_t adminDirDeviceID, ino_t adminDirNodeID,
const char* stateName)
{
if (!fStateName.SetTo(stateName))
RETURN_ERROR(B_NO_MEMORY);
struct vnode* vnode;
status_t error = vfs_entry_ref_to_vnode(adminDirDeviceID, adminDirNodeID,
stateName, &vnode);
if (error != B_OK) {
ERROR("Failed to open old state directory \"%s\"\n", strerror(error));
RETURN_ERROR(error);
}
struct stat st;
return _Init(vnode, st);
}
status_t
PackagesDirectory::_Init(struct vnode* vnode, struct stat& _st)
{
fDirFD = vfs_open_vnode(vnode, O_RDONLY, true); fDirFD = vfs_open_vnode(vnode, O_RDONLY, true);
if (fDirFD < 0) { if (fDirFD < 0) {
ERROR("Failed to open package domain \"%s\"\n", strerror(fDirFD)); ERROR("Failed to open packages directory \"%s\"\n", strerror(fDirFD));
vfs_put_vnode(vnode); vfs_put_vnode(vnode);
RETURN_ERROR(fDirFD); RETURN_ERROR(fDirFD);
} }
@@ -83,8 +124,8 @@ status_t PackagesDirectory::Init(const char* path, dev_t mountPointDeviceID,
RETURN_ERROR(normalizedPath.InitCheck()); RETURN_ERROR(normalizedPath.InitCheck());
char* normalizedPathBuffer = normalizedPath.LockBuffer(); char* normalizedPathBuffer = normalizedPath.LockBuffer();
error = vfs_entry_ref_to_path(fNodeRef.device, fNodeRef.node, NULL, true, status_t error = vfs_entry_ref_to_path(fNodeRef.device, fNodeRef.node, NULL,
normalizedPathBuffer, normalizedPath.BufferSize()); true, normalizedPathBuffer, normalizedPath.BufferSize());
if (error != B_OK) if (error != B_OK)
RETURN_ERROR(error); RETURN_ERROR(error);
@@ -9,15 +9,22 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <Referenceable.h> #include <Referenceable.h>
#include <util/DoublyLinkedList.h>
#include <util/OpenHashTable.h>
#include "NodeRef.h" #include "NodeRef.h"
#include "String.h"
class PackagesDirectory : public BReferenceable { class PackagesDirectory : public BReferenceable,
public DoublyLinkedListLinkImpl<PackagesDirectory> {
public: public:
PackagesDirectory(); PackagesDirectory();
~PackagesDirectory(); ~PackagesDirectory();
const String& StateName() const
{ return fStateName; }
// empty for the latest state
const char* Path() const const char* Path() const
{ return fPath; } { return fPath; }
int DirectoryFD() const int DirectoryFD() const
@@ -31,12 +38,57 @@ public:
status_t Init(const char* path, dev_t mountPointDeviceID, status_t Init(const char* path, dev_t mountPointDeviceID,
ino_t mountPointNodeID, struct stat& _st); ino_t mountPointNodeID, struct stat& _st);
status_t InitOldState(dev_t adminDirDeviceID,
ino_t adminDirNodeID,
const char* stateName);
static bool IsNewer(const PackagesDirectory* a,
const PackagesDirectory* b);
PackagesDirectory*& HashTableNext()
{ return fHashNext; }
private: private:
status_t _Init(struct vnode* vnode, struct stat& _st);
private:
String fStateName;
char* fPath; char* fPath;
int fDirFD; int fDirFD;
node_ref fNodeRef; node_ref fNodeRef;
PackagesDirectory* fHashNext;
}; };
struct PackagesDirectoryHashDefinition {
typedef const node_ref& KeyType;
typedef PackagesDirectory ValueType;
size_t HashKey(const node_ref& key) const
{
return (size_t)key.device ^ (size_t)key.node;
}
size_t Hash(const PackagesDirectory* value) const
{
return HashKey(value->NodeRef());
}
bool Compare(const node_ref& key, const PackagesDirectory* value) const
{
return key == value->NodeRef();
}
PackagesDirectory*& GetLink(PackagesDirectory* value) const
{
return value->HashTableNext();
}
};
typedef BOpenHashTable<PackagesDirectoryHashDefinition>
PackagesDirectoryHashTable;
typedef DoublyLinkedList<PackagesDirectory> PackagesDirectoryList;
#endif // PACKAGES_DIRECTORY_H #endif // PACKAGES_DIRECTORY_H
@@ -35,7 +35,6 @@
#include "PackageFSRoot.h" #include "PackageFSRoot.h"
#include "PackageLinkDirectory.h" #include "PackageLinkDirectory.h"
#include "PackageLinksDirectory.h" #include "PackageLinksDirectory.h"
#include "PackagesDirectory.h"
#include "Resolvable.h" #include "Resolvable.h"
#include "SizeIndex.h" #include "SizeIndex.h"
#include "UnpackingLeafNode.h" #include "UnpackingLeafNode.h"
@@ -62,6 +61,10 @@ const size_t kMaxActivationRequestSize = 10 * 1024 * 1024;
// sanity limit for activation file size // sanity limit for activation file size
const size_t kMaxActivationFileSize = 10 * 1024 * 1024; const size_t kMaxActivationFileSize = 10 * 1024 * 1024;
static const char* const kAdministrativeDirectoryName
= PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
static const char* const kActivationFileName
= PACKAGES_DIRECTORY_ACTIVATION_FILE;
static const char* const kActivationFilePath static const char* const kActivationFilePath
= PACKAGES_DIRECTORY_ADMIN_DIRECTORY "/" = PACKAGES_DIRECTORY_ADMIN_DIRECTORY "/"
PACKAGES_DIRECTORY_ACTIVATION_FILE; PACKAGES_DIRECTORY_ACTIVATION_FILE;
@@ -165,6 +168,8 @@ Volume::Volume(fs_volume* fsVolume)
fRootDirectory(NULL), fRootDirectory(NULL),
fPackageFSRoot(NULL), fPackageFSRoot(NULL),
fPackagesDirectory(NULL), fPackagesDirectory(NULL),
fPackagesDirectories(),
fPackagesDirectoriesByNodeRef(),
fPackageSettings(), fPackageSettings(),
fNextNodeID(kRootDirectoryID + 1) fNextNodeID(kRootDirectoryID + 1)
{ {
@@ -213,8 +218,8 @@ Volume::~Volume()
if (fRootDirectory != NULL) if (fRootDirectory != NULL)
fRootDirectory->ReleaseReference(); fRootDirectory->ReleaseReference();
if (fPackagesDirectory != NULL) while (PackagesDirectory* directory = fPackagesDirectories.RemoveHead())
fPackagesDirectory->ReleaseReference(); directory->ReleaseReference();
rw_lock_destroy(&fLock); rw_lock_destroy(&fLock);
} }
@@ -224,7 +229,11 @@ status_t
Volume::Mount(const char* parameterString) Volume::Mount(const char* parameterString)
{ {
// init the hash tables // init the hash tables
status_t error = fNodes.Init(); status_t error = fPackagesDirectoriesByNodeRef.Init();
if (error != B_OK)
RETURN_ERROR(error);
error = fNodes.Init();
if (error != B_OK) if (error != B_OK)
RETURN_ERROR(error); RETURN_ERROR(error);
@@ -305,6 +314,7 @@ Volume::Mount(const char* parameterString)
const char* volumeName = NULL; const char* volumeName = NULL;
const char* mountType = NULL; const char* mountType = NULL;
const char* shineThrough = NULL; const char* shineThrough = NULL;
const char* packagesState = NULL;
void* parameterHandle = parse_driver_settings_string(parameterString); void* parameterHandle = parse_driver_settings_string(parameterString);
if (parameterHandle != NULL) { if (parameterHandle != NULL) {
@@ -315,6 +325,8 @@ Volume::Mount(const char* parameterString)
mountType = get_driver_parameter(parameterHandle, "type", NULL, NULL); mountType = get_driver_parameter(parameterHandle, "type", NULL, NULL);
shineThrough = get_driver_parameter(parameterHandle, "shine-through", shineThrough = get_driver_parameter(parameterHandle, "shine-through",
NULL, NULL); NULL, NULL);
packagesState = get_driver_parameter(parameterHandle, "state", NULL,
NULL);
} }
CObjectDeleter<void, status_t> parameterHandleDeleter(parameterHandle, CObjectDeleter<void, status_t> parameterHandleDeleter(parameterHandle,
@@ -348,6 +360,8 @@ Volume::Mount(const char* parameterString)
fPackagesDirectory = new(std::nothrow) PackagesDirectory; fPackagesDirectory = new(std::nothrow) PackagesDirectory;
if (fPackagesDirectory == NULL) if (fPackagesDirectory == NULL)
RETURN_ERROR(B_NO_MEMORY); RETURN_ERROR(B_NO_MEMORY);
fPackagesDirectories.Add(fPackagesDirectory);
fPackagesDirectoriesByNodeRef.Insert(fPackagesDirectory);
struct stat st; struct stat st;
error = fPackagesDirectory->Init(packages, fMountPoint.deviceID, error = fPackagesDirectory->Init(packages, fMountPoint.deviceID,
@@ -355,6 +369,13 @@ Volume::Mount(const char* parameterString)
if (error != B_OK) if (error != B_OK)
RETURN_ERROR(error); RETURN_ERROR(error);
// If a packages state has been specified, load the needed states.
if (packagesState != NULL) {
error = _LoadOldPackagesStates(packagesState);
if (error != B_OK)
RETURN_ERROR(error);
}
// If no volume name is given, infer it from the mount type. // If no volume name is given, infer it from the mount type.
if (volumeName == NULL) { if (volumeName == NULL) {
switch (fMountType) { switch (fMountType) {
@@ -620,14 +641,89 @@ Volume::PackageLinkNodeChanged(Node* node, uint32 statFields,
} }
status_t
Volume::_LoadOldPackagesStates(const char* packagesState)
{
// open and stat the admininistrative dir
int fd = openat(fPackagesDirectory->DirectoryFD(),
kAdministrativeDirectoryName, O_RDONLY);
if (fd < 0) {
ERROR("Failed to open administrative directory: %s\n", strerror(errno));
RETURN_ERROR(errno);
}
struct stat adminDirStat;
if (fstat(fd, &adminDirStat) < 0) {
ERROR("Failed to fstat() administrative directory: %s\n",
strerror(errno));
RETURN_ERROR(errno);
}
// iterate through the "administrative" dir
DIR* dir = fdopendir(fd);
if (dir == NULL) {
ERROR("Failed to open administrative directory: %s\n", strerror(errno));
RETURN_ERROR(errno);
}
CObjectDeleter<DIR, int> dirCloser(dir, closedir);
while (dirent* entry = readdir(dir)) {
if (strncmp(entry->d_name, "state_", 6) != 0
|| strcmp(entry->d_name, packagesState) < 0) {
continue;
}
PackagesDirectory* packagesDirectory
= new(std::nothrow) PackagesDirectory;
status_t error = packagesDirectory->InitOldState(adminDirStat.st_dev,
adminDirStat.st_ino, entry->d_name);
if (error != B_OK) {
delete packagesDirectory;
continue;
}
fPackagesDirectories.Add(packagesDirectory);
fPackagesDirectoriesByNodeRef.Insert(packagesDirectory);
INFORM("added old packages dir state \"%s\"\n",
packagesDirectory->StateName().Data());
}
// sort the packages directories by state age
fPackagesDirectories.Sort(&PackagesDirectory::IsNewer);
return B_OK;
}
status_t status_t
Volume::_AddInitialPackages() Volume::_AddInitialPackages()
{ {
dprintf("packagefs: Adding packages from \"%s\"\n", PackagesDirectory* packagesDirectory = fPackagesDirectories.Last();
fPackagesDirectory->Path()); INFORM("Adding packages from \"%s\"\n", packagesDirectory->Path());
// try reading the activation file of the oldest state
status_t error = _AddInitialPackagesFromActivationFile(packagesDirectory);
if (error != B_OK && packagesDirectory != fPackagesDirectory) {
WARN("Loading packages from old state \"%s\" failed. Loading packages "
"from latest state.\n", packagesDirectory->StateName().Data());
// remove all packages already added
{
VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
VolumeWriteLocker volumeLocker(this);
_RemoveAllPackages();
}
// remove the old states
while (fPackagesDirectories.Last() != fPackagesDirectory)
fPackagesDirectories.RemoveTail()->ReleaseReference();
// try reading the activation file of the latest state
packagesDirectory = fPackagesDirectory;
error = _AddInitialPackagesFromActivationFile(packagesDirectory);
}
// try reading the activation file
status_t error = _AddInitialPackagesFromActivationFile();
if (error != B_OK) { if (error != B_OK) {
INFORM("Loading packages from activation file failed. Loading all " INFORM("Loading packages from activation file failed. Loading all "
"packages in packages directory.\n"); "packages in packages directory.\n");
@@ -666,11 +762,14 @@ Volume::_AddInitialPackages()
status_t status_t
Volume::_AddInitialPackagesFromActivationFile() Volume::_AddInitialPackagesFromActivationFile(
PackagesDirectory* packagesDirectory)
{ {
// try reading the activation file // try reading the activation file
int fd = openat(fPackagesDirectory->DirectoryFD(), int fd = openat(packagesDirectory->DirectoryFD(),
kActivationFilePath, O_RDONLY); packagesDirectory == fPackagesDirectory
? kActivationFilePath : kActivationFileName,
O_RDONLY);
if (fd < 0) { if (fd < 0) {
INFORM("Failed to open packages activation file: %s\n", INFORM("Failed to open packages activation file: %s\n",
strerror(errno)); strerror(errno));
@@ -730,7 +829,8 @@ Volume::_AddInitialPackagesFromActivationFile()
RETURN_ERROR(B_BAD_DATA); RETURN_ERROR(B_BAD_DATA);
} }
status_t error = _LoadAndAddInitialPackage(packageName); status_t error = _LoadAndAddInitialPackage(packagesDirectory,
packageName);
if (error != B_OK) if (error != B_OK)
RETURN_ERROR(error); RETURN_ERROR(error);
@@ -771,7 +871,7 @@ Volume::_AddInitialPackagesFromDirectory()
continue; continue;
} }
_LoadAndAddInitialPackage(entry->d_name); _LoadAndAddInitialPackage(fPackagesDirectory, entry->d_name);
} }
return B_OK; return B_OK;
@@ -779,10 +879,11 @@ Volume::_AddInitialPackagesFromDirectory()
status_t status_t
Volume::_LoadAndAddInitialPackage(const char* name) Volume::_LoadAndAddInitialPackage(PackagesDirectory* packagesDirectory,
const char* name)
{ {
Package* package; Package* package;
status_t error = _LoadPackage(name, package); status_t error = _LoadPackage(packagesDirectory, name, package);
if (error != B_OK) { if (error != B_OK) {
ERROR("Failed to load package \"%s\": %s\n", name, strerror(error)); ERROR("Failed to load package \"%s\": %s\n", name, strerror(error));
RETURN_ERROR(error); RETURN_ERROR(error);
@@ -1309,17 +1410,28 @@ Volume::_RemoveNodeAndVNode(Node* node)
status_t status_t
Volume::_LoadPackage(const char* name, Package*& _package) Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name,
Package*& _package)
{ {
// check whether the entry is a file // Find the package -- check the specified packages directory and iterate
// toward the newer states.
struct stat st; struct stat st;
if (fstatat(fPackagesDirectory->DirectoryFD(), name, &st, 0) < 0 for (;;) {
|| !S_ISREG(st.st_mode)) { if (packagesDirectory == NULL)
return B_BAD_VALUE; return B_ENTRY_NOT_FOUND;
if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) {
// check whether the entry is a file
if (!S_ISREG(st.st_mode))
return B_BAD_VALUE;
break;
}
packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory);
} }
// create a package // create a package
Package* package = new(std::nothrow) Package(this, fPackagesDirectory, Package* package = new(std::nothrow) Package(this, packagesDirectory,
st.st_dev, st.st_ino); st.st_dev, st.st_ino);
if (package == NULL) if (package == NULL)
RETURN_ERROR(B_NO_MEMORY); RETURN_ERROR(B_NO_MEMORY);
@@ -1417,7 +1529,7 @@ INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32 " o
} }
Package* package; Package* package;
status_t error = _LoadPackage(item->name, package); status_t error = _LoadPackage(fPackagesDirectory, item->name, package);
if (error != B_OK) { if (error != B_OK) {
ERROR("Volume::_ChangeActivation(): failed to load package " ERROR("Volume::_ChangeActivation(): failed to load package "
"\"%s\"\n", item->name); "\"%s\"\n", item->name);
@@ -21,6 +21,7 @@
#include "NodeListener.h" #include "NodeListener.h"
#include "Package.h" #include "Package.h"
#include "PackageLinksListener.h" #include "PackageLinksListener.h"
#include "PackagesDirectory.h"
#include "PackageSettings.h" #include "PackageSettings.h"
#include "Query.h" #include "Query.h"
@@ -109,10 +110,16 @@ private:
struct ActivationChangeRequest; struct ActivationChangeRequest;
private: private:
status_t _LoadOldPackagesStates(
const char* packagesState);
status_t _AddInitialPackages(); status_t _AddInitialPackages();
status_t _AddInitialPackagesFromActivationFile(); status_t _AddInitialPackagesFromActivationFile(
PackagesDirectory* packagesDirectory);
status_t _AddInitialPackagesFromDirectory(); status_t _AddInitialPackagesFromDirectory();
status_t _LoadAndAddInitialPackage(const char* name); status_t _LoadAndAddInitialPackage(
PackagesDirectory* packagesDirectory,
const char* name);
inline void _AddPackage(Package* package); inline void _AddPackage(Package* package);
inline void _RemovePackage(Package* package); inline void _RemovePackage(Package* package);
@@ -145,8 +152,9 @@ private:
void _RemoveNodeAndVNode(Node* node); void _RemoveNodeAndVNode(Node* node);
// caller must hold a reference // caller must hold a reference
status_t _LoadPackage(const char* name, status_t _LoadPackage(
Package*& _package); PackagesDirectory* packagesDirectory,
const char* name, Package*& _package);
status_t _ChangeActivation( status_t _ChangeActivation(
ActivationChangeRequest& request); ActivationChangeRequest& request);
@@ -178,6 +186,8 @@ private:
::PackageFSRoot* fPackageFSRoot; ::PackageFSRoot* fPackageFSRoot;
::MountType fMountType; ::MountType fMountType;
PackagesDirectory* fPackagesDirectory; PackagesDirectory* fPackagesDirectory;
PackagesDirectoryList fPackagesDirectories;
PackagesDirectoryHashTable fPackagesDirectoriesByNodeRef;
PackageSettings fPackageSettings; PackageSettings fPackageSettings;
struct { struct {