diff --git a/src/servers/package/CommitTransactionHandler.cpp b/src/servers/package/CommitTransactionHandler.cpp index eef8bb9d3f..87c7eaabb2 100644 --- a/src/servers/package/CommitTransactionHandler.cpp +++ b/src/servers/package/CommitTransactionHandler.cpp @@ -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); } diff --git a/src/servers/package/CommitTransactionHandler.h b/src/servers/package/CommitTransactionHandler.h index 2a2c9ccee4..1b8f72274f 100644 --- a/src/servers/package/CommitTransactionHandler.h +++ b/src/servers/package/CommitTransactionHandler.h @@ -25,7 +25,9 @@ typedef std::set 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 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; diff --git a/src/servers/package/Constants.h b/src/servers/package/Constants.h index 3fcbde9fcb..d3c8686422 100644 --- a/src/servers/package/Constants.h +++ b/src/servers/package/Constants.h @@ -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 diff --git a/src/servers/package/Jamfile b/src/servers/package/Jamfile index 3db078b7ec..58ca48c380 100644 --- a/src/servers/package/Jamfile +++ b/src/servers/package/Jamfile @@ -15,6 +15,8 @@ Server package_daemon JobQueue.cpp Package.cpp PackageDaemon.cpp + PackageFile.cpp + PackageFileManager.cpp PackageManager.cpp ProblemWindow.cpp ResultWindow.cpp diff --git a/src/servers/package/Package.cpp b/src/servers/package/Package.cpp index 2f85b9604c..224b8cc86c 100644 --- a/src/servers/package/Package.cpp +++ b/src/servers/package/Package.cpp @@ -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; } diff --git a/src/servers/package/Package.h b/src/servers/package/Package.h index ce22531429..d89d3e89a1 100644 --- a/src/servers/package/Package.h +++ b/src/servers/package/Package.h @@ -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 -#include -#include -#include - -#include +#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; }; diff --git a/src/servers/package/PackageFile.cpp b/src/servers/package/PackageFile.cpp new file mode 100644 index 0000000000..143da0a8c1 --- /dev/null +++ b/src/servers/package/PackageFile.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + + +#include "PackageFile.h" + +#include + +#include + +#include + +#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; +} diff --git a/src/servers/package/PackageFile.h b/src/servers/package/PackageFile.h new file mode 100644 index 0000000000..c1f1ecef87 --- /dev/null +++ b/src/servers/package/PackageFile.h @@ -0,0 +1,124 @@ +/* + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ +#ifndef PACKAGE_FILE_H +#define PACKAGE_FILE_H + + +#include +#include + +#include +#include +#include + + +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 + PackageFileEntryRefHashTable; + + +#endif // PACKAGE_FILE_H diff --git a/src/servers/package/PackageFileManager.cpp b/src/servers/package/PackageFileManager.cpp new file mode 100644 index 0000000000..04a430192c --- /dev/null +++ b/src/servers/package/PackageFileManager.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageFileManager.h" + +#include + +#include + +#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 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 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 locker(fLock); + + fFilesByEntryRef.Remove(file); + file->SetDirectoryRef(newDirectory); + fFilesByEntryRef.Insert(file); +} + + +void +PackageFileManager::RemovePackageFile(PackageFile* file) +{ + AutoLocker locker(fLock); + + fFilesByEntryRef.Remove(file); +} diff --git a/src/servers/package/PackageFileManager.h b/src/servers/package/PackageFileManager.h new file mode 100644 index 0000000000..e8a035808b --- /dev/null +++ b/src/servers/package/PackageFileManager.h @@ -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 diff --git a/src/servers/package/Volume.cpp b/src/servers/package/Volume.cpp index 8b2d1c9410..15787ca698 100644 --- a/src/servers/package/Volume.cpp +++ b/src/servers/package/Volume.cpp @@ -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 stateLocker(fState); + AutoLocker 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 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 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 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 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 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 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 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 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()); + } } diff --git a/src/servers/package/Volume.h b/src/servers/package/Volume.h index f364a3f15a..305ee68b1f 100644 --- a/src/servers/package/Volume.h +++ b/src/servers/package/Volume.h @@ -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; diff --git a/src/servers/package/VolumeState.cpp b/src/servers/package/VolumeState.cpp index 10f5b721b8..73bfdb0184 100644 --- a/src/servers/package/VolumeState.cpp +++ b/src/servers/package/VolumeState.cpp @@ -9,12 +9,12 @@ #include "VolumeState.h" +#include #include 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 locker(fLock); fPackagesByFileName.Insert(package); fPackagesByNodeRef.Insert(package); + fChangeCount++; } void VolumeState::RemovePackage(Package* package) { - AutoLocker locker(fLock); - _RemovePackage(package); + fPackagesByFileName.Remove(package); + fPackagesByNodeRef.Remove(package); + fChangeCount++; } void VolumeState::SetPackageActive(Package* package, bool active) { - AutoLocker locker(fLock); package->SetActive(active); + fChangeCount++; } @@ -72,8 +73,6 @@ void VolumeState::ActivationChanged(const PackageSet& activatedPackage, const PackageSet& deactivatePackages) { - AutoLocker 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 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(); } diff --git a/src/servers/package/VolumeState.h b/src/servers/package/VolumeState.h index 6d73a06de4..ebd0f4a069 100644 --- a/src/servers/package/VolumeState.h +++ b/src/servers/package/VolumeState.h @@ -9,8 +9,6 @@ #define VOLUME_STATE_H -#include - #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;