Make UEFI boot-platform build again
[haiku.git] / src / servers / package / Volume.cpp
blob700de901f310dc1c6442242fcf4f80e7d87c1f0c
1 /*
2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
7 */
10 #include "Volume.h"
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <sys/stat.h>
15 #include <time.h>
16 #include <unistd.h>
18 #include <Directory.h>
19 #include <Entry.h>
20 #include <File.h>
21 #include <Looper.h>
22 #include <MessageRunner.h>
23 #include <NodeMonitor.h>
24 #include <Path.h>
25 #include <Roster.h>
26 #include <SymLink.h>
28 #include <package/CommitTransactionResult.h>
29 #include <package/PackageRoster.h>
30 #include <package/solver/Solver.h>
31 #include <package/solver/SolverPackage.h>
32 #include <package/solver/SolverProblem.h>
33 #include <package/solver/SolverProblemSolution.h>
34 #include <package/solver/SolverRepository.h>
35 #include <package/solver/SolverResult.h>
37 #include <AutoDeleter.h>
38 #include <AutoLocker.h>
39 #include <NotOwningEntryRef.h>
40 #include <package/DaemonDefs.h>
41 #include <RosterPrivate.h>
43 #include "CommitTransactionHandler.h"
44 #include "Constants.h"
45 #include "DebugSupport.h"
46 #include "Exception.h"
47 #include "PackageFileManager.h"
48 #include "Root.h"
49 #include "VolumeState.h"
52 using namespace BPackageKit::BPrivate;
55 // #pragma mark - Listener
58 Volume::Listener::~Listener()
63 // #pragma mark - NodeMonitorEvent
66 struct Volume::NodeMonitorEvent
67 : public DoublyLinkedListLinkImpl<NodeMonitorEvent> {
68 public:
69 NodeMonitorEvent(const BString& entryName, bool created)
71 fEntryName(entryName),
72 fCreated(created)
76 const BString& EntryName() const
78 return fEntryName;
81 bool WasCreated() const
83 return fCreated;
86 private:
87 BString fEntryName;
88 bool fCreated;
92 // #pragma mark - PackagesDirectory
95 struct Volume::PackagesDirectory {
96 public:
97 PackagesDirectory()
99 fNodeRef(),
100 fName()
104 void Init(const node_ref& nodeRef, bool isPackagesDir)
106 fNodeRef = nodeRef;
108 if (isPackagesDir)
109 return;
111 BDirectory directory;
112 BEntry entry;
113 if (directory.SetTo(&fNodeRef) == B_OK
114 && directory.GetEntry(&entry) == B_OK) {
115 fName = entry.Name();
118 if (fName.IsEmpty())
119 fName = "unknown state";
122 const node_ref& NodeRef() const
124 return fNodeRef;
127 const BString& Name() const
129 return fName;
132 private:
133 node_ref fNodeRef;
134 BString fName;
138 // #pragma mark - Volume
141 Volume::Volume(BLooper* looper)
143 BHandler(),
144 fPath(),
145 fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM),
146 fRootDirectoryRef(),
147 fPackagesDirectories(NULL),
148 fPackagesDirectoryCount(0),
149 fRoot(NULL),
150 fListener(NULL),
151 fPackageFileManager(NULL),
152 fLatestState(NULL),
153 fActiveState(NULL),
154 fChangeCount(0),
155 fLock("volume"),
156 fPendingNodeMonitorEventsLock("pending node monitor events"),
157 fPendingNodeMonitorEvents(),
158 fNodeMonitorEventHandleTime(0),
159 fPackagesToBeActivated(),
160 fPackagesToBeDeactivated(),
161 fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY),
162 fPendingPackageJobCount(0)
164 looper->AddHandler(this);
168 Volume::~Volume()
170 Unmounted();
171 // needed for error case in InitPackages()
173 _SetLatestState(NULL, true);
175 delete[] fPackagesDirectories;
176 delete fPackageFileManager;
180 status_t
181 Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef)
183 status_t error = fLock.InitCheck();
184 if (error != B_OK)
185 return error;
187 error = fPendingNodeMonitorEventsLock.InitCheck();
188 if (error != B_OK)
189 return error;
191 fLatestState = new(std::nothrow) VolumeState;
192 if (fLatestState == NULL || !fLatestState->Init())
193 RETURN_ERROR(B_NO_MEMORY);
195 fPackageFileManager = new(std::nothrow) PackageFileManager(fLock);
196 if (fPackageFileManager == NULL)
197 RETURN_ERROR(B_NO_MEMORY);
199 error = fPackageFileManager->Init();
200 if (error != B_OK)
201 RETURN_ERROR(error);
203 fRootDirectoryRef = rootDirectoryRef;
205 // open the root directory
206 BDirectory directory;
207 error = directory.SetTo(&fRootDirectoryRef);
208 if (error != B_OK) {
209 ERROR("Volume::Init(): failed to open root directory: %s\n",
210 strerror(error));
211 RETURN_ERROR(error);
214 // get the directory path
215 BEntry entry;
216 error = directory.GetEntry(&entry);
218 BPath path;
219 if (error == B_OK)
220 error = entry.GetPath(&path);
222 if (error != B_OK) {
223 ERROR("Volume::Init(): failed to get root directory path: %s\n",
224 strerror(error));
225 RETURN_ERROR(error);
228 fPath = path.Path();
229 if (fPath.IsEmpty())
230 RETURN_ERROR(B_NO_MEMORY);
232 // get a volume info from the FS
233 int fd = directory.Dup();
234 if (fd < 0) {
235 ERROR("Volume::Init(): failed to get root directory FD: %s\n",
236 strerror(fd));
237 RETURN_ERROR(fd);
239 FileDescriptorCloser fdCloser(fd);
241 // get the volume info from packagefs
242 uint32 maxPackagesDirCount = 16;
243 PackageFSVolumeInfo* info = NULL;
244 MemoryDeleter infoDeleter;
245 size_t bufferSize;
246 for (;;) {
247 bufferSize = sizeof(PackageFSVolumeInfo)
248 + (maxPackagesDirCount - 1) * sizeof(PackageFSDirectoryInfo);
249 info = (PackageFSVolumeInfo*)malloc(bufferSize);
250 if (info == NULL)
251 RETURN_ERROR(B_NO_MEMORY);
252 infoDeleter.SetTo(info);
254 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_VOLUME_INFO, info,
255 bufferSize) != 0) {
256 ERROR("Volume::Init(): failed to get volume info: %s\n",
257 strerror(errno));
258 RETURN_ERROR(errno);
261 if (info->packagesDirectoryCount <= maxPackagesDirCount)
262 break;
264 maxPackagesDirCount = info->packagesDirectoryCount;
265 infoDeleter.Unset();
268 if (info->packagesDirectoryCount < 1) {
269 ERROR("Volume::Init(): got invalid volume info from packagefs\n");
270 RETURN_ERROR(B_BAD_VALUE);
273 fMountType = info->mountType;
275 fPackagesDirectories = new(std::nothrow) PackagesDirectory[
276 info->packagesDirectoryCount];
277 if (fPackagesDirectories == NULL)
278 RETURN_ERROR(B_NO_MEMORY);
280 fPackagesDirectoryCount = info->packagesDirectoryCount;
282 for (uint32 i = 0; i < info->packagesDirectoryCount; i++) {
283 fPackagesDirectories[i].Init(
284 node_ref(info->packagesDirectoryInfos[i].deviceID,
285 info->packagesDirectoryInfos[i].nodeID),
286 i == 0);
289 _packageRootRef.device = info->rootDeviceID;
290 _packageRootRef.node = info->rootDirectoryID;
292 return B_OK;
296 status_t
297 Volume::InitPackages(Listener* listener)
299 // node-monitor the volume's packages directory
300 status_t error = watch_node(&PackagesDirectoryRef(), B_WATCH_DIRECTORY,
301 BMessenger(this));
302 if (error == B_OK) {
303 fListener = listener;
304 } else {
305 ERROR("Volume::InitPackages(): failed to start watching the packages "
306 "directory of the volume at \"%s\": %s\n",
307 fPath.String(), strerror(error));
308 // Not good, but not fatal. Only the manual package operations in the
309 // packages directory won't work correctly.
312 // read the packages directory and get the active packages
313 int fd = OpenRootDirectory();
314 if (fd < 0) {
315 ERROR("Volume::InitPackages(): failed to open root directory: %s\n",
316 strerror(fd));
317 RETURN_ERROR(fd);
319 FileDescriptorCloser fdCloser(fd);
321 error = _ReadPackagesDirectory();
322 if (error != B_OK)
323 RETURN_ERROR(error);
325 error = _InitLatestState();
326 if (error != B_OK)
327 RETURN_ERROR(error);
329 error = _GetActivePackages(fd);
330 if (error != B_OK)
331 RETURN_ERROR(error);
333 // create the admin directory, if it doesn't exist yet
334 BDirectory packagesDirectory;
335 if (packagesDirectory.SetTo(&PackagesDirectoryRef()) == B_OK) {
336 if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists())
337 packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL);
340 return B_OK;
344 status_t
345 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly)
347 for (PackageFileNameHashTable::Iterator it
348 = fLatestState->ByFileNameIterator(); it.HasNext();) {
349 Package* package = it.Next();
350 if (activeOnly && !package->IsActive())
351 continue;
353 status_t error = repository.AddPackage(package->Info());
354 if (error != B_OK) {
355 ERROR("Volume::AddPackagesToRepository(): failed to add package %s "
356 "to repository: %s\n", package->FileName().String(),
357 strerror(error));
358 return error;
362 return B_OK;
366 void
367 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume)
369 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume);
370 // create the solver
371 BSolver* solver;
372 status_t error = BSolver::Create(solver);
373 if (error != B_OK) {
374 ERROR("Volume::InitialVerify(): failed to create solver: %s\n",
375 strerror(error));
376 return;
378 ObjectDeleter<BSolver> solverDeleter(solver);
380 // add a repository with all active packages
381 BSolverRepository repository;
382 error = _AddRepository(solver, repository, true, true);
383 if (error != B_OK) {
384 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
385 strerror(error));
386 return;
389 // add a repository for the next volume
390 BSolverRepository nextRepository;
391 if (nextVolume != NULL) {
392 nextRepository.SetPriority(1);
393 error = nextVolume->_AddRepository(solver, nextRepository, true, false);
394 if (error != B_OK) {
395 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
396 strerror(error));
397 return;
401 // add a repository for the next next volume
402 BSolverRepository nextNextRepository;
403 if (nextNextVolume != NULL) {
404 nextNextRepository.SetPriority(2);
405 error = nextNextVolume->_AddRepository(solver, nextNextRepository, true,
406 false);
407 if (error != B_OK) {
408 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
409 strerror(error));
410 return;
414 // verify
415 error = solver->VerifyInstallation();
416 if (error != B_OK) {
417 ERROR("Volume::InitialVerify(): failed to verify: %s\n",
418 strerror(error));
419 return;
422 if (!solver->HasProblems()) {
423 INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n",
424 Path().String());
425 return;
428 // print the problems
429 // TODO: Notify the user ...
430 INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n",
431 Path().String());
433 int32 problemCount = solver->CountProblems();
434 for (int32 i = 0; i < problemCount; i++) {
435 BSolverProblem* problem = solver->ProblemAt(i);
436 INFORM(" %" B_PRId32 ": %s\n", i + 1, problem->ToString().String());
437 int32 solutionCount = problem->CountSolutions();
438 for (int32 k = 0; k < solutionCount; k++) {
439 const BSolverProblemSolution* solution = problem->SolutionAt(k);
440 INFORM(" solution %" B_PRId32 ":\n", k + 1);
441 int32 elementCount = solution->CountElements();
442 for (int32 l = 0; l < elementCount; l++) {
443 const BSolverProblemSolutionElement* element
444 = solution->ElementAt(l);
445 INFORM(" - %s\n", element->ToString().String());
452 void
453 Volume::HandleGetLocationInfoRequest(BMessage* message)
455 AutoLocker<BLocker> locker(fLock);
457 // If the cached reply message is up-to-date, just send it.
458 int64 changeCount;
459 if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK
460 && changeCount == fChangeCount) {
461 locker.Unlock();
462 message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
463 kCommunicationTimeout);
464 return;
467 // rebuild the reply message
468 fLocationInfoReply.MakeEmpty();
470 if (fLocationInfoReply.AddInt32("base directory device",
471 fRootDirectoryRef.device) != B_OK
472 || fLocationInfoReply.AddInt64("base directory node",
473 fRootDirectoryRef.node) != B_OK
474 || fLocationInfoReply.AddInt32("packages directory device",
475 PackagesDeviceID()) != B_OK
476 || fLocationInfoReply.AddInt64("packages directory node",
477 PackagesDirectoryID()) != B_OK) {
478 return;
481 for (PackageFileNameHashTable::Iterator it
482 = fLatestState->ByFileNameIterator(); it.HasNext();) {
483 Package* package = it.Next();
484 const char* fieldName = package->IsActive()
485 ? "latest active packages" : "latest inactive packages";
486 BMessage packageArchive;
487 if (package->Info().Archive(&packageArchive) != B_OK
488 || fLocationInfoReply.AddMessage(fieldName, &packageArchive)
489 != B_OK) {
490 return;
494 if (fActiveState != fLatestState) {
495 if (fPackagesDirectoryCount > 1) {
496 fLocationInfoReply.AddString("old state",
497 fPackagesDirectories[fPackagesDirectoryCount - 1].Name());
500 for (PackageFileNameHashTable::Iterator it
501 = fActiveState->ByFileNameIterator(); it.HasNext();) {
502 Package* package = it.Next();
503 if (!package->IsActive())
504 continue;
506 BMessage packageArchive;
507 if (package->Info().Archive(&packageArchive) != B_OK
508 || fLocationInfoReply.AddMessage("currently active packages",
509 &packageArchive) != B_OK) {
510 return;
515 if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK)
516 return;
518 locker.Unlock();
520 message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
521 kCommunicationTimeout);
525 void
526 Volume::HandleCommitTransactionRequest(BMessage* message)
528 BCommitTransactionResult result;
529 PackageSet dummy;
530 _CommitTransaction(message, NULL, dummy, dummy, result);
532 BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY);
533 status_t error = result.AddToMessage(reply);
534 if (error != B_OK) {
535 ERROR("Volume::HandleCommitTransactionRequest(): Failed to add "
536 "transaction result to reply: %s\n", strerror(error));
537 return;
540 message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout);
544 void
545 Volume::PackageJobPending()
547 atomic_add(&fPendingPackageJobCount, 1);
551 void
552 Volume::PackageJobFinished()
554 atomic_add(&fPendingPackageJobCount, -1);
558 bool
559 Volume::IsPackageJobPending() const
561 return fPendingPackageJobCount != 0;
565 void
566 Volume::Unmounted()
568 if (fListener != NULL) {
569 stop_watching(BMessenger(this));
570 fListener = NULL;
573 if (BLooper* looper = Looper())
574 looper->RemoveHandler(this);
578 void
579 Volume::MessageReceived(BMessage* message)
581 switch (message->what) {
582 case B_NODE_MONITOR:
584 int32 opcode;
585 if (message->FindInt32("opcode", &opcode) != B_OK)
586 break;
588 switch (opcode) {
589 case B_ENTRY_CREATED:
590 _HandleEntryCreatedOrRemoved(message, true);
591 break;
592 case B_ENTRY_REMOVED:
593 _HandleEntryCreatedOrRemoved(message, false);
594 break;
595 case B_ENTRY_MOVED:
596 _HandleEntryMoved(message);
597 break;
598 default:
599 break;
601 break;
604 case kHandleNodeMonitorEvents:
605 if (fListener != NULL) {
606 if (system_time() >= fNodeMonitorEventHandleTime)
607 fListener->VolumeNodeMonitorEventOccurred(this);
609 break;
611 default:
612 BHandler::MessageReceived(message);
613 break;
618 BPackageInstallationLocation
619 Volume::Location() const
621 switch (fMountType) {
622 case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
623 return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
624 case PACKAGE_FS_MOUNT_TYPE_HOME:
625 return B_PACKAGE_INSTALLATION_LOCATION_HOME;
626 case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
627 default:
628 return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT;
633 const node_ref&
634 Volume::PackagesDirectoryRef() const
636 return fPackagesDirectories[0].NodeRef();
640 PackageFileNameHashTable::Iterator
641 Volume::PackagesByFileNameIterator() const
643 return fLatestState->ByFileNameIterator();
648 Volume::OpenRootDirectory() const
650 BDirectory directory;
651 status_t error = directory.SetTo(&fRootDirectoryRef);
652 if (error != B_OK) {
653 ERROR("Volume::OpenRootDirectory(): failed to open root directory: "
654 "%s\n", strerror(error));
655 RETURN_ERROR(error);
658 return directory.Dup();
662 void
663 Volume::ProcessPendingNodeMonitorEvents()
665 // get the events
666 NodeMonitorEventList events;
668 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
669 events.MoveFrom(&fPendingNodeMonitorEvents);
672 // process them
673 while (NodeMonitorEvent* event = events.RemoveHead()) {
674 ObjectDeleter<NodeMonitorEvent> eventDeleter(event);
675 if (event->WasCreated())
676 _PackagesEntryCreated(event->EntryName());
677 else
678 _PackagesEntryRemoved(event->EntryName());
683 bool
684 Volume::HasPendingPackageActivationChanges() const
686 return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty();
690 void
691 Volume::ProcessPendingPackageActivationChanges()
693 if (!HasPendingPackageActivationChanges())
694 return;
696 // perform the request
697 BCommitTransactionResult result;
698 _CommitTransaction(NULL, NULL, fPackagesToBeActivated,
699 fPackagesToBeDeactivated, result);
701 if (result.Error() != B_TRANSACTION_OK) {
702 ERROR("Volume::ProcessPendingPackageActivationChanges(): package "
703 "activation failed: %s\n", result.FullErrorMessage().String());
704 // TODO: Notify the user!
707 // clear the activation/deactivation sets in any event
708 fPackagesToBeActivated.clear();
709 fPackagesToBeDeactivated.clear();
713 void
714 Volume::ClearPackageActivationChanges()
716 fPackagesToBeActivated.clear();
717 fPackagesToBeDeactivated.clear();
721 status_t
722 Volume::CreateTransaction(BPackageInstallationLocation location,
723 BActivationTransaction& _transaction, BDirectory& _transactionDirectory)
725 // open admin directory
726 BDirectory adminDirectory;
727 status_t error = _OpenPackagesSubDirectory(
728 RelativePath(kAdminDirectoryName), true, adminDirectory);
729 if (error != B_OK)
730 return error;
732 // create a transaction directory
733 int uniqueId = 1;
734 BString directoryName;
735 for (;; uniqueId++) {
736 directoryName.SetToFormat("transaction-%d", uniqueId);
737 if (directoryName.IsEmpty())
738 return B_NO_MEMORY;
740 error = adminDirectory.CreateDirectory(directoryName,
741 &_transactionDirectory);
742 if (error == B_OK)
743 break;
744 if (error != B_FILE_EXISTS)
745 return error;
748 // init the transaction
749 error = _transaction.SetTo(location, fChangeCount, directoryName);
750 if (error != B_OK) {
751 BEntry entry;
752 _transactionDirectory.GetEntry(&entry);
753 _transactionDirectory.Unset();
754 if (entry.InitCheck() == B_OK)
755 entry.Remove();
756 return error;
759 return B_OK;
763 void
764 Volume::CommitTransaction(const BActivationTransaction& transaction,
765 const PackageSet& packagesAlreadyAdded,
766 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result)
768 _CommitTransaction(NULL, &transaction, packagesAlreadyAdded,
769 packagesAlreadyRemoved, _result);
773 void
774 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created)
776 // only moves to or from our packages directory are interesting
777 int32 deviceID;
778 int64 directoryID;
779 const char* name;
780 if (message->FindInt32("device", &deviceID) != B_OK
781 || message->FindInt64("directory", &directoryID) != B_OK
782 || message->FindString("name", &name) != B_OK
783 || node_ref(deviceID, directoryID) != PackagesDirectoryRef()) {
784 return;
787 _QueueNodeMonitorEvent(name, created);
791 void
792 Volume::_HandleEntryMoved(const BMessage* message)
794 int32 deviceID;
795 int64 fromDirectoryID;
796 int64 toDirectoryID;
797 const char* fromName;
798 const char* toName;
799 if (message->FindInt32("device", &deviceID) != B_OK
800 || message->FindInt64("from directory", &fromDirectoryID) != B_OK
801 || message->FindInt64("to directory", &toDirectoryID) != B_OK
802 || message->FindString("from name", &fromName) != B_OK
803 || message->FindString("name", &toName) != B_OK
804 || deviceID != PackagesDeviceID()
805 || (fromDirectoryID != PackagesDirectoryID()
806 && toDirectoryID != PackagesDirectoryID())) {
807 return;
810 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
811 // make sure for a move the two events cannot get split
813 if (fromDirectoryID == PackagesDirectoryID())
814 _QueueNodeMonitorEvent(fromName, false);
815 if (toDirectoryID == PackagesDirectoryID())
816 _QueueNodeMonitorEvent(toName, true);
820 void
821 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated)
823 if (name.IsEmpty()) {
824 ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n");
825 return;
828 // ignore entries that don't have the ".hpkg" extension
829 if (!name.EndsWith(kPackageFileNameExtension))
830 return;
832 NodeMonitorEvent* event
833 = new(std::nothrow) NodeMonitorEvent(name, wasCreated);
834 if (event == NULL) {
835 ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n");
836 return;
839 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
840 fPendingNodeMonitorEvents.Add(event);
841 eventsLock.Unlock();
843 fNodeMonitorEventHandleTime
844 = system_time() + kNodeMonitorEventHandlingDelay;
845 BMessage message(kHandleNodeMonitorEvents);
846 BMessageRunner::StartSending(this, &message, kNodeMonitorEventHandlingDelay,
851 void
852 Volume::_PackagesEntryCreated(const char* name)
854 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name);
855 // Ignore the event, if the package is already known.
856 Package* package = fLatestState->FindPackage(name);
857 if (package != NULL) {
858 if (package->File()->EntryCreatedIgnoreLevel() > 0) {
859 package->File()->DecrementEntryCreatedIgnoreLevel();
860 } else {
861 WARN("node monitoring created event for already known entry "
862 "\"%s\"\n", name);
865 // Remove the package from the packages-to-be-deactivated set, if it is in
866 // there (unlikely, unless we see a remove-create sequence).
867 PackageSet::iterator it = fPackagesToBeDeactivated.find(package);
868 if (it != fPackagesToBeDeactivated.end())
869 fPackagesToBeDeactivated.erase(it);
871 return;
874 status_t error = fPackageFileManager->CreatePackage(
875 NotOwningEntryRef(PackagesDirectoryRef(), name),
876 package);
877 if (error != B_OK) {
878 ERROR("failed to init package for file \"%s\"\n", name);
879 return;
882 fLock.Lock();
883 fLatestState->AddPackage(package);
884 fChangeCount++;
885 fLock.Unlock();
887 try {
888 fPackagesToBeActivated.insert(package);
889 } catch (std::bad_alloc& exception) {
890 ERROR("out of memory\n");
891 return;
896 void
897 Volume::_PackagesEntryRemoved(const char* name)
899 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name);
900 Package* package = fLatestState->FindPackage(name);
901 if (package == NULL)
902 return;
904 // Ignore the event, if we generated it ourselves.
905 if (package->File()->EntryRemovedIgnoreLevel() > 0) {
906 package->File()->DecrementEntryRemovedIgnoreLevel();
907 return;
910 // Remove the package from the packages-to-be-activated set, if it is in
911 // there (unlikely, unless we see a create-remove-create sequence).
912 PackageSet::iterator it = fPackagesToBeActivated.find(package);
913 if (it != fPackagesToBeActivated.end())
914 fPackagesToBeActivated.erase(it);
916 // If the package isn't active, just remove it for good.
917 if (!package->IsActive()) {
918 AutoLocker<BLocker> locker(fLock);
919 fLatestState->RemovePackage(package);
920 fChangeCount++;
921 delete package;
922 return;
925 // The package must be deactivated.
926 try {
927 fPackagesToBeDeactivated.insert(package);
928 } catch (std::bad_alloc& exception) {
929 ERROR("out of memory\n");
930 return;
935 status_t
936 Volume::_ReadPackagesDirectory()
938 BDirectory directory;
939 status_t error = directory.SetTo(&PackagesDirectoryRef());
940 if (error != B_OK) {
941 ERROR("Volume::_ReadPackagesDirectory(): failed to open packages "
942 "directory: %s\n", strerror(error));
943 RETURN_ERROR(error);
946 entry_ref entry;
947 while (directory.GetNextRef(&entry) == B_OK) {
948 if (!BString(entry.name).EndsWith(kPackageFileNameExtension))
949 continue;
951 Package* package;
952 status_t error = fPackageFileManager->CreatePackage(entry, package);
953 if (error == B_OK) {
954 AutoLocker<BLocker> locker(fLock);
955 fLatestState->AddPackage(package);
956 fChangeCount++;
960 return B_OK;
964 status_t
965 Volume::_InitLatestState()
967 if (_InitLatestStateFromActivatedPackages() == B_OK)
968 return B_OK;
970 INFORM("Failed to get activated packages info from activated packages file."
971 " Assuming all package files in package directory are activated.\n");
973 AutoLocker<BLocker> locker(fLock);
975 for (PackageFileNameHashTable::Iterator it
976 = fLatestState->ByFileNameIterator();
977 Package* package = it.Next();) {
978 fLatestState->SetPackageActive(package, true);
979 fChangeCount++;
982 return B_OK;
986 status_t
987 Volume::_InitLatestStateFromActivatedPackages()
989 // try reading the activation file
990 NotOwningEntryRef entryRef(PackagesDirectoryRef(), kActivationFileName);
991 BFile file;
992 status_t error = file.SetTo(&entryRef, B_READ_ONLY);
993 if (error != B_OK) {
994 INFORM("Failed to open packages activation file: %s\n",
995 strerror(error));
996 RETURN_ERROR(error);
999 // read the whole file into memory to simplify things
1000 off_t size;
1001 error = file.GetSize(&size);
1002 if (error != B_OK) {
1003 ERROR("Failed to packages activation file size: %s\n",
1004 strerror(error));
1005 RETURN_ERROR(error);
1008 if (size > (off_t)kMaxActivationFileSize) {
1009 ERROR("The packages activation file is too big.\n");
1010 RETURN_ERROR(B_BAD_DATA);
1013 char* fileContent = (char*)malloc(size + 1);
1014 if (fileContent == NULL)
1015 RETURN_ERROR(B_NO_MEMORY);
1016 MemoryDeleter fileContentDeleter(fileContent);
1018 ssize_t bytesRead = file.Read(fileContent, size);
1019 if (bytesRead < 0) {
1020 ERROR("Failed to read packages activation file: %s\n",
1021 strerror(bytesRead));
1022 RETURN_ERROR(errno);
1025 if (bytesRead != size) {
1026 ERROR("Failed to read whole packages activation file.\n");
1027 RETURN_ERROR(B_ERROR);
1030 // null-terminate to simplify parsing
1031 fileContent[size] = '\0';
1033 AutoLocker<BLocker> locker(fLock);
1035 // parse the file and mark the respective packages active
1036 const char* packageName = fileContent;
1037 char* const fileContentEnd = fileContent + size;
1038 while (packageName < fileContentEnd) {
1039 char* packageNameEnd = strchr(packageName, '\n');
1040 if (packageNameEnd == NULL)
1041 packageNameEnd = fileContentEnd;
1043 // skip empty lines
1044 if (packageName == packageNameEnd) {
1045 packageName++;
1046 continue;
1048 *packageNameEnd = '\0';
1050 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
1051 ERROR("Invalid packages activation file content.\n");
1052 RETURN_ERROR(B_BAD_DATA);
1055 Package* package = fLatestState->FindPackage(packageName);
1056 if (package != NULL) {
1057 fLatestState->SetPackageActive(package, true);
1058 fChangeCount++;
1059 } else {
1060 WARN("Package \"%s\" from activation file not in packages "
1061 "directory.\n", packageName);
1064 packageName = packageNameEnd + 1;
1067 return B_OK;
1071 status_t
1072 Volume::_GetActivePackages(int fd)
1074 // get the info from packagefs
1075 PackageFSGetPackageInfosRequest* request = NULL;
1076 MemoryDeleter requestDeleter;
1077 size_t bufferSize = 64 * 1024;
1078 for (;;) {
1079 request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize);
1080 if (request == NULL)
1081 RETURN_ERROR(B_NO_MEMORY);
1082 requestDeleter.SetTo(request);
1084 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request,
1085 bufferSize) != 0) {
1086 ERROR("Volume::_GetActivePackages(): failed to get active package "
1087 "info from package FS: %s\n", strerror(errno));
1088 RETURN_ERROR(errno);
1091 if (request->bufferSize <= bufferSize)
1092 break;
1094 bufferSize = request->bufferSize;
1095 requestDeleter.Unset();
1098 INFORM("latest volume state:\n");
1099 _DumpState(fLatestState);
1101 // check whether that matches the expected state
1102 if (_CheckActivePackagesMatchLatestState(request)) {
1103 INFORM("The latest volume state is also the currently active one\n");
1104 fActiveState = fLatestState;
1105 return B_OK;
1108 // There's a mismatch. We need a new state that reflects the actual
1109 // activation situation.
1110 VolumeState* state = new(std::nothrow) VolumeState;
1111 if (state == NULL)
1112 RETURN_ERROR(B_NO_MEMORY);
1113 ObjectDeleter<VolumeState> stateDeleter(state);
1115 for (uint32 i = 0; i < request->packageCount; i++) {
1116 const PackageFSPackageInfo& info = request->infos[i];
1117 NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID,
1118 info.name);
1119 Package* package;
1120 status_t error = fPackageFileManager->CreatePackage(entryRef, package);
1121 if (error != B_OK) {
1122 WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %"
1123 B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID,
1124 info.directoryNodeID, info.name, strerror(error));
1125 continue;
1128 state->AddPackage(package);
1129 state->SetPackageActive(package, true);
1132 INFORM("currently active volume state:\n");
1133 _DumpState(state);
1135 fActiveState = stateDeleter.Detach();
1136 return B_OK;
1140 void
1141 Volume::_RunQueuedScripts()
1143 BDirectory adminDirectory;
1144 status_t error = _OpenPackagesSubDirectory(
1145 RelativePath(kAdminDirectoryName), false, adminDirectory);
1146 if (error != B_OK)
1147 return;
1149 BDirectory scriptsDirectory;
1150 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName);
1151 if (error != B_OK)
1152 return;
1154 // enumerate all the symlinks in the queued scripts directory
1155 BEntry scriptEntry;
1156 while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) {
1157 BPath scriptPath;
1158 scriptEntry.GetPath(&scriptPath);
1159 error = scriptPath.InitCheck();
1160 if (error != B_OK) {
1161 INFORM("failed to get path of post-installation script \"%s\"\n",
1162 strerror(error));
1163 continue;
1166 errno = 0;
1167 int result = system(scriptPath.Path());
1168 if (result != 0) {
1169 INFORM("running post-installation script \"%s\" "
1170 "failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno));
1173 // remove the symlink, now that we've run the post-installation script
1174 error = scriptEntry.Remove();
1175 if (error != B_OK) {
1176 INFORM("removing queued post-install script failed \"%s\"\n",
1177 strerror(error));
1183 bool
1184 Volume::_CheckActivePackagesMatchLatestState(
1185 PackageFSGetPackageInfosRequest* request)
1187 if (fPackagesDirectoryCount != 1) {
1188 INFORM("An old packages state (\"%s\") seems to be active.\n",
1189 fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String());
1190 return false;
1193 const node_ref packagesDirRef(PackagesDirectoryRef());
1195 // mark the returned packages active
1196 for (uint32 i = 0; i < request->packageCount; i++) {
1197 const PackageFSPackageInfo& info = request->infos[i];
1198 if (node_ref(info.directoryDeviceID, info.directoryNodeID)
1199 != packagesDirRef) {
1200 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1201 ") not in packages directory\n", info.name,
1202 info.packageDeviceID, info.packageNodeID);
1203 return false;
1206 Package* package = fLatestState->FindPackage(
1207 node_ref(info.packageDeviceID, info.packageNodeID));
1208 if (package == NULL || !package->IsActive()) {
1209 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1210 ") not %s\n", info.name,
1211 info.packageDeviceID, info.packageNodeID,
1212 package == NULL
1213 ? "found in packages directory" : "supposed to be active");
1214 return false;
1218 // Check whether there are packages that aren't active but should be.
1219 uint32 count = 0;
1220 for (PackageNodeRefHashTable::Iterator it
1221 = fLatestState->ByNodeRefIterator(); it.HasNext();) {
1222 Package* package = it.Next();
1223 if (package->IsActive())
1224 count++;
1227 if (count != request->packageCount) {
1228 INFORM("There seem to be packages in the packages directory that "
1229 "should be active.\n");
1230 return false;
1233 return true;
1237 void
1238 Volume::_SetLatestState(VolumeState* state, bool isActive)
1240 AutoLocker<BLocker> locker(fLock);
1241 if (isActive) {
1242 if (fLatestState != fActiveState)
1243 delete fActiveState;
1244 fActiveState = state;
1247 if (fLatestState != fActiveState)
1248 delete fLatestState;
1249 fLatestState = state;
1250 fChangeCount++;
1252 locker.Unlock();
1254 // Send a notification, if this is a system root volume.
1255 if (fRoot->IsSystemRoot()) {
1256 BMessage message(B_PACKAGE_UPDATE);
1257 if (message.AddInt32("event",
1258 (int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK
1259 && message.AddInt32("location", (int32)Location()) == B_OK
1260 && message.AddInt64("change count", fChangeCount) == B_OK) {
1261 BRoster::Private().SendTo(&message, NULL, false);
1267 void
1268 Volume::_DumpState(VolumeState* state)
1270 uint32 inactiveCount = 0;
1271 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1272 it.HasNext();) {
1273 Package* package = it.Next();
1274 if (package->IsActive()) {
1275 INFORM("active package: \"%s\"\n", package->FileName().String());
1276 } else
1277 inactiveCount++;
1280 if (inactiveCount == 0)
1281 return;
1283 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1284 it.HasNext();) {
1285 Package* package = it.Next();
1286 if (!package->IsActive())
1287 INFORM("inactive package: \"%s\"\n", package->FileName().String());
1292 status_t
1293 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository,
1294 bool activeOnly, bool installed)
1296 status_t error = repository.SetTo(Path());
1297 if (error != B_OK) {
1298 ERROR("Volume::_AddRepository(): failed to init repository: %s\n",
1299 strerror(error));
1300 return error;
1303 repository.SetInstalled(installed);
1305 error = AddPackagesToRepository(repository, true);
1306 if (error != B_OK) {
1307 ERROR("Volume::_AddRepository(): failed to add packages to "
1308 "repository: %s\n", strerror(error));
1309 return error;
1312 error = solver->AddRepository(&repository);
1313 if (error != B_OK) {
1314 ERROR("Volume::_AddRepository(): failed to add repository to solver: "
1315 "%s\n", strerror(error));
1316 return error;
1319 return B_OK;
1323 status_t
1324 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
1325 BDirectory& _directory)
1327 // open the packages directory
1328 BDirectory directory;
1329 status_t error = directory.SetTo(&PackagesDirectoryRef());
1330 if (error != B_OK) {
1331 ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages "
1332 "directory: %s\n", strerror(error));
1333 RETURN_ERROR(error);
1336 return FSUtils::OpenSubDirectory(directory, path, create, _directory);
1340 void
1341 Volume::_CommitTransaction(BMessage* message,
1342 const BActivationTransaction* transaction,
1343 const PackageSet& packagesAlreadyAdded,
1344 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result)
1346 _result.Unset();
1348 // perform the request
1349 CommitTransactionHandler handler(this, fPackageFileManager, _result);
1350 BTransactionError error = B_TRANSACTION_INTERNAL_ERROR;
1351 try {
1352 handler.Init(fLatestState, fLatestState == fActiveState,
1353 packagesAlreadyAdded, packagesAlreadyRemoved);
1355 if (message != NULL)
1356 handler.HandleRequest(message);
1357 else if (transaction != NULL)
1358 handler.HandleRequest(*transaction);
1359 else
1360 handler.HandleRequest();
1362 _SetLatestState(handler.DetachVolumeState(),
1363 handler.IsActiveVolumeState());
1364 error = B_TRANSACTION_OK;
1365 } catch (Exception& exception) {
1366 error = exception.Error();
1367 exception.SetOnResult(_result);
1368 if (_result.ErrorPackage().IsEmpty()
1369 && handler.CurrentPackage() != NULL) {
1370 _result.SetErrorPackage(handler.CurrentPackage()->FileName());
1372 } catch (std::bad_alloc& exception) {
1373 error = B_TRANSACTION_NO_MEMORY;
1376 _result.SetError(B_TRANSACTION_OK);
1378 // revert on error
1379 if (error != B_TRANSACTION_OK)
1380 handler.Revert();