[haiku-commits] haiku: hrev47189 - src/servers/package

  • From: ingo_weinhold@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Thu, 1 May 2014 18:18:54 +0200 (CEST)

hrev47189 adds 2 changesets to branch 'master'
old head: 2354b8ac00da0383f2e8d59620a64afb3118c500
new head: fe28d36222e1eddf0686a3d9ea98baf59de17cf1
overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=fe28d36+%5E2354b8a

----------------------------------------------------------------------------

2dbf816: 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.

fe28d36: package daemon: Don't apply system package changes immediately
  
  When a system package is going to be deactivated, activate/deactivate
  the packages of the whole transaction only to the latest state.
  Afterward latest state and active state will differ.

                                    [ Ingo Weinhold <ingo_weinhold@xxxxxx> ]

----------------------------------------------------------------------------

14 files changed, 894 insertions(+), 230 deletions(-)
src/servers/package/CommitTransactionHandler.cpp | 160 +++++---
src/servers/package/CommitTransactionHandler.h   |  15 +
src/servers/package/Constants.h                  |   3 +
src/servers/package/Jamfile                      |   2 +
src/servers/package/Package.cpp                  |  69 +---
src/servers/package/Package.h                    |  47 +--
src/servers/package/PackageFile.cpp              | 106 ++++++
src/servers/package/PackageFile.h                | 124 ++++++
src/servers/package/PackageFileManager.cpp       | 106 ++++++
src/servers/package/PackageFileManager.h         |  45 +++
src/servers/package/Volume.cpp                   | 374 +++++++++++++++----
src/servers/package/Volume.h                     |  22 +-
src/servers/package/VolumeState.cpp              |  41 +-
src/servers/package/VolumeState.h                |  10 +-

############################################################################

Commit:      2dbf8167fec7282ac384273014903a1bbdfa49e6
URL:         http://cgit.haiku-os.org/haiku/commit/?id=2dbf816
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Sun Apr 27 20:37:50 2014 UTC

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.

----------------------------------------------------------------------------

diff --git a/src/servers/package/CommitTransactionHandler.cpp 
b/src/servers/package/CommitTransactionHandler.cpp
index eef8bb9..87c7eaa 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 2a2c9cc..1b8f722 100644
--- a/src/servers/package/CommitTransactionHandler.h
+++ b/src/servers/package/CommitTransactionHandler.h
@@ -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;
diff --git a/src/servers/package/Constants.h b/src/servers/package/Constants.h
index 3fcbde9..d3c8686 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 3db078b..58ca48c 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 2f85b96..224b8cc 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 ce22531..d89d3e8 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 <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;
 };
 
 
diff --git a/src/servers/package/PackageFile.cpp 
b/src/servers/package/PackageFile.cpp
new file mode 100644
index 0000000..143da0a
--- /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 <ingo_weinhold@xxxxxx>
+ */
+
+
+#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;
+}
diff --git a/src/servers/package/PackageFile.h 
b/src/servers/package/PackageFile.h
new file mode 100644
index 0000000..c1f1ece
--- /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 <ingo_weinhold@xxxxxx>
+ */
+#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
diff --git a/src/servers/package/PackageFileManager.cpp 
b/src/servers/package/PackageFileManager.cpp
new file mode 100644
index 0000000..04a4301
--- /dev/null
+++ b/src/servers/package/PackageFileManager.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014, Ingo Weinhold, ingo_weinhold@xxxxxx.
+ * 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);
+}
diff --git a/src/servers/package/PackageFileManager.h 
b/src/servers/package/PackageFileManager.h
new file mode 100644
index 0000000..e8a0358
--- /dev/null
+++ b/src/servers/package/PackageFileManager.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014, Ingo Weinhold, ingo_weinhold@xxxxxx.
+ * 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 8b2d1c9..15787ca 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<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,16 +974,117 @@ 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);
+               }
+       }
+
+       return B_OK;
+}
+
+
+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;
@@ -961,7 +1094,7 @@ Volume::_ReadPackagesDirectory()
 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());
+       }
 }
 
 
diff --git a/src/servers/package/Volume.h b/src/servers/package/Volume.h
index f364a3f..305ee68 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 10f5b72..73bfdb0 100644
--- a/src/servers/package/VolumeState.cpp
+++ b/src/servers/package/VolumeState.cpp
@@ -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();
 }
diff --git a/src/servers/package/VolumeState.h 
b/src/servers/package/VolumeState.h
index 6d73a06..ebd0f4a 100644
--- a/src/servers/package/VolumeState.h
+++ b/src/servers/package/VolumeState.h
@@ -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;

############################################################################

Revision:    hrev47189
Commit:      fe28d36222e1eddf0686a3d9ea98baf59de17cf1
URL:         http://cgit.haiku-os.org/haiku/commit/?id=fe28d36
Author:      Ingo Weinhold <ingo_weinhold@xxxxxx>
Date:        Wed Apr 30 15:02:42 2014 UTC

package daemon: Don't apply system package changes immediately

When a system package is going to be deactivated, activate/deactivate
the packages of the whole transaction only to the latest state.
Afterward latest state and active state will differ.

----------------------------------------------------------------------------

diff --git a/src/servers/package/CommitTransactionHandler.cpp 
b/src/servers/package/CommitTransactionHandler.cpp
index 87c7eaa..8e738b5 100644
--- a/src/servers/package/CommitTransactionHandler.cpp
+++ b/src/servers/package/CommitTransactionHandler.cpp
@@ -368,6 +368,11 @@ CommitTransactionHandler::_RemovePackagesToDeactivate()
        for (PackageSet::const_iterator it = fPackagesToDeactivate.begin();
                it != fPackagesToDeactivate.end(); ++it) {
                Package* package = *it;
+
+               // When deactivating (or updating) a system package, don't do 
that live.
+               if (_IsSystemPackage(package))
+                       fVolumeStateIsActive = false;
+
                if (fPackagesAlreadyRemoved.find(package)
                                != fPackagesAlreadyRemoved.end()) {
                        fRemovedPackages.insert(package);
@@ -1508,6 +1513,24 @@ CommitTransactionHandler::_FillInActivationChangeItem(
 }
 
 
+bool
+CommitTransactionHandler::_IsSystemPackage(Package* package)
+{
+       // package name should be "haiku[_<arch>]"
+       const BString& name = package->Info().Name();
+       if (!name.StartsWith("haiku"))
+               return false;
+       if (name.Length() == 5)
+               return true;
+       if (name[5] != '_')
+               return false;
+
+       BPackageArchitecture architecture;
+       return BPackageInfo::GetArchitectureByName(name.String() + 6, 
architecture)
+               == B_OK;
+}
+
+
 /*static*/ BString
 CommitTransactionHandler::_GetPath(const FSUtils::Entry& entry,
        const BString& fallback)
diff --git a/src/servers/package/CommitTransactionHandler.h 
b/src/servers/package/CommitTransactionHandler.h
index 1b8f722..ce30f92 100644
--- a/src/servers/package/CommitTransactionHandler.h
+++ b/src/servers/package/CommitTransactionHandler.h
@@ -127,6 +127,8 @@ private:
                                                                        
PackageFSActivationChangeType type,
                                                                        
Package* package, char*& nameBuffer);
 
+                       bool                            
_IsSystemPackage(Package* package);
+
        static  BString                         _GetPath(const FSUtils::Entry& 
entry,
                                                                        const 
BString& fallback);
 


Other related posts:

  • » [haiku-commits] haiku: hrev47189 - src/servers/package - ingo_weinhold