package daemon: Initial old packages state support

* VolumeState: Move locking to Volume.
* Package: Pull the file related functionality into new class
  PackageFile. A Package refers to a PackageFile. A PackageFile can be
  referenced by multiple Packages. PackageFiles are managed by the new
  PackageFileManager.
* CommitTransactionHandler: Clone the passed volume state.
* Volume now manages two VolumeStates: A state reflecting the currently
  active packages and the latest state (i.e. the one reflecting the
  situation of the packages directory). Usually the two are the same,
  unless an old state has been booted.
* The client interface hasn't been adjusted yet. Clients only see the
  latest state.
This commit is contained in:
Ingo Weinhold
2014-05-01 18:18:37 +02:00
parent 2354b8ac00
commit 2dbf8167fe
14 changed files with 869 additions and 230 deletions
@@ -25,6 +25,7 @@
#include "Constants.h"
#include "DebugSupport.h"
#include "Exception.h"
#include "PackageFileManager.h"
#include "VolumeState.h"
@@ -32,17 +33,25 @@ using namespace BPackageKit::BPrivate;
CommitTransactionHandler::CommitTransactionHandler(Volume* volume,
VolumeState* volumeState, const PackageSet& packagesAlreadyAdded,
PackageFileManager* packageFileManager, VolumeState* volumeState,
bool isActiveVolumeState, const PackageSet& packagesAlreadyAdded,
const PackageSet& packagesAlreadyRemoved)
:
fVolume(volume),
fVolumeState(volumeState),
fPackageFileManager(packageFileManager),
fVolumeState(volumeState->Clone()),
fVolumeStateIsActive(isActiveVolumeState),
fPackagesToActivate(),
fPackagesToDeactivate(),
fAddedPackages(),
fRemovedPackages(),
fPackagesAlreadyAdded(packagesAlreadyAdded),
fPackagesAlreadyRemoved(packagesAlreadyRemoved),
fOldStateDirectory(),
fOldStateDirectoryRef(),
fOldStateDirectoryName(),
fTransactionDirectoryRef(),
fWritableFilesDirectory(),
fAddedGroups(),
fAddedUsers(),
fFSTransaction()
@@ -62,6 +71,8 @@ CommitTransactionHandler::~CommitTransactionHandler()
delete package;
}
}
delete fVolumeState;
}
@@ -144,6 +155,15 @@ CommitTransactionHandler::Revert()
}
VolumeState*
CommitTransactionHandler::DetachVolumeState()
{
VolumeState* result = fVolumeState;
fVolumeState = NULL;
return result;
}
void
CommitTransactionHandler::_GetPackagesToDeactivate(
const BActivationTransaction& transaction)
@@ -164,11 +184,6 @@ CommitTransactionHandler::_GetPackagesToDeactivate(
}
fPackagesToDeactivate.insert(package);
if (fPackagesAlreadyRemoved.find(package)
== fPackagesAlreadyRemoved.end()) {
package->IncrementEntryRemovedIgnoreLevel();
}
}
}
@@ -231,18 +246,16 @@ CommitTransactionHandler::_ReadPackagesToActivate(
}
// read the package
package = new(std::nothrow) Package;
if (package == NULL || !fPackagesToActivate.AddItem(package)) {
delete package;
throw Exception(B_NO_MEMORY);
}
error = package->Init(
NotOwningEntryRef(fTransactionDirectoryRef, packageName));
error = fPackageFileManager->CreatePackage(
NotOwningEntryRef(fTransactionDirectoryRef, packageName),
package);
if (error != B_OK)
throw Exception(error, "failed to read package", packageName);
package->IncrementEntryCreatedIgnoreLevel();
if (!fPackagesToActivate.AddItem(package)) {
delete package;
throw Exception(B_NO_MEMORY);
}
}
}
@@ -250,6 +263,9 @@ CommitTransactionHandler::_ReadPackagesToActivate(
void
CommitTransactionHandler::_ApplyChanges(BMessage* reply)
{
if (fVolumeState == NULL)
throw std::bad_alloc();
// create an old state directory
_CreateOldStateDirectory(reply);
@@ -262,8 +278,12 @@ CommitTransactionHandler::_ApplyChanges(BMessage* reply)
// activate/deactivate packages
_ChangePackageActivation(fAddedPackages, fRemovedPackages);
// run post-installation scripts
_RunPostInstallScripts();
if (fVolumeStateIsActive) {
// run post-installation scripts
_RunPostInstallScripts();
} else {
// TODO: Make sure the post-install scripts are run on next boot!
}
// removed packages have been deleted, new packages shall not be deleted
fAddedPackages.clear();
@@ -318,6 +338,10 @@ CommitTransactionHandler::_CreateOldStateDirectory(BMessage* reply)
fOldStateDirectoryName = directoryName;
error = fOldStateDirectory.GetNodeRef(&fOldStateDirectoryRef);
if (error != B_OK)
throw Exception(error, "failed get old state directory ref");
// write the old activation file
BEntry activationFile;
error = _WriteActivationFile(
@@ -351,8 +375,7 @@ CommitTransactionHandler::_RemovePackagesToDeactivate()
}
// get a BEntry for the package
NotOwningEntryRef entryRef(fVolume->PackagesDirectoryRef(),
package->FileName());
NotOwningEntryRef entryRef(package->EntryRef());
BEntry entry;
status_t error = entry.SetTo(&entryRef);
@@ -371,6 +394,10 @@ CommitTransactionHandler::_RemovePackagesToDeactivate()
"failed to move old package from packages directory",
package->FileName());
}
fPackageFileManager->PackageFileMoved(package->File(),
fOldStateDirectoryRef);
package->File()->IncrementEntryRemovedIgnoreLevel();
}
}
@@ -419,6 +446,10 @@ CommitTransactionHandler::_AddPackagesToActivate()
package->FileName());
}
fPackageFileManager->PackageFileMoved(package->File(),
fVolume->PackagesDirectoryRef());
package->File()->IncrementEntryCreatedIgnoreLevel();
// also add the package to the volume
fVolumeState->AddPackage(package);
@@ -944,9 +975,7 @@ CommitTransactionHandler::_RevertAddPackagesToActivate()
continue;
// get BEntry for the package
NotOwningEntryRef entryRef(fVolume->PackagesDirectoryRef(),
package->FileName());
NotOwningEntryRef entryRef(package->EntryRef());
BEntry entry;
error = entry.SetTo(&entryRef);
if (error != B_OK) {
@@ -963,6 +992,10 @@ CommitTransactionHandler::_RevertAddPackagesToActivate()
strerror(error));
continue;
}
fPackageFileManager->PackageFileMoved(package->File(),
fTransactionDirectoryRef);
package->File()->IncrementEntryRemovedIgnoreLevel();
}
}
@@ -1010,6 +1043,10 @@ CommitTransactionHandler::_RevertRemovePackagesToDeactivate()
strerror(error));
continue;
}
fPackageFileManager->PackageFileMoved(package->File(),
fVolume->PackagesDirectoryRef());
package->File()->IncrementEntryCreatedIgnoreLevel();
}
}
@@ -1160,8 +1197,7 @@ CommitTransactionHandler::_ExtractPackageContent(Package* package,
createSubDirectoryOperation.Finished();
// extract
NotOwningEntryRef packageRef(fVolume->PackagesDirectoryRef(),
package->FileName());
NotOwningEntryRef packageRef(package->EntryRef());
int32 contentPathCount = contentPaths.CountStrings();
for (int32 i = 0; i < contentPathCount; i++) {
@@ -1368,6 +1404,39 @@ CommitTransactionHandler::_ChangePackageActivation(
if (error != B_OK)
throw Exception(error, "failed to write activation file");
// notify packagefs
if (fVolumeStateIsActive) {
_ChangePackageActivationIOCtl(packagesToActivate, packagesToDeactivate);
} else {
// TODO: Notify packagefs that active packages have been moved or do
// node monitoring in packagefs!
}
// rename the temporary activation file to the final file
error = activationFileEntry.Rename(kActivationFileName, true);
if (error != B_OK) {
throw Exception(error,
"failed to rename temporary activation file to final file");
// TODO: We should probably try to revert the activation changes, though that
// will fail, if this method has been called in response to node monitoring
// events. Alternatively moving the package activation file could be made part
// of the ioctl(), since packagefs should be able to undo package changes until
// the very end, unless running out of memory. In the end the situation would be
// bad anyway, though, since the activation file may refer to removed packages
// and things would be in an inconsistent state after rebooting.
}
// Update our state, i.e. remove deactivated packages and mark activated
// packages accordingly.
fVolumeState->ActivationChanged(packagesToActivate, packagesToDeactivate);
}
void
CommitTransactionHandler::_ChangePackageActivationIOCtl(
const PackageSet& packagesToActivate,
const PackageSet& packagesToDeactivate)
{
// compute the size of the allocation we need for the activation change
// request
int32 itemCount = packagesToActivate.size() + packagesToDeactivate.size();
@@ -1419,24 +1488,6 @@ CommitTransactionHandler::_ChangePackageActivation(
// TODO: We need more error information and error handling!
throw Exception(errno, "ioctl() to de-/activate packages failed");
}
// rename the temporary activation file to the final file
error = activationFileEntry.Rename(kActivationFileName, true);
if (error != B_OK) {
throw Exception(error,
"failed to rename temporary activation file to final file");
// TODO: We should probably try to reverse the activation changes, though that
// will fail, if this method has been called in response to node monitoring
// events. Alternatively moving the package activation file could be made part
// of the ioctl(), since packagefs should be able to undo package changes until
// the very end, unless running out of memory. In the end the situation would be
// bad anyway, though, since the activation file may refer to removed packages
// and things would be in an inconsistent state after rebooting.
}
// Update our state, i.e. remove deactivated packages and mark activated
// packages accordingly.
fVolumeState->ActivationChanged(packagesToActivate, packagesToDeactivate);
}
@@ -25,7 +25,9 @@ typedef std::set<std::string> StringSet;
class CommitTransactionHandler {
public:
CommitTransactionHandler(Volume* volume,
PackageFileManager* packageFileManager,
VolumeState* volumeState,
bool isActiveVolumeState,
const PackageSet& packagesAlreadyAdded,
const PackageSet& packagesAlreadyRemoved);
~CommitTransactionHandler();
@@ -43,6 +45,10 @@ public:
const BString& OldStateDirectoryName() const
{ return fOldStateDirectoryName; }
VolumeState* DetachVolumeState();
bool IsActiveVolumeState() const
{ return fVolumeStateIsActive; }
private:
typedef BObjectList<Package> PackageList;
typedef FSUtils::RelativePath RelativePath;
@@ -112,6 +118,10 @@ private:
const PackageSet& packagesToActivate,
const PackageSet& packagesToDeactivate);
// throws Exception
void _ChangePackageActivationIOCtl(
const PackageSet& packagesToActivate,
const PackageSet& packagesToDeactivate);
// throws Exception
void _FillInActivationChangeItem(
PackageFSActivationChangeItem* item,
PackageFSActivationChangeType type,
@@ -126,7 +136,9 @@ private:
private:
Volume* fVolume;
PackageFileManager* fPackageFileManager;
VolumeState* fVolumeState;
bool fVolumeStateIsActive;
PackageList fPackagesToActivate;
PackageSet fPackagesToDeactivate;
PackageSet fAddedPackages;
@@ -134,6 +146,7 @@ private:
const PackageSet& fPackagesAlreadyAdded;
const PackageSet& fPackagesAlreadyRemoved;
BDirectory fOldStateDirectory;
node_ref fOldStateDirectoryRef;
BString fOldStateDirectoryName;
node_ref fTransactionDirectoryRef;
BDirectory fWritableFilesDirectory;
+3
View File
@@ -21,5 +21,8 @@ static const bigtime_t kHandleNodeMonitorEvents = 'nmon';
static const bigtime_t kNodeMonitorEventHandlingDelay = 500000;
static const bigtime_t kCommunicationTimeout = 1000000;
// sanity limit for activation file size
static const size_t kMaxActivationFileSize = 10 * 1024 * 1024;
#endif // CONSTANTS_H
+2
View File
@@ -15,6 +15,8 @@ Server package_daemon
JobQueue.cpp
Package.cpp
PackageDaemon.cpp
PackageFile.cpp
PackageFileManager.cpp
PackageManager.cpp
ProblemWindow.cpp
ResultWindow.cpp
+12 -57
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -18,73 +18,28 @@
#include "DebugSupport.h"
Package::Package()
Package::Package(PackageFile* file)
:
fNodeRef(),
fFileName(),
fInfo(),
fFile(file),
fActive(false),
fFileNameHashTableNext(NULL),
fNodeRefHashTableNext(NULL),
fIgnoreEntryCreated(0),
fIgnoreEntryRemoved(0)
fNodeRefHashTableNext(NULL)
{
fFile->AcquireReference();
}
Package::~Package()
{
fFile->ReleaseReference();
}
status_t
Package::Init(const entry_ref& entryRef)
Package*
Package::Clone() const
{
// init the file name
fFileName = entryRef.name;
if (fFileName.IsEmpty())
RETURN_ERROR(B_NO_MEMORY);
// open the file and get the node_ref
BFile file;
status_t error = file.SetTo(&entryRef, B_READ_ONLY);
if (error != B_OK)
RETURN_ERROR(error);
error = file.GetNodeRef(&fNodeRef);
if (error != B_OK)
RETURN_ERROR(error);
// get the package info
int fd = file.Dup();
if (fd < 0)
RETURN_ERROR(error);
FileDescriptorCloser fdCloser(fd);
error = fInfo.ReadFromPackageFile(fd);
if (error != B_OK)
RETURN_ERROR(error);
if (fFileName != fInfo.CanonicalFileName())
fInfo.SetFileName(fFileName);
return B_OK;
}
BString
Package::RevisionedName() const
{
return BString().SetToFormat("%s-%s", fInfo.Name().String(),
fInfo.Version().ToString().String());
}
BString
Package::RevisionedNameThrows() const
{
BString result(RevisionedName());
if (result.IsEmpty())
throw std::bad_alloc();
return result;
Package* clone = new(std::nothrow) Package(fFile);
if (clone != NULL)
clone->fActive = fActive;
return clone;
}
+16 -31
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -11,11 +11,7 @@
#include <set>
#include <Entry.h>
#include <Node.h>
#include <package/PackageInfo.h>
#include <util/OpenHashTable.h>
#include "PackageFile.h"
using namespace BPackageKit;
@@ -23,40 +19,33 @@ using namespace BPackageKit;
class Package {
public:
Package();
Package(PackageFile* file);
~Package();
status_t Init(const entry_ref& entryRef);
PackageFile* File() const
{ return fFile; }
const node_ref& NodeRef() const
{ return fNodeRef; }
{ return fFile->NodeRef(); }
const BString& FileName() const
{ return fFileName; }
{ return fFile->FileName(); }
NotOwningEntryRef EntryRef() const
{ return fFile->EntryRef(); }
const BPackageInfo & Info() const
{ return fInfo; }
{ return fFile->Info(); }
BString RevisionedName() const;
BString RevisionedNameThrows() const;
BString RevisionedName() const
{ return fFile->RevisionedName(); }
BString RevisionedNameThrows() const
{ return fFile->RevisionedNameThrows(); }
bool IsActive() const
{ return fActive; }
void SetActive(bool active)
{ fActive = active; }
int32 EntryCreatedIgnoreLevel() const
{ return fIgnoreEntryCreated; }
void IncrementEntryCreatedIgnoreLevel()
{ fIgnoreEntryCreated++; }
void DecrementEntryCreatedIgnoreLevel()
{ fIgnoreEntryCreated--; }
int32 EntryRemovedIgnoreLevel() const
{ return fIgnoreEntryRemoved; }
void IncrementEntryRemovedIgnoreLevel()
{ fIgnoreEntryRemoved++; }
void DecrementEntryRemovedIgnoreLevel()
{ fIgnoreEntryRemoved--; }
Package* Clone() const;
Package*& FileNameHashTableNext()
{ return fFileNameHashTableNext; }
@@ -64,14 +53,10 @@ public:
{ return fNodeRefHashTableNext; }
private:
node_ref fNodeRef;
BString fFileName;
BPackageInfo fInfo;
PackageFile* fFile;
bool fActive;
Package* fFileNameHashTableNext;
Package* fNodeRefHashTableNext;
int32 fIgnoreEntryCreated;
int32 fIgnoreEntryRemoved;
};
+106
View File
@@ -0,0 +1,106 @@
/*
* Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include "PackageFile.h"
#include <fcntl.h>
#include <File.h>
#include <AutoDeleter.h>
#include "DebugSupport.h"
#include "PackageFileManager.h"
PackageFile::PackageFile()
:
fNodeRef(),
fDirectoryRef(),
fFileName(),
fInfo(),
fEntryRefHashTableNext(NULL),
// fNodeRefHashTableNext(NULL),
fOwner(NULL),
fIgnoreEntryCreated(0),
fIgnoreEntryRemoved(0)
{
}
PackageFile::~PackageFile()
{
}
status_t
PackageFile::Init(const entry_ref& entryRef, PackageFileManager* owner)
{
fDirectoryRef.device = entryRef.device;
fDirectoryRef.node = entryRef.directory;
// init the file name
fFileName = entryRef.name;
if (fFileName.IsEmpty())
RETURN_ERROR(B_NO_MEMORY);
// open the file and get the node_ref
BFile file;
status_t error = file.SetTo(&entryRef, B_READ_ONLY);
if (error != B_OK)
RETURN_ERROR(error);
error = file.GetNodeRef(&fNodeRef);
if (error != B_OK)
RETURN_ERROR(error);
// get the package info
int fd = file.Dup();
if (fd < 0)
RETURN_ERROR(error);
FileDescriptorCloser fdCloser(fd);
error = fInfo.ReadFromPackageFile(fd);
if (error != B_OK)
RETURN_ERROR(error);
if (fFileName != fInfo.CanonicalFileName())
fInfo.SetFileName(fFileName);
fOwner = owner;
return B_OK;
}
BString
PackageFile::RevisionedName() const
{
return BString().SetToFormat("%s-%s", fInfo.Name().String(),
fInfo.Version().ToString().String());
}
BString
PackageFile::RevisionedNameThrows() const
{
BString result(RevisionedName());
if (result.IsEmpty())
throw std::bad_alloc();
return result;
}
void
PackageFile::LastReferenceReleased()
{
if (fOwner == NULL)
fOwner->RemovePackageFile(this);
delete this;
}
+124
View File
@@ -0,0 +1,124 @@
/*
* Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#ifndef PACKAGE_FILE_H
#define PACKAGE_FILE_H
#include <Node.h>
#include <package/PackageInfo.h>
#include <NotOwningEntryRef.h>
#include <Referenceable.h>
#include <util/OpenHashTable.h>
using namespace BPackageKit;
class PackageFileManager;
class PackageFile : public BReferenceable {
public:
PackageFile();
~PackageFile();
status_t Init(const entry_ref& entryRef,
PackageFileManager* owner);
const node_ref& NodeRef() const
{ return fNodeRef; }
const BString& FileName() const
{ return fFileName; }
const node_ref& DirectoryRef() const
{ return fDirectoryRef; }
void SetDirectoryRef(const node_ref& directoryRef)
{ fDirectoryRef = directoryRef; }
NotOwningEntryRef EntryRef() const;
const BPackageInfo & Info() const
{ return fInfo; }
BString RevisionedName() const;
BString RevisionedNameThrows() const;
int32 EntryCreatedIgnoreLevel() const
{ return fIgnoreEntryCreated; }
void IncrementEntryCreatedIgnoreLevel()
{ fIgnoreEntryCreated++; }
void DecrementEntryCreatedIgnoreLevel()
{ fIgnoreEntryCreated--; }
int32 EntryRemovedIgnoreLevel() const
{ return fIgnoreEntryRemoved; }
void IncrementEntryRemovedIgnoreLevel()
{ fIgnoreEntryRemoved++; }
void DecrementEntryRemovedIgnoreLevel()
{ fIgnoreEntryRemoved--; }
PackageFile*& EntryRefHashTableNext()
{ return fEntryRefHashTableNext; }
protected:
virtual void LastReferenceReleased();
private:
node_ref fNodeRef;
node_ref fDirectoryRef;
BString fFileName;
BPackageInfo fInfo;
PackageFile* fEntryRefHashTableNext;
PackageFileManager* fOwner;
int32 fIgnoreEntryCreated;
int32 fIgnoreEntryRemoved;
};
inline NotOwningEntryRef
PackageFile::EntryRef() const
{
return NotOwningEntryRef(fDirectoryRef, fFileName);
}
struct PackageFileEntryRefHashDefinition {
typedef entry_ref KeyType;
typedef PackageFile ValueType;
size_t HashKey(const entry_ref& key) const
{
size_t hash = BString::HashValue(key.name);
hash ^= (size_t)key.device;
hash ^= (size_t)key.directory;
return hash;
}
size_t Hash(const PackageFile* value) const
{
return HashKey(value->EntryRef());
}
bool Compare(const entry_ref& key, const PackageFile* value) const
{
return value->EntryRef() == key;
}
PackageFile*& GetLink(PackageFile* value) const
{
return value->EntryRefHashTableNext();
}
};
typedef BOpenHashTable<PackageFileEntryRefHashDefinition>
PackageFileEntryRefHashTable;
#endif // PACKAGE_FILE_H
+106
View File
@@ -0,0 +1,106 @@
/*
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "PackageFileManager.h"
#include <Locker.h>
#include <AutoLocker.h>
#include "DebugSupport.h"
#include "Package.h"
PackageFileManager::PackageFileManager(BLocker& lock)
:
fLock(lock),
fFilesByEntryRef()
{
}
PackageFileManager::~PackageFileManager()
{
}
status_t
PackageFileManager::Init()
{
return fFilesByEntryRef.Init();
}
status_t
PackageFileManager::GetPackageFile(const entry_ref& entryRef,
PackageFile*& _file)
{
AutoLocker<BLocker> locker(fLock);
PackageFile* file = fFilesByEntryRef.Lookup(entryRef);
if (file != NULL) {
if (file->AcquireReference() > 0) {
_file = file;
return B_OK;
}
// File already full dereferenced. It is about to be deleted.
fFilesByEntryRef.Remove(file);
}
file = new(std::nothrow) PackageFile;
if (file == NULL)
RETURN_ERROR(B_NO_MEMORY);
status_t error = file->Init(entryRef, this);
if (error != B_OK) {
delete file;
return error;
}
fFilesByEntryRef.Insert(file);
_file = file;
return B_OK;
}
status_t
PackageFileManager::CreatePackage(const entry_ref& entryRef, Package*& _package)
{
PackageFile* packageFile;
status_t error = GetPackageFile(entryRef, packageFile);
if (error != B_OK)
RETURN_ERROR(error);
BReference<PackageFile> packageFileReference(packageFile, true);
_package = new(std::nothrow) Package(packageFile);
RETURN_ERROR(_package != NULL ? B_OK : B_NO_MEMORY);
}
void
PackageFileManager::PackageFileMoved(PackageFile* file,
const node_ref& newDirectory)
{
if (newDirectory == file->DirectoryRef())
return;
AutoLocker<BLocker> locker(fLock);
fFilesByEntryRef.Remove(file);
file->SetDirectoryRef(newDirectory);
fFilesByEntryRef.Insert(file);
}
void
PackageFileManager::RemovePackageFile(PackageFile* file)
{
AutoLocker<BLocker> locker(fLock);
fFilesByEntryRef.Remove(file);
}
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_FILE_MANAGER_H
#define PACKAGE_FILE_MANAGER_H
#include "PackageFile.h"
class BLocker;
class Package;
class PackageFileManager {
public:
PackageFileManager(BLocker& lock);
~PackageFileManager();
status_t Init();
status_t GetPackageFile(const entry_ref& entryRef,
PackageFile*& _file);
// returns a reference
status_t CreatePackage(const entry_ref& entryRef,
Package*& _package);
void PackageFileMoved(PackageFile* file,
const node_ref& newDirectory);
// conceptually private
void RemovePackageFile(PackageFile* file);
private:
typedef PackageFileEntryRefHashTable EntryRefTable;
private:
BLocker& fLock;
EntryRefTable fFilesByEntryRef;
};
#endif // PACKAGE_FILE_MANAGER_H
+304 -70
View File
@@ -39,6 +39,7 @@
#include "Constants.h"
#include "DebugSupport.h"
#include "Exception.h"
#include "PackageFileManager.h"
#include "VolumeState.h"
@@ -141,7 +142,10 @@ Volume::Volume(BLooper* looper)
fPackagesDirectoryCount(0),
fRoot(NULL),
fListener(NULL),
fState(NULL),
fPackageFileManager(NULL),
fLatestState(NULL),
fActiveState(NULL),
fLock("volume"),
fPendingNodeMonitorEventsLock("pending node monitor events"),
fPendingNodeMonitorEvents(),
fNodeMonitorEventHandleTime(0),
@@ -159,23 +163,41 @@ Volume::~Volume()
Unmounted();
// need for error case in InitPackages()
delete [] fPackagesDirectories;
delete fState;
delete[] fPackagesDirectories;
delete fPackageFileManager;
_SetLatestState(NULL, true);
}
status_t
Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef)
{
fState = new(std::nothrow) VolumeState;
if (fState == NULL || !fState->Init())
status_t error = fLock.InitCheck();
if (error != B_OK)
return error;
error = fPendingNodeMonitorEventsLock.InitCheck();
if (error != B_OK)
return error;
fLatestState = new(std::nothrow) VolumeState;
if (fLatestState == NULL || !fLatestState->Init())
RETURN_ERROR(B_NO_MEMORY);
fPackageFileManager = new(std::nothrow) PackageFileManager(fLock);
if (fPackageFileManager == NULL)
RETURN_ERROR(B_NO_MEMORY);
error = fPackageFileManager->Init();
if (error != B_OK)
RETURN_ERROR(error);
fRootDirectoryRef = rootDirectoryRef;
// open the root directory
BDirectory directory;
status_t error = directory.SetTo(&fRootDirectoryRef);
error = directory.SetTo(&fRootDirectoryRef);
if (error != B_OK) {
ERROR("Volume::Init(): failed to open root directory: %s\n",
strerror(error));
@@ -293,6 +315,10 @@ Volume::InitPackages(Listener* listener)
if (error != B_OK)
RETURN_ERROR(error);
error = _InitLatestState();
if (error != B_OK)
RETURN_ERROR(error);
error = _GetActivePackages(fd);
if (error != B_OK)
RETURN_ERROR(error);
@@ -311,8 +337,8 @@ Volume::InitPackages(Listener* listener)
status_t
Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly)
{
for (PackageFileNameHashTable::Iterator it = fState->ByFileNameIterator();
it.HasNext();) {
for (PackageFileNameHashTable::Iterator it
= fLatestState->ByFileNameIterator(); it.HasNext();) {
Package* package = it.Next();
if (activeOnly && !package->IsActive())
continue;
@@ -419,13 +445,13 @@ INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume);
void
Volume::HandleGetLocationInfoRequest(BMessage* message)
{
AutoLocker<VolumeState> stateLocker(fState);
AutoLocker<BLocker> locker(fLock);
// If the cached reply message is up-to-date, just send it.
int64 changeCount;
if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK
&& changeCount == fState->ChangeCount()) {
stateLocker.Unlock();
&& changeCount == fLatestState->ChangeCount()) {
locker.Unlock();
message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
kCommunicationTimeout);
return;
@@ -445,8 +471,8 @@ Volume::HandleGetLocationInfoRequest(BMessage* message)
return;
}
for (PackageFileNameHashTable::Iterator it = fState->ByFileNameIterator();
it.HasNext();) {
for (PackageFileNameHashTable::Iterator it
= fLatestState->ByFileNameIterator(); it.HasNext();) {
Package* package = it.Next();
const char* fieldName = package->IsActive()
? "active packages" : "inactive packages";
@@ -458,12 +484,12 @@ Volume::HandleGetLocationInfoRequest(BMessage* message)
}
}
if (fLocationInfoReply.AddInt64("change count", fState->ChangeCount())
if (fLocationInfoReply.AddInt64("change count", fLatestState->ChangeCount())
!= B_OK) {
return;
}
stateLocker.Unlock();
locker.Unlock();
message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
kCommunicationTimeout);
@@ -481,10 +507,13 @@ Volume::HandleCommitTransactionRequest(BMessage* message)
// perform the request
PackageSet dummy;
CommitTransactionHandler handler(this, fState, dummy, dummy);
CommitTransactionHandler handler(this, fPackageFileManager, fLatestState,
fLatestState == fActiveState, dummy, dummy);
int32 error;
try {
handler.HandleRequest(message, &reply);
_SetLatestState(handler.DetachVolumeState(),
handler.IsActiveVolumeState());
error = B_DAEMON_OK;
} catch (Exception& exception) {
error = exception.Error();
@@ -606,7 +635,7 @@ Volume::PackagesDirectoryRef() const
PackageFileNameHashTable::Iterator
Volume::PackagesByFileNameIterator() const
{
return fState->ByFileNameIterator();
return fLatestState->ByFileNameIterator();
}
@@ -660,11 +689,14 @@ Volume::ProcessPendingPackageActivationChanges()
return;
// perform the request
CommitTransactionHandler handler(this, fState, fPackagesToBeActivated,
CommitTransactionHandler handler(this, fPackageFileManager, fLatestState,
fLatestState == fActiveState, fPackagesToBeActivated,
fPackagesToBeDeactivated);
int32 error;
try {
handler.HandleRequest(fPackagesToBeActivated, fPackagesToBeDeactivated);
_SetLatestState(handler.DetachVolumeState(),
handler.IsActiveVolumeState());
error = B_DAEMON_OK;
} catch (Exception& exception) {
error = exception.Error();
@@ -724,7 +756,8 @@ Volume::CreateTransaction(BPackageInstallationLocation location,
}
// init the transaction
error = _transaction.SetTo(location, fState->ChangeCount(), directoryName);
error = _transaction.SetTo(location, fLatestState->ChangeCount(),
directoryName);
if (error != B_OK) {
BEntry entry;
_transactionDirectory.GetEntry(&entry);
@@ -745,11 +778,14 @@ Volume::CommitTransaction(const BActivationTransaction& transaction,
BDaemonClient::BCommitTransactionResult& _result)
{
// perform the request
CommitTransactionHandler handler(this, fState, packagesAlreadyAdded,
CommitTransactionHandler handler(this, fPackageFileManager, fLatestState,
fLatestState == fActiveState, packagesAlreadyAdded,
packagesAlreadyRemoved);
int32 error;
try {
handler.HandleRequest(transaction, NULL);
_SetLatestState(handler.DetachVolumeState(),
handler.IsActiveVolumeState());
error = B_DAEMON_OK;
_result.SetTo(error, BString(), BString(),
handler.OldStateDirectoryName());
@@ -851,10 +887,10 @@ Volume::_PackagesEntryCreated(const char* name)
{
INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name);
// Ignore the event, if the package is already known.
Package* package = fState->FindPackage(name);
Package* package = fLatestState->FindPackage(name);
if (package != NULL) {
if (package->EntryCreatedIgnoreLevel() > 0) {
package->DecrementEntryCreatedIgnoreLevel();
if (package->File()->EntryCreatedIgnoreLevel() > 0) {
package->File()->DecrementEntryCreatedIgnoreLevel();
} else {
WARN("node monitoring created event for already known entry "
"\"%s\"\n", name);
@@ -863,22 +899,17 @@ INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name);
return;
}
package = new(std::nothrow) Package;
if (package == NULL) {
ERROR("out of memory\n");
return;
}
ObjectDeleter<Package> packageDeleter(package);
NotOwningEntryRef entry(PackagesDirectoryRef(), name);
status_t error = package->Init(entry);
status_t error = fPackageFileManager->CreatePackage(
NotOwningEntryRef(PackagesDirectoryRef(), name),
package);
if (error != B_OK) {
ERROR("failed to init package for file \"%s\"\n", name);
return;
}
fState->AddPackage(package);
packageDeleter.Detach();
fLock.Lock();
fLatestState->AddPackage(package);
fLock.Unlock();
try {
fPackagesToBeActivated.insert(package);
@@ -893,13 +924,13 @@ void
Volume::_PackagesEntryRemoved(const char* name)
{
INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name);
Package* package = fState->FindPackage(name);
Package* package = fLatestState->FindPackage(name);
if (package == NULL)
return;
// Ignore the event, if we generated it ourselves.
if (package->EntryRemovedIgnoreLevel() > 0) {
package->DecrementEntryRemovedIgnoreLevel();
if (package->File()->EntryRemovedIgnoreLevel() > 0) {
package->File()->DecrementEntryRemovedIgnoreLevel();
return;
}
@@ -911,7 +942,8 @@ INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name);
// If the package isn't active, just remove it for good.
if (!package->IsActive()) {
fState->RemovePackage(package);
AutoLocker<BLocker> locker(fLock);
fLatestState->RemovePackage(package);
delete package;
return;
}
@@ -942,15 +974,11 @@ Volume::_ReadPackagesDirectory()
if (!BString(entry.name).EndsWith(kPackageFileNameExtension))
continue;
Package* package = new(std::nothrow) Package;
if (package == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<Package> packageDeleter(package);
status_t error = package->Init(entry);
Package* package;
status_t error = fPackageFileManager->CreatePackage(entry, package);
if (error == B_OK) {
fState->AddPackage(package);
packageDeleter.Detach();
AutoLocker<BLocker> locker(fLock);
fLatestState->AddPackage(package);
}
}
@@ -958,10 +986,115 @@ Volume::_ReadPackagesDirectory()
}
status_t
Volume::_InitLatestState()
{
if (_InitLatestStateFromActivatedPackages() == B_OK)
return B_OK;
INFORM("Failed to get activated packages info from activated packages file."
" Assuming all package files in package directory are activated.\n");
AutoLocker<BLocker> locker(fLock);
for (PackageFileNameHashTable::Iterator it
= fLatestState->ByFileNameIterator();
Package* package = it.Next();) {
fLatestState->SetPackageActive(package, true);
}
return B_OK;
}
status_t
Volume::_InitLatestStateFromActivatedPackages()
{
// try reading the activation file
NotOwningEntryRef entryRef(PackagesDirectoryRef(), kActivationFileName);
BFile file;
status_t error = file.SetTo(&entryRef, B_READ_ONLY);
if (error != B_OK) {
INFORM("Failed to open packages activation file: %s\n",
strerror(error));
RETURN_ERROR(error);
}
// read the whole file into memory to simplify things
off_t size;
error = file.GetSize(&size);
if (error != B_OK) {
ERROR("Failed to packages activation file size: %s\n",
strerror(error));
RETURN_ERROR(error);
}
if (size > (off_t)kMaxActivationFileSize) {
ERROR("The packages activation file is too big.\n");
RETURN_ERROR(B_BAD_DATA);
}
char* fileContent = (char*)malloc(size + 1);
if (fileContent == NULL)
RETURN_ERROR(B_NO_MEMORY);
MemoryDeleter fileContentDeleter(fileContent);
ssize_t bytesRead = file.Read(fileContent, size);
if (bytesRead < 0) {
ERROR("Failed to read packages activation file: %s\n",
strerror(bytesRead));
RETURN_ERROR(errno);
}
if (bytesRead != size) {
ERROR("Failed to read whole packages activation file.\n");
RETURN_ERROR(B_ERROR);
}
// null-terminate to simplify parsing
fileContent[size] = '\0';
AutoLocker<BLocker> locker(fLock);
// parse the file and mark the respective packages active
const char* packageName = fileContent;
char* const fileContentEnd = fileContent + size;
while (packageName < fileContentEnd) {
char* packageNameEnd = strchr(packageName, '\n');
if (packageNameEnd == NULL)
packageNameEnd = fileContentEnd;
// skip empty lines
if (packageName == packageNameEnd) {
packageName++;
continue;
}
*packageNameEnd = '\0';
if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
ERROR("Invalid packages activation file content.\n");
RETURN_ERROR(B_BAD_DATA);
}
Package* package = fLatestState->FindPackage(packageName);
if (package != NULL) {
fLatestState->SetPackageActive(package, true);
} else {
WARN("Package \"%s\" from activation file not in packages "
"directory.\n", packageName);
}
packageName = packageNameEnd + 1;
}
return B_OK;
}
status_t
Volume::_GetActivePackages(int fd)
{
// TODO: Adjust for old state support!
// get the info from packagefs
PackageFSGetPackageInfosRequest* request = NULL;
MemoryDeleter requestDeleter;
size_t bufferSize = 64 * 1024;
@@ -985,38 +1118,139 @@ Volume::_GetActivePackages(int fd)
requestDeleter.Unset();
}
// mark the returned packages active
INFORM("latest volume state:\n");
_DumpState(fLatestState);
// check whether that matches the expected state
if (_CheckActivePackagesMatchLatestState(request)) {
INFORM("The latest volume state is also the currently active one\n");
fActiveState = fLatestState;
return B_OK;
}
// There's a mismatch. We need a new state that reflects the actual
// activation situation.
VolumeState* state = new(std::nothrow) VolumeState;
if (state == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<VolumeState> stateDeleter(state);
for (uint32 i = 0; i < request->packageCount; i++) {
Package* package = fState->FindPackage(
node_ref(request->infos[i].packageDeviceID,
request->infos[i].packageNodeID));
if (package == NULL) {
WARN("active package (dev: %" B_PRIdDEV ", node: %" B_PRIdINO ") "
"not found in package directory\n",
request->infos[i].packageDeviceID,
request->infos[i].packageNodeID);
// TODO: Deactivate the package right away?
const PackageFSPackageInfo& info = request->infos[i];
NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID,
info.name);
Package* package;
status_t error = fPackageFileManager->CreatePackage(entryRef, package);
if (error != B_OK) {
WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %"
B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID,
info.directoryNodeID, info.name, strerror(error));
continue;
}
fState->SetPackageActive(package, true);
INFORM("active package: \"%s\"\n", package->FileName().String());
state->AddPackage(package);
state->SetPackageActive(package, true);
}
for (PackageNodeRefHashTable::Iterator it = fState->ByNodeRefIterator();
it.HasNext();) {
Package* package = it.Next();
if (!package->IsActive())
INFORM("inactive package: \"%s\"\n", package->FileName().String());
INFORM("currently active volume state:\n");
_DumpState(state);
fActiveState = stateDeleter.Detach();
return B_OK;
}
// INFORM("%" B_PRIu32 " active packages:\n", request->packageCount);
// for (uint32 i = 0; i < request->packageCount; i++) {
// INFORM(" dev: %" B_PRIdDEV ", node: %" B_PRIdINO "\n",
// request->infos[i].packageDeviceID, request->infos[i].packageNodeID);
// }
return B_OK;
bool
Volume::_CheckActivePackagesMatchLatestState(
PackageFSGetPackageInfosRequest* request)
{
if (fPackagesDirectoryCount != 1) {
INFORM("An old packages state (\"%s\") seems to be active.\n",
fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String());
return false;
}
const node_ref packagesDirRef(PackagesDirectoryRef());
// mark the returned packages active
for (uint32 i = 0; i < request->packageCount; i++) {
const PackageFSPackageInfo& info = request->infos[i];
if (node_ref(info.directoryDeviceID, info.directoryNodeID)
!= packagesDirRef) {
WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
") not in packages directory\n", info.name,
info.packageDeviceID, info.packageNodeID);
return false;
}
Package* package = fLatestState->FindPackage(
node_ref(info.packageDeviceID, info.packageNodeID));
if (package == NULL || !package->IsActive()) {
WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
") not %s\n", info.name,
info.packageDeviceID, info.packageNodeID,
package == NULL
? "found in packages directory" : "supposed to be active");
return false;
}
}
// Check whether there are packages that aren't active but should be.
uint32 count = 0;
for (PackageNodeRefHashTable::Iterator it
= fLatestState->ByNodeRefIterator(); it.HasNext();) {
Package* package = it.Next();
if (package->IsActive())
count++;
}
if (count != request->packageCount) {
INFORM("There seem to be packages in the packages directory that "
"should be active.\n");
return false;
}
return true;
}
void
Volume::_SetLatestState(VolumeState* state, bool isActive)
{
AutoLocker<BLocker> locker(fLock);
if (isActive) {
if (fLatestState != fActiveState)
delete fActiveState;
fActiveState = state;
}
delete fLatestState;
fLatestState = state;
}
void
Volume::_DumpState(VolumeState* state)
{
uint32 inactiveCount = 0;
for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
it.HasNext();) {
Package* package = it.Next();
if (package->IsActive()) {
INFORM("active package: \"%s\"\n", package->FileName().String());
} else
inactiveCount++;
}
if (inactiveCount == 0)
return;
for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
it.HasNext();) {
Package* package = it.Next();
if (!package->IsActive())
INFORM("inactive package: \"%s\"\n", package->FileName().String());
}
}
+15 -7
View File
@@ -33,9 +33,9 @@
//
// The only thread synchronization needed is for the status information accessed
// by HandleGetLocationInfoRequest() and modified by the job thread. The data
// are encapsulated in a VolumeState object which contains a lock. The lock
// must be held by the app thread when accessing the data (it reads only) and
// by the job thread when modifying the data (not needed when reading).
// are encapsulated in a VolumeState, which is protected by Volume::fLock. The
// lock must be held by the app thread when accessing the data (it reads only)
// and by the job thread when modifying the data (not needed when reading).
using BPackageKit::BPrivate::BActivationTransaction;
@@ -44,6 +44,7 @@ using BPackageKit::BPrivate::BDaemonClient;
class BDirectory;
class CommitTransactionHandler;
class PackageFileManager;
class Root;
class VolumeState;
@@ -154,7 +155,14 @@ private:
void _PackagesEntryRemoved(const char* name);
status_t _ReadPackagesDirectory();
status_t _InitLatestState();
status_t _InitLatestStateFromActivatedPackages();
status_t _GetActivePackages(int fd);
bool _CheckActivePackagesMatchLatestState(
PackageFSGetPackageInfosRequest* request);
void _SetLatestState(VolumeState* state,
bool isActive);
void _DumpState(VolumeState* state);
status_t _AddRepository(BSolver* solver,
BSolverRepository& repository,
@@ -164,9 +172,6 @@ private:
const RelativePath& path, bool create,
BDirectory& _directory);
status_t _OpenSettingsRootDirectory(
BDirectory& _directory);
private:
BString fPath;
PackageFSMountType fMountType;
@@ -175,7 +180,10 @@ private:
uint32 fPackagesDirectoryCount;
Root* fRoot;
Listener* fListener;
VolumeState* fState;
PackageFileManager* fPackageFileManager;
VolumeState* fLatestState;
VolumeState* fActiveState;
BLocker fLock;
BLocker fPendingNodeMonitorEventsLock;
NodeMonitorEventList fPendingNodeMonitorEvents;
bigtime_t fNodeMonitorEventHandleTime;
+27 -14
View File
@@ -9,12 +9,12 @@
#include "VolumeState.h"
#include <AutoDeleter.h>
#include <AutoLocker.h>
VolumeState::VolumeState()
:
fLock("volume state"),
fPackagesByFileName(),
fPackagesByNodeRef(),
fChangeCount(0)
@@ -38,7 +38,7 @@ VolumeState::~VolumeState()
bool
VolumeState::Init()
{
return fLock.InitCheck() == B_OK && fPackagesByFileName.Init() == B_OK
return fPackagesByFileName.Init() == B_OK
&& fPackagesByNodeRef.Init() == B_OK;
}
@@ -46,25 +46,26 @@ VolumeState::Init()
void
VolumeState::AddPackage(Package* package)
{
AutoLocker<BLocker> locker(fLock);
fPackagesByFileName.Insert(package);
fPackagesByNodeRef.Insert(package);
fChangeCount++;
}
void
VolumeState::RemovePackage(Package* package)
{
AutoLocker<BLocker> locker(fLock);
_RemovePackage(package);
fPackagesByFileName.Remove(package);
fPackagesByNodeRef.Remove(package);
fChangeCount++;
}
void
VolumeState::SetPackageActive(Package* package, bool active)
{
AutoLocker<BLocker> locker(fLock);
package->SetActive(active);
fChangeCount++;
}
@@ -72,8 +73,6 @@ void
VolumeState::ActivationChanged(const PackageSet& activatedPackage,
const PackageSet& deactivatePackages)
{
AutoLocker<BLocker> locker(fLock);
for (PackageSet::iterator it = activatedPackage.begin();
it != activatedPackage.end(); ++it) {
(*it)->SetActive(true);
@@ -83,16 +82,30 @@ VolumeState::ActivationChanged(const PackageSet& activatedPackage,
for (PackageSet::iterator it = deactivatePackages.begin();
it != deactivatePackages.end(); ++it) {
Package* package = *it;
_RemovePackage(package);
RemovePackage(package);
delete package;
}
}
void
VolumeState::_RemovePackage(Package* package)
VolumeState*
VolumeState::Clone() const
{
fPackagesByFileName.Remove(package);
fPackagesByNodeRef.Remove(package);
fChangeCount++;
VolumeState* clone = new(std::nothrow) VolumeState;
if (clone == NULL)
return NULL;
ObjectDeleter<VolumeState> cloneDeleter(clone);
for (PackageFileNameHashTable::Iterator it
= fPackagesByFileName.GetIterator();
Package* package = it.Next();) {
Package* clonedPackage = package->Clone();
if (clonedPackage == NULL)
return NULL;
clone->AddPackage(clonedPackage);
}
clone->fChangeCount = fChangeCount;
return cloneDeleter.Detach();
}
+2 -8
View File
@@ -9,8 +9,6 @@
#define VOLUME_STATE_H
#include <Locker.h>
#include "Package.h"
@@ -21,11 +19,6 @@ public:
bool Init();
bool Lock()
{ return fLock.Lock(); }
void Unlock()
{ fLock.Unlock(); }
int64 ChangeCount() const
{ return fChangeCount; }
@@ -44,11 +37,12 @@ public:
const PackageSet& activatedPackage,
const PackageSet& deactivatePackages);
VolumeState* Clone() const;
private:
void _RemovePackage(Package* package);
private:
BLocker fLock;
PackageFileNameHashTable fPackagesByFileName;
PackageNodeRefHashTable fPackagesByNodeRef;
int64 fChangeCount;