1 /*
2 * Copyright 2013-2021, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
7 * Andrew Lindesay <apl@lindesay.co.nz>
8 */
9
10
11 #include "Volume.h"
12
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <sys/stat.h>
16 #include <time.h>
17 #include <unistd.h>
18
19 #include <Directory.h>
20 #include <Entry.h>
21 #include <File.h>
22 #include <Looper.h>
23 #include <MessageRunner.h>
24 #include <NodeMonitor.h>
25 #include <Path.h>
26 #include <Roster.h>
27 #include <SymLink.h>
28
29 #include <package/CommitTransactionResult.h>
30 #include <package/PackageRoster.h>
31 #include <package/solver/Solver.h>
32 #include <package/solver/SolverPackage.h>
33 #include <package/solver/SolverProblem.h>
34 #include <package/solver/SolverProblemSolution.h>
35 #include <package/solver/SolverRepository.h>
36 #include <package/solver/SolverResult.h>
37
38 #include <AutoDeleter.h>
39 #include <AutoLocker.h>
40 #include <NotOwningEntryRef.h>
41 #include <package/DaemonDefs.h>
42 #include <RosterPrivate.h>
43
44 #include "CommitTransactionHandler.h"
45 #include "Constants.h"
46 #include "DebugSupport.h"
47 #include "Exception.h"
48 #include "PackageFileManager.h"
49 #include "Root.h"
50 #include "VolumeState.h"
51
52
53 using namespace BPackageKit::BPrivate;
54
55
56 // #pragma mark - Listener
57
58
~Listener()59 Volume::Listener::~Listener()
60 {
61 }
62
63
64 // #pragma mark - NodeMonitorEvent
65
66
67 struct Volume::NodeMonitorEvent
68 : public DoublyLinkedListLinkImpl<NodeMonitorEvent> {
69 public:
NodeMonitorEventVolume::NodeMonitorEvent70 NodeMonitorEvent(const BString& entryName, bool created)
71 :
72 fEntryName(entryName),
73 fCreated(created)
74 {
75 }
76
EntryNameVolume::NodeMonitorEvent77 const BString& EntryName() const
78 {
79 return fEntryName;
80 }
81
WasCreatedVolume::NodeMonitorEvent82 bool WasCreated() const
83 {
84 return fCreated;
85 }
86
87 private:
88 BString fEntryName;
89 bool fCreated;
90 };
91
92
93 // #pragma mark - PackagesDirectory
94
95
96 struct Volume::PackagesDirectory {
97 public:
PackagesDirectoryVolume::PackagesDirectory98 PackagesDirectory()
99 :
100 fNodeRef(),
101 fName()
102 {
103 }
104
InitVolume::PackagesDirectory105 void Init(const node_ref& nodeRef, bool isPackagesDir)
106 {
107 fNodeRef = nodeRef;
108
109 if (isPackagesDir)
110 return;
111
112 BDirectory directory;
113 BEntry entry;
114 if (directory.SetTo(&fNodeRef) == B_OK
115 && directory.GetEntry(&entry) == B_OK) {
116 fName = entry.Name();
117 }
118
119 if (fName.IsEmpty())
120 fName = "unknown state";
121 }
122
NodeRefVolume::PackagesDirectory123 const node_ref& NodeRef() const
124 {
125 return fNodeRef;
126 }
127
NameVolume::PackagesDirectory128 const BString& Name() const
129 {
130 return fName;
131 }
132
133 private:
134 node_ref fNodeRef;
135 BString fName;
136 };
137
138
139 // #pragma mark - Volume
140
141
Volume(BLooper * looper)142 Volume::Volume(BLooper* looper)
143 :
144 BHandler(),
145 fPath(),
146 fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM),
147 fRootDirectoryRef(),
148 fPackagesDirectories(NULL),
149 fPackagesDirectoryCount(0),
150 fRoot(NULL),
151 fListener(NULL),
152 fPackageFileManager(NULL),
153 fLatestState(NULL),
154 fActiveState(NULL),
155 fChangeCount(0),
156 fLock("volume"),
157 fPendingNodeMonitorEventsLock("pending node monitor events"),
158 fPendingNodeMonitorEvents(),
159 fNodeMonitorEventHandleTime(0),
160 fPackagesToBeActivated(),
161 fPackagesToBeDeactivated(),
162 fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY),
163 fPendingPackageJobCount(0)
164 {
165 looper->AddHandler(this);
166 }
167
168
~Volume()169 Volume::~Volume()
170 {
171 Unmounted();
172 // needed for error case in InitPackages()
173
174 _SetLatestState(NULL, true);
175
176 delete[] fPackagesDirectories;
177 delete fPackageFileManager;
178 }
179
180
181 status_t
Init(const node_ref & rootDirectoryRef,node_ref & _packageRootRef)182 Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef)
183 {
184 status_t error = fLock.InitCheck();
185 if (error != B_OK)
186 return error;
187
188 error = fPendingNodeMonitorEventsLock.InitCheck();
189 if (error != B_OK)
190 return error;
191
192 fLatestState = new(std::nothrow) VolumeState;
193 if (fLatestState == NULL || !fLatestState->Init())
194 RETURN_ERROR(B_NO_MEMORY);
195
196 fPackageFileManager = new(std::nothrow) PackageFileManager(fLock);
197 if (fPackageFileManager == NULL)
198 RETURN_ERROR(B_NO_MEMORY);
199
200 error = fPackageFileManager->Init();
201 if (error != B_OK)
202 RETURN_ERROR(error);
203
204 fRootDirectoryRef = rootDirectoryRef;
205
206 // open the root directory
207 BDirectory directory;
208 error = directory.SetTo(&fRootDirectoryRef);
209 if (error != B_OK) {
210 ERROR("Volume::Init(): failed to open root directory: %s\n",
211 strerror(error));
212 RETURN_ERROR(error);
213 }
214
215 // get the directory path
216 BEntry entry;
217 error = directory.GetEntry(&entry);
218
219 BPath path;
220 if (error == B_OK)
221 error = entry.GetPath(&path);
222
223 if (error != B_OK) {
224 ERROR("Volume::Init(): failed to get root directory path: %s\n",
225 strerror(error));
226 RETURN_ERROR(error);
227 }
228
229 fPath = path.Path();
230 if (fPath.IsEmpty())
231 RETURN_ERROR(B_NO_MEMORY);
232
233 // get a volume info from the FS
234 FileDescriptorCloser fd(directory.Dup());
235 if (!fd.IsSet()) {
236 ERROR("Volume::Init(): failed to get root directory FD: %s\n",
237 strerror(fd.Get()));
238 RETURN_ERROR(fd.Get());
239 }
240
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);
253
254 if (ioctl(fd.Get(), 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);
259 }
260
261 if (info->packagesDirectoryCount <= maxPackagesDirCount)
262 break;
263
264 maxPackagesDirCount = info->packagesDirectoryCount;
265 infoDeleter.Unset();
266 }
267
268 if (info->packagesDirectoryCount < 1) {
269 ERROR("Volume::Init(): got invalid volume info from packagefs\n");
270 RETURN_ERROR(B_BAD_VALUE);
271 }
272
273 fMountType = info->mountType;
274
275 fPackagesDirectories = new(std::nothrow) PackagesDirectory[
276 info->packagesDirectoryCount];
277 if (fPackagesDirectories == NULL)
278 RETURN_ERROR(B_NO_MEMORY);
279
280 fPackagesDirectoryCount = info->packagesDirectoryCount;
281
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);
287 }
288
289 _packageRootRef.device = info->rootDeviceID;
290 _packageRootRef.node = info->rootDirectoryID;
291
292 return B_OK;
293 }
294
295
296 status_t
InitPackages(Listener * listener)297 Volume::InitPackages(Listener* listener)
298 {
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.
310 }
311
312 // read the packages directory and get the active packages
313 FileDescriptorCloser fd(OpenRootDirectory());
314 if (!fd.IsSet()) {
315 ERROR("Volume::InitPackages(): failed to open root directory: %s\n",
316 strerror(fd.Get()));
317 RETURN_ERROR(fd.Get());
318 }
319
320 error = _ReadPackagesDirectory();
321 if (error != B_OK)
322 RETURN_ERROR(error);
323
324 error = _InitLatestState();
325 if (error != B_OK)
326 RETURN_ERROR(error);
327
328 error = _GetActivePackages(fd.Get());
329 if (error != B_OK)
330 RETURN_ERROR(error);
331
332 // create the admin directory, if it doesn't exist yet
333 BDirectory packagesDirectory;
334 bool createdAdminDirectory = false;
335 if (packagesDirectory.SetTo(&PackagesDirectoryRef()) == B_OK) {
336 if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists()) {
337 packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL);
338 createdAdminDirectory = true;
339 }
340 }
341 BDirectory adminDirectory(&packagesDirectory, kAdminDirectoryName);
342 error = adminDirectory.InitCheck();
343 if (error != B_OK)
344 RETURN_ERROR(error);
345
346 // First boot processing requested by a magic file left by the OS installer?
347 BEntry flagFileEntry(&adminDirectory, kFirstBootProcessingNeededFileName);
348 if (createdAdminDirectory || flagFileEntry.Exists()) {
349 INFORM("Volume::InitPackages Requesting delayed first boot processing "
350 "for packages dir %s.\n", BPath(&packagesDirectory).Path());
351 if (flagFileEntry.Exists())
352 flagFileEntry.Remove(); // Remove early on to avoid an error loop.
353
354 // Are there any packages needing processing? Don't want to create an
355 // empty transaction directory and then never have it cleaned up when
356 // the empty transaction gets rejected.
357 bool anyPackages = false;
358 for (PackageNodeRefHashTable::Iterator it =
359 fActiveState->ByNodeRefIterator(); it.HasNext();) {
360 Package* package = it.Next();
361 if (package->IsActive()) {
362 anyPackages = true;
363 break;
364 }
365 }
366
367 if (anyPackages) {
368 // Create first boot processing special transaction for current
369 // volume, which also creates an empty transaction directory.
370 BPackageInstallationLocation location = Location();
371 BDirectory transactionDirectory;
372 BActivationTransaction transaction;
373 error = CreateTransaction(location, transaction,
374 transactionDirectory);
375 if (error != B_OK)
376 RETURN_ERROR(error);
377
378 // Add all package files in currently active state to transaction.
379 for (PackageNodeRefHashTable::Iterator it =
380 fActiveState->ByNodeRefIterator(); it.HasNext();) {
381 Package* package = it.Next();
382 if (package->IsActive()) {
383 if (!transaction.AddPackageToActivate(
384 package->FileName().String()))
385 RETURN_ERROR(B_NO_MEMORY);
386 }
387 }
388 transaction.SetFirstBootProcessing(true);
389
390 // Queue up the transaction as a BMessage for processing a bit
391 // later, once the package daemon has finished initialising.
392 BMessage commitMessage(B_MESSAGE_COMMIT_TRANSACTION);
393 error = transaction.Archive(&commitMessage);
394 if (error != B_OK)
395 RETURN_ERROR(error);
396 BLooper *myLooper = Looper() ;
397 if (myLooper == NULL)
398 RETURN_ERROR(B_NOT_INITIALIZED);
399 error = myLooper->PostMessage(&commitMessage);
400 if (error != B_OK)
401 RETURN_ERROR(error);
402 }
403 }
404
405 return B_OK;
406 }
407
408
409 status_t
AddPackagesToRepository(BSolverRepository & repository,bool activeOnly)410 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly)
411 {
412 for (PackageFileNameHashTable::Iterator it
413 = fLatestState->ByFileNameIterator(); it.HasNext();) {
414 Package* package = it.Next();
415 if (activeOnly && !package->IsActive())
416 continue;
417
418 status_t error = repository.AddPackage(package->Info());
419 if (error != B_OK) {
420 ERROR("Volume::AddPackagesToRepository(): failed to add package %s "
421 "to repository: %s\n", package->FileName().String(),
422 strerror(error));
423 return error;
424 }
425 }
426
427 return B_OK;
428 }
429
430
431 void
InitialVerify(Volume * nextVolume,Volume * nextNextVolume)432 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume)
433 {
434 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume);
435 // create the solver
436 BSolver* solver;
437 status_t error = BSolver::Create(solver);
438 if (error != B_OK) {
439 ERROR("Volume::InitialVerify(): failed to create solver: %s\n",
440 strerror(error));
441 return;
442 }
443 ObjectDeleter<BSolver> solverDeleter(solver);
444
445 // add a repository with all active packages
446 BSolverRepository repository;
447 error = _AddRepository(solver, repository, true, true);
448 if (error != B_OK) {
449 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
450 strerror(error));
451 return;
452 }
453
454 // add a repository for the next volume
455 BSolverRepository nextRepository;
456 if (nextVolume != NULL) {
457 nextRepository.SetPriority(1);
458 error = nextVolume->_AddRepository(solver, nextRepository, true, false);
459 if (error != B_OK) {
460 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
461 strerror(error));
462 return;
463 }
464 }
465
466 // add a repository for the next next volume
467 BSolverRepository nextNextRepository;
468 if (nextNextVolume != NULL) {
469 nextNextRepository.SetPriority(2);
470 error = nextNextVolume->_AddRepository(solver, nextNextRepository, true,
471 false);
472 if (error != B_OK) {
473 ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
474 strerror(error));
475 return;
476 }
477 }
478
479 // verify
480 error = solver->VerifyInstallation();
481 if (error != B_OK) {
482 ERROR("Volume::InitialVerify(): failed to verify: %s\n",
483 strerror(error));
484 return;
485 }
486
487 if (!solver->HasProblems()) {
488 INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n",
489 Path().String());
490 return;
491 }
492
493 // print the problems
494 // TODO: Notify the user ...
495 INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n",
496 Path().String());
497
498 int32 problemCount = solver->CountProblems();
499 for (int32 i = 0; i < problemCount; i++) {
500 BSolverProblem* problem = solver->ProblemAt(i);
501 INFORM(" %" B_PRId32 ": %s\n", i + 1, problem->ToString().String());
502 int32 solutionCount = problem->CountSolutions();
503 for (int32 k = 0; k < solutionCount; k++) {
504 const BSolverProblemSolution* solution = problem->SolutionAt(k);
505 INFORM(" solution %" B_PRId32 ":\n", k + 1);
506 int32 elementCount = solution->CountElements();
507 for (int32 l = 0; l < elementCount; l++) {
508 const BSolverProblemSolutionElement* element
509 = solution->ElementAt(l);
510 INFORM(" - %s\n", element->ToString().String());
511 }
512 }
513 }
514 }
515
516
517 void
HandleGetLocationInfoRequest(BMessage * message)518 Volume::HandleGetLocationInfoRequest(BMessage* message)
519 {
520 AutoLocker<BLocker> locker(fLock);
521
522 // If the cached reply message is up-to-date, just send it.
523 int64 changeCount;
524 if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK
525 && changeCount == fChangeCount) {
526 locker.Unlock();
527 message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
528 kCommunicationTimeout);
529 return;
530 }
531
532 // rebuild the reply message
533 fLocationInfoReply.MakeEmpty();
534
535 if (fLocationInfoReply.AddInt32("base directory device",
536 fRootDirectoryRef.device) != B_OK
537 || fLocationInfoReply.AddInt64("base directory node",
538 fRootDirectoryRef.node) != B_OK
539 || fLocationInfoReply.AddInt32("packages directory device",
540 PackagesDeviceID()) != B_OK
541 || fLocationInfoReply.AddInt64("packages directory node",
542 PackagesDirectoryID()) != B_OK) {
543 return;
544 }
545
546 for (PackageFileNameHashTable::Iterator it
547 = fLatestState->ByFileNameIterator(); it.HasNext();) {
548 Package* package = it.Next();
549 const char* fieldName = package->IsActive()
550 ? "latest active packages" : "latest inactive packages";
551 BMessage packageArchive;
552 if (package->Info().Archive(&packageArchive) != B_OK
553 || fLocationInfoReply.AddMessage(fieldName, &packageArchive)
554 != B_OK) {
555 return;
556 }
557 }
558
559 if (fActiveState != fLatestState) {
560 if (fPackagesDirectoryCount > 1) {
561 fLocationInfoReply.AddString("old state",
562 fPackagesDirectories[fPackagesDirectoryCount - 1].Name());
563 }
564
565 for (PackageFileNameHashTable::Iterator it
566 = fActiveState->ByFileNameIterator(); it.HasNext();) {
567 Package* package = it.Next();
568 if (!package->IsActive())
569 continue;
570
571 BMessage packageArchive;
572 if (package->Info().Archive(&packageArchive) != B_OK
573 || fLocationInfoReply.AddMessage("currently active packages",
574 &packageArchive) != B_OK) {
575 return;
576 }
577 }
578 }
579
580 if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK)
581 return;
582
583 locker.Unlock();
584
585 message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
586 kCommunicationTimeout);
587 }
588
589
590 void
HandleCommitTransactionRequest(BMessage * message)591 Volume::HandleCommitTransactionRequest(BMessage* message)
592 {
593 BCommitTransactionResult result;
594 PackageSet dummy;
595 _CommitTransaction(message, NULL, dummy, dummy, result);
596
597 BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY);
598 status_t error = result.AddToMessage(reply);
599 if (error != B_OK) {
600 ERROR("Volume::HandleCommitTransactionRequest(): Failed to add "
601 "transaction result to reply: %s\n", strerror(error));
602 return;
603 }
604
605 message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout);
606 }
607
608
609 void
PackageJobPending()610 Volume::PackageJobPending()
611 {
612 atomic_add(&fPendingPackageJobCount, 1);
613 }
614
615
616 void
PackageJobFinished()617 Volume::PackageJobFinished()
618 {
619 atomic_add(&fPendingPackageJobCount, -1);
620 }
621
622
623 bool
IsPackageJobPending() const624 Volume::IsPackageJobPending() const
625 {
626 return fPendingPackageJobCount != 0;
627 }
628
629
630 void
Unmounted()631 Volume::Unmounted()
632 {
633 if (fListener != NULL) {
634 stop_watching(BMessenger(this));
635 fListener = NULL;
636 }
637
638 if (BLooper* looper = Looper())
639 looper->RemoveHandler(this);
640 }
641
642
643 void
MessageReceived(BMessage * message)644 Volume::MessageReceived(BMessage* message)
645 {
646 switch (message->what) {
647 case B_NODE_MONITOR:
648 {
649 int32 opcode;
650 if (message->FindInt32("opcode", &opcode) != B_OK)
651 break;
652
653 switch (opcode) {
654 case B_ENTRY_CREATED:
655 _HandleEntryCreatedOrRemoved(message, true);
656 break;
657 case B_ENTRY_REMOVED:
658 _HandleEntryCreatedOrRemoved(message, false);
659 break;
660 case B_ENTRY_MOVED:
661 _HandleEntryMoved(message);
662 break;
663 default:
664 break;
665 }
666 break;
667 }
668
669 case kHandleNodeMonitorEvents:
670 if (fListener != NULL) {
671 if (system_time() >= fNodeMonitorEventHandleTime)
672 fListener->VolumeNodeMonitorEventOccurred(this);
673 }
674 break;
675
676 default:
677 BHandler::MessageReceived(message);
678 break;
679 }
680 }
681
682
683 BPackageInstallationLocation
Location() const684 Volume::Location() const
685 {
686 switch (fMountType) {
687 case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
688 return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
689 case PACKAGE_FS_MOUNT_TYPE_HOME:
690 return B_PACKAGE_INSTALLATION_LOCATION_HOME;
691 case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
692 default:
693 return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT;
694 }
695 }
696
697
698 const node_ref&
PackagesDirectoryRef() const699 Volume::PackagesDirectoryRef() const
700 {
701 return fPackagesDirectories[0].NodeRef();
702 }
703
704
705 PackageFileNameHashTable::Iterator
PackagesByFileNameIterator() const706 Volume::PackagesByFileNameIterator() const
707 {
708 return fLatestState->ByFileNameIterator();
709 }
710
711
712 int
OpenRootDirectory() const713 Volume::OpenRootDirectory() const
714 {
715 BDirectory directory;
716 status_t error = directory.SetTo(&fRootDirectoryRef);
717 if (error != B_OK) {
718 ERROR("Volume::OpenRootDirectory(): failed to open root directory: "
719 "%s\n", strerror(error));
720 RETURN_ERROR(error);
721 }
722
723 return directory.Dup();
724 }
725
726
727 void
ProcessPendingNodeMonitorEvents()728 Volume::ProcessPendingNodeMonitorEvents()
729 {
730 // get the events
731 NodeMonitorEventList events;
732 {
733 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
734 events.MoveFrom(&fPendingNodeMonitorEvents);
735 }
736
737 // process them
738 while (NodeMonitorEvent* event = events.RemoveHead()) {
739 ObjectDeleter<NodeMonitorEvent> eventDeleter(event);
740 if (event->WasCreated())
741 _PackagesEntryCreated(event->EntryName());
742 else
743 _PackagesEntryRemoved(event->EntryName());
744 }
745 }
746
747
748 bool
HasPendingPackageActivationChanges() const749 Volume::HasPendingPackageActivationChanges() const
750 {
751 return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty();
752 }
753
754
755 void
ProcessPendingPackageActivationChanges()756 Volume::ProcessPendingPackageActivationChanges()
757 {
758 if (!HasPendingPackageActivationChanges())
759 return;
760
761 // perform the request
762 BCommitTransactionResult result;
763 _CommitTransaction(NULL, NULL, fPackagesToBeActivated,
764 fPackagesToBeDeactivated, result);
765
766 if (result.Error() != B_TRANSACTION_OK) {
767 ERROR("Volume::ProcessPendingPackageActivationChanges(): package "
768 "activation failed: %s\n", result.FullErrorMessage().String());
769 // TODO: Notify the user!
770 }
771
772 // clear the activation/deactivation sets in any event
773 fPackagesToBeActivated.clear();
774 fPackagesToBeDeactivated.clear();
775 }
776
777
778 void
ClearPackageActivationChanges()779 Volume::ClearPackageActivationChanges()
780 {
781 fPackagesToBeActivated.clear();
782 fPackagesToBeDeactivated.clear();
783 }
784
785
786 status_t
CreateTransaction(BPackageInstallationLocation location,BActivationTransaction & _transaction,BDirectory & _transactionDirectory)787 Volume::CreateTransaction(BPackageInstallationLocation location,
788 BActivationTransaction& _transaction, BDirectory& _transactionDirectory)
789 {
790 // open admin directory
791 BDirectory adminDirectory;
792 status_t error = _OpenPackagesSubDirectory(
793 RelativePath(kAdminDirectoryName), true, adminDirectory);
794 if (error != B_OK)
795 return error;
796
797 // create a transaction directory
798 int uniqueId = 1;
799 BString directoryName;
800 for (;; uniqueId++) {
801 directoryName.SetToFormat("transaction-%d", uniqueId);
802 if (directoryName.IsEmpty())
803 return B_NO_MEMORY;
804
805 error = adminDirectory.CreateDirectory(directoryName,
806 &_transactionDirectory);
807 if (error == B_OK)
808 break;
809 if (error != B_FILE_EXISTS)
810 return error;
811 }
812
813 // init the transaction
814 error = _transaction.SetTo(location, fChangeCount, directoryName);
815 if (error != B_OK) {
816 BEntry entry;
817 _transactionDirectory.GetEntry(&entry);
818 _transactionDirectory.Unset();
819 if (entry.InitCheck() == B_OK)
820 entry.Remove();
821 return error;
822 }
823
824 return B_OK;
825 }
826
827
828 void
CommitTransaction(const BActivationTransaction & transaction,const PackageSet & packagesAlreadyAdded,const PackageSet & packagesAlreadyRemoved,BCommitTransactionResult & _result)829 Volume::CommitTransaction(const BActivationTransaction& transaction,
830 const PackageSet& packagesAlreadyAdded,
831 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result)
832 {
833 _CommitTransaction(NULL, &transaction, packagesAlreadyAdded,
834 packagesAlreadyRemoved, _result);
835 }
836
837
838 void
_HandleEntryCreatedOrRemoved(const BMessage * message,bool created)839 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created)
840 {
841 // only moves to or from our packages directory are interesting
842 int32 deviceID;
843 int64 directoryID;
844 const char* name;
845 if (message->FindInt32("device", &deviceID) != B_OK
846 || message->FindInt64("directory", &directoryID) != B_OK
847 || message->FindString("name", &name) != B_OK
848 || node_ref(deviceID, directoryID) != PackagesDirectoryRef()) {
849 return;
850 }
851
852 _QueueNodeMonitorEvent(name, created);
853 }
854
855
856 void
_HandleEntryMoved(const BMessage * message)857 Volume::_HandleEntryMoved(const BMessage* message)
858 {
859 int32 deviceID;
860 int64 fromDirectoryID;
861 int64 toDirectoryID;
862 const char* fromName;
863 const char* toName;
864 if (message->FindInt32("device", &deviceID) != B_OK
865 || message->FindInt64("from directory", &fromDirectoryID) != B_OK
866 || message->FindInt64("to directory", &toDirectoryID) != B_OK
867 || message->FindString("from name", &fromName) != B_OK
868 || message->FindString("name", &toName) != B_OK
869 || deviceID != PackagesDeviceID()
870 || (fromDirectoryID != PackagesDirectoryID()
871 && toDirectoryID != PackagesDirectoryID())) {
872 return;
873 }
874
875 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
876 // make sure for a move the two events cannot get split
877
878 if (fromDirectoryID == PackagesDirectoryID())
879 _QueueNodeMonitorEvent(fromName, false);
880 if (toDirectoryID == PackagesDirectoryID())
881 _QueueNodeMonitorEvent(toName, true);
882 }
883
884
885 void
_QueueNodeMonitorEvent(const BString & name,bool wasCreated)886 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated)
887 {
888 if (name.IsEmpty()) {
889 ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n");
890 return;
891 }
892
893 // ignore entries that don't have the ".hpkg" extension
894 if (!name.EndsWith(kPackageFileNameExtension))
895 return;
896
897 NodeMonitorEvent* event
898 = new(std::nothrow) NodeMonitorEvent(name, wasCreated);
899 if (event == NULL) {
900 ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n");
901 return;
902 }
903
904 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
905 fPendingNodeMonitorEvents.Add(event);
906 eventsLock.Unlock();
907
908 fNodeMonitorEventHandleTime
909 = system_time() + kNodeMonitorEventHandlingDelay;
910 BMessage message(kHandleNodeMonitorEvents);
911 BMessageRunner::StartSending(this, &message, kNodeMonitorEventHandlingDelay,
912 1);
913 }
914
915
916 void
_PackagesEntryCreated(const char * name)917 Volume::_PackagesEntryCreated(const char* name)
918 {
919 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name);
920 // Ignore the event, if the package is already known.
921 Package* package = fLatestState->FindPackage(name);
922 if (package != NULL) {
923 if (package->File()->EntryCreatedIgnoreLevel() > 0) {
924 package->File()->DecrementEntryCreatedIgnoreLevel();
925 } else {
926 WARN("node monitoring created event for already known entry "
927 "\"%s\"\n", name);
928 }
929
930 // Remove the package from the packages-to-be-deactivated set, if it is in
931 // there (unlikely, unless we see a remove-create sequence).
932 PackageSet::iterator it = fPackagesToBeDeactivated.find(package);
933 if (it != fPackagesToBeDeactivated.end())
934 fPackagesToBeDeactivated.erase(it);
935
936 return;
937 }
938
939 status_t error = fPackageFileManager->CreatePackage(
940 NotOwningEntryRef(PackagesDirectoryRef(), name),
941 package);
942 if (error != B_OK) {
943 ERROR("failed to init package for file \"%s\"\n", name);
944 return;
945 }
946
947 fLock.Lock();
948 fLatestState->AddPackage(package);
949 fChangeCount++;
950 fLock.Unlock();
951
952 try {
953 fPackagesToBeActivated.insert(package);
954 } catch (std::bad_alloc& exception) {
955 ERROR("out of memory\n");
956 return;
957 }
958 }
959
960
961 void
_PackagesEntryRemoved(const char * name)962 Volume::_PackagesEntryRemoved(const char* name)
963 {
964 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name);
965 Package* package = fLatestState->FindPackage(name);
966 if (package == NULL)
967 return;
968
969 // Ignore the event, if we generated it ourselves.
970 if (package->File()->EntryRemovedIgnoreLevel() > 0) {
971 package->File()->DecrementEntryRemovedIgnoreLevel();
972 return;
973 }
974
975 // Remove the package from the packages-to-be-activated set, if it is in
976 // there (unlikely, unless we see a create-remove-create sequence).
977 PackageSet::iterator it = fPackagesToBeActivated.find(package);
978 if (it != fPackagesToBeActivated.end())
979 fPackagesToBeActivated.erase(it);
980
981 // If the package isn't active, just remove it for good.
982 if (!package->IsActive()) {
983 AutoLocker<BLocker> locker(fLock);
984 fLatestState->RemovePackage(package);
985 fChangeCount++;
986 delete package;
987 return;
988 }
989
990 // The package must be deactivated.
991 try {
992 fPackagesToBeDeactivated.insert(package);
993 } catch (std::bad_alloc& exception) {
994 ERROR("out of memory\n");
995 return;
996 }
997 }
998
999
1000 status_t
_ReadPackagesDirectory()1001 Volume::_ReadPackagesDirectory()
1002 {
1003 BDirectory directory;
1004 status_t error = directory.SetTo(&PackagesDirectoryRef());
1005 if (error != B_OK) {
1006 ERROR("Volume::_ReadPackagesDirectory(): failed to open packages "
1007 "directory: %s\n", strerror(error));
1008 RETURN_ERROR(error);
1009 }
1010
1011 entry_ref entry;
1012 while (directory.GetNextRef(&entry) == B_OK) {
1013 if (!BString(entry.name).EndsWith(kPackageFileNameExtension))
1014 continue;
1015
1016 Package* package;
1017 status_t error = fPackageFileManager->CreatePackage(entry, package);
1018 if (error == B_OK) {
1019 AutoLocker<BLocker> locker(fLock);
1020 fLatestState->AddPackage(package);
1021 fChangeCount++;
1022 }
1023 }
1024
1025 return B_OK;
1026 }
1027
1028
1029 status_t
_InitLatestState()1030 Volume::_InitLatestState()
1031 {
1032 if (_InitLatestStateFromActivatedPackages() == B_OK)
1033 return B_OK;
1034
1035 INFORM("Failed to get activated packages info from activated packages file."
1036 " Assuming all package files in package directory are activated.\n");
1037
1038 AutoLocker<BLocker> locker(fLock);
1039
1040 for (PackageFileNameHashTable::Iterator it
1041 = fLatestState->ByFileNameIterator();
1042 Package* package = it.Next();) {
1043 fLatestState->SetPackageActive(package, true);
1044 fChangeCount++;
1045 }
1046
1047 return B_OK;
1048 }
1049
1050
1051 status_t
_InitLatestStateFromActivatedPackages()1052 Volume::_InitLatestStateFromActivatedPackages()
1053 {
1054 // open admin directory
1055 BDirectory adminDirectory;
1056 status_t error = _OpenPackagesSubDirectory(
1057 RelativePath(kAdminDirectoryName), false, adminDirectory);
1058 if (error != B_OK)
1059 RETURN_ERROR(error);
1060
1061 node_ref adminNode;
1062 error = adminDirectory.GetNodeRef(&adminNode);
1063 if (error != B_OK)
1064 RETURN_ERROR(error);
1065
1066 // try reading the activation file
1067 NotOwningEntryRef entryRef(adminNode, kActivationFileName);
1068 BFile file;
1069 error = file.SetTo(&entryRef, B_READ_ONLY);
1070 if (error != B_OK) {
1071 BEntry activationEntry(&entryRef);
1072 BPath activationPath;
1073 const char *activationFilePathName = "Unknown due to errors";
1074 if (activationEntry.InitCheck() == B_OK &&
1075 activationEntry.GetPath(&activationPath) == B_OK)
1076 activationFilePathName = activationPath.Path();
1077 INFORM("Failed to open packages activation file %s: %s\n",
1078 activationFilePathName, strerror(error));
1079 RETURN_ERROR(error);
1080 }
1081
1082 // read the whole file into memory to simplify things
1083 off_t size;
1084 error = file.GetSize(&size);
1085 if (error != B_OK) {
1086 ERROR("Failed to packages activation file size: %s\n",
1087 strerror(error));
1088 RETURN_ERROR(error);
1089 }
1090
1091 if (size > (off_t)kMaxActivationFileSize) {
1092 ERROR("The packages activation file is too big.\n");
1093 RETURN_ERROR(B_BAD_DATA);
1094 }
1095
1096 char* fileContent = (char*)malloc(size + 1);
1097 if (fileContent == NULL)
1098 RETURN_ERROR(B_NO_MEMORY);
1099 MemoryDeleter fileContentDeleter(fileContent);
1100
1101 ssize_t bytesRead = file.Read(fileContent, size);
1102 if (bytesRead < 0) {
1103 ERROR("Failed to read packages activation file: %s\n",
1104 strerror(bytesRead));
1105 RETURN_ERROR(errno);
1106 }
1107
1108 if (bytesRead != size) {
1109 ERROR("Failed to read whole packages activation file.\n");
1110 RETURN_ERROR(B_ERROR);
1111 }
1112
1113 // null-terminate to simplify parsing
1114 fileContent[size] = '\0';
1115
1116 AutoLocker<BLocker> locker(fLock);
1117
1118 // parse the file and mark the respective packages active
1119 const char* packageName = fileContent;
1120 char* const fileContentEnd = fileContent + size;
1121 while (packageName < fileContentEnd) {
1122 char* packageNameEnd = strchr(packageName, '\n');
1123 if (packageNameEnd == NULL)
1124 packageNameEnd = fileContentEnd;
1125
1126 // skip empty lines
1127 if (packageName == packageNameEnd) {
1128 packageName++;
1129 continue;
1130 }
1131 *packageNameEnd = '\0';
1132
1133 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
1134 ERROR("Invalid packages activation file content.\n");
1135 RETURN_ERROR(B_BAD_DATA);
1136 }
1137
1138 Package* package = fLatestState->FindPackage(packageName);
1139 if (package != NULL) {
1140 fLatestState->SetPackageActive(package, true);
1141 fChangeCount++;
1142 } else {
1143 WARN("Package \"%s\" from activation file not in packages "
1144 "directory.\n", packageName);
1145 }
1146
1147 packageName = packageNameEnd + 1;
1148 }
1149
1150 return B_OK;
1151 }
1152
1153
1154 status_t
_GetActivePackages(int fd)1155 Volume::_GetActivePackages(int fd)
1156 {
1157 // get the info from packagefs
1158 PackageFSGetPackageInfosRequest* request = NULL;
1159 MemoryDeleter requestDeleter;
1160 size_t bufferSize = 64 * 1024;
1161 for (;;) {
1162 request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize);
1163 if (request == NULL)
1164 RETURN_ERROR(B_NO_MEMORY);
1165 requestDeleter.SetTo(request);
1166
1167 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request,
1168 bufferSize) != 0) {
1169 ERROR("Volume::_GetActivePackages(): failed to get active package "
1170 "info from package FS: %s\n", strerror(errno));
1171 RETURN_ERROR(errno);
1172 }
1173
1174 if (request->bufferSize <= bufferSize)
1175 break;
1176
1177 bufferSize = request->bufferSize;
1178 requestDeleter.Unset();
1179 }
1180
1181 #if 0
1182 INFORM("latest volume state:\n");
1183 _DumpState(fLatestState);
1184 #endif
1185
1186 // check whether that matches the expected state
1187 if (_CheckActivePackagesMatchLatestState(request)) {
1188 INFORM("The latest volume state is also the currently active one\n");
1189 fActiveState = fLatestState;
1190 return B_OK;
1191 }
1192
1193 // There's a mismatch. We need a new state that reflects the actual
1194 // activation situation.
1195 VolumeState* state = new(std::nothrow) VolumeState;
1196 if (state == NULL)
1197 RETURN_ERROR(B_NO_MEMORY);
1198 ObjectDeleter<VolumeState> stateDeleter(state);
1199
1200 for (uint32 i = 0; i < request->packageCount; i++) {
1201 const PackageFSPackageInfo& info = request->infos[i];
1202 NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID,
1203 info.name);
1204 Package* package;
1205 status_t error = fPackageFileManager->CreatePackage(entryRef, package);
1206 if (error != B_OK) {
1207 WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %"
1208 B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID,
1209 info.directoryNodeID, info.name, strerror(error));
1210 continue;
1211 }
1212
1213 state->AddPackage(package);
1214 state->SetPackageActive(package, true);
1215 }
1216
1217 #if 0
1218 INFORM("currently active volume state:\n");
1219 _DumpState(state);
1220 #endif
1221
1222 fActiveState = stateDeleter.Detach();
1223 return B_OK;
1224 }
1225
1226
1227 void
_RunQueuedScripts()1228 Volume::_RunQueuedScripts()
1229 {
1230 BDirectory adminDirectory;
1231 status_t error = _OpenPackagesSubDirectory(
1232 RelativePath(kAdminDirectoryName), false, adminDirectory);
1233 if (error != B_OK)
1234 return;
1235
1236 BDirectory scriptsDirectory;
1237 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName);
1238 if (error != B_OK)
1239 return;
1240
1241 // enumerate all the symlinks in the queued scripts directory
1242 BEntry scriptEntry;
1243 while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) {
1244 BPath scriptPath;
1245 scriptEntry.GetPath(&scriptPath);
1246 error = scriptPath.InitCheck();
1247 if (error != B_OK) {
1248 INFORM("failed to get path of post-installation script \"%s\"\n",
1249 strerror(error));
1250 continue;
1251 }
1252
1253 errno = 0;
1254 int result = system(scriptPath.Path());
1255 if (result != 0) {
1256 INFORM("running post-installation script \"%s\" "
1257 "failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno));
1258 }
1259
1260 // remove the symlink, now that we've run the post-installation script
1261 error = scriptEntry.Remove();
1262 if (error != B_OK) {
1263 INFORM("removing queued post-install script failed \"%s\"\n",
1264 strerror(error));
1265 }
1266 }
1267 }
1268
1269
1270 bool
_CheckActivePackagesMatchLatestState(PackageFSGetPackageInfosRequest * request)1271 Volume::_CheckActivePackagesMatchLatestState(
1272 PackageFSGetPackageInfosRequest* request)
1273 {
1274 if (fPackagesDirectoryCount != 1) {
1275 INFORM("An old packages state (\"%s\") seems to be active.\n",
1276 fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String());
1277 return false;
1278 }
1279
1280 const node_ref packagesDirRef(PackagesDirectoryRef());
1281
1282 // mark the returned packages active
1283 for (uint32 i = 0; i < request->packageCount; i++) {
1284 const PackageFSPackageInfo& info = request->infos[i];
1285 if (node_ref(info.directoryDeviceID, info.directoryNodeID)
1286 != packagesDirRef) {
1287 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1288 ") not in packages directory\n", info.name,
1289 info.packageDeviceID, info.packageNodeID);
1290 return false;
1291 }
1292
1293 Package* package = fLatestState->FindPackage(
1294 node_ref(info.packageDeviceID, info.packageNodeID));
1295 if (package == NULL || !package->IsActive()) {
1296 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1297 ") not %s\n", info.name,
1298 info.packageDeviceID, info.packageNodeID,
1299 package == NULL
1300 ? "found in packages directory" : "supposed to be active");
1301 return false;
1302 }
1303 }
1304
1305 // Check whether there are packages that aren't active but should be.
1306 uint32 count = 0;
1307 for (PackageNodeRefHashTable::Iterator it
1308 = fLatestState->ByNodeRefIterator(); it.HasNext();) {
1309 Package* package = it.Next();
1310 if (package->IsActive())
1311 count++;
1312 }
1313
1314 if (count != request->packageCount) {
1315 INFORM("There seem to be packages in the packages directory that "
1316 "should be active.\n");
1317 return false;
1318 }
1319
1320 return true;
1321 }
1322
1323
1324 void
_SetLatestState(VolumeState * state,bool isActive)1325 Volume::_SetLatestState(VolumeState* state, bool isActive)
1326 {
1327 AutoLocker<BLocker> locker(fLock);
1328
1329 bool sendNotification = fRoot->IsSystemRoot();
1330 // Send a notification, if this is a system root volume.
1331 BStringList addedPackageNames;
1332 BStringList removedPackageNames;
1333
1334 // If a notification should be sent then assemble the latest and incoming
1335 // set of the packages' names. This can be used to figure out which
1336 // packages are added and which are removed.
1337
1338 if (sendNotification) {
1339 _CollectPackageNamesAdded(fLatestState, state, addedPackageNames);
1340 _CollectPackageNamesAdded(state, fLatestState, removedPackageNames);
1341 }
1342
1343 if (isActive) {
1344 if (fLatestState != fActiveState)
1345 delete fActiveState;
1346 fActiveState = state;
1347 }
1348
1349 if (fLatestState != fActiveState)
1350 delete fLatestState;
1351 fLatestState = state;
1352 fChangeCount++;
1353
1354 locker.Unlock();
1355
1356 // Send a notification, if this is a system root volume.
1357 if (sendNotification) {
1358 BMessage message(B_PACKAGE_UPDATE);
1359 if (message.AddInt32("event",
1360 (int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK
1361 && message.AddStrings("added package names",
1362 addedPackageNames) == B_OK
1363 && message.AddStrings("removed package names",
1364 removedPackageNames) == B_OK
1365 && message.AddInt32("location", (int32)Location()) == B_OK
1366 && message.AddInt64("change count", fChangeCount) == B_OK) {
1367 BRoster::Private().SendTo(&message, NULL, false);
1368 }
1369 }
1370 }
1371
1372
1373 /*static*/ void
_CollectPackageNamesAdded(const VolumeState * oldState,const VolumeState * newState,BStringList & addedPackageNames)1374 Volume::_CollectPackageNamesAdded(const VolumeState* oldState,
1375 const VolumeState* newState, BStringList& addedPackageNames)
1376 {
1377 if (newState == NULL)
1378 return;
1379
1380 for (PackageFileNameHashTable::Iterator it
1381 = newState->ByFileNameIterator(); it.HasNext();) {
1382 Package* package = it.Next();
1383 BString packageName = package->Info().Name();
1384 if (oldState == NULL)
1385 addedPackageNames.Add(packageName);
1386 else {
1387 Package* oldStatePackage = oldState->FindPackage(
1388 package->FileName());
1389 if (oldStatePackage == NULL)
1390 addedPackageNames.Add(packageName);
1391 }
1392 }
1393 }
1394
1395
1396 void
_DumpState(VolumeState * state)1397 Volume::_DumpState(VolumeState* state)
1398 {
1399 uint32 inactiveCount = 0;
1400 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1401 it.HasNext();) {
1402 Package* package = it.Next();
1403 if (package->IsActive()) {
1404 INFORM("active package: \"%s\"\n", package->FileName().String());
1405 } else
1406 inactiveCount++;
1407 }
1408
1409 if (inactiveCount == 0)
1410 return;
1411
1412 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1413 it.HasNext();) {
1414 Package* package = it.Next();
1415 if (!package->IsActive())
1416 INFORM("inactive package: \"%s\"\n", package->FileName().String());
1417 }
1418 }
1419
1420
1421 status_t
_AddRepository(BSolver * solver,BSolverRepository & repository,bool activeOnly,bool installed)1422 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository,
1423 bool activeOnly, bool installed)
1424 {
1425 status_t error = repository.SetTo(Path());
1426 if (error != B_OK) {
1427 ERROR("Volume::_AddRepository(): failed to init repository: %s\n",
1428 strerror(error));
1429 return error;
1430 }
1431
1432 repository.SetInstalled(installed);
1433
1434 error = AddPackagesToRepository(repository, true);
1435 if (error != B_OK) {
1436 ERROR("Volume::_AddRepository(): failed to add packages to "
1437 "repository: %s\n", strerror(error));
1438 return error;
1439 }
1440
1441 error = solver->AddRepository(&repository);
1442 if (error != B_OK) {
1443 ERROR("Volume::_AddRepository(): failed to add repository to solver: "
1444 "%s\n", strerror(error));
1445 return error;
1446 }
1447
1448 return B_OK;
1449 }
1450
1451
1452 status_t
_OpenPackagesSubDirectory(const RelativePath & path,bool create,BDirectory & _directory)1453 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
1454 BDirectory& _directory)
1455 {
1456 // open the packages directory
1457 BDirectory directory;
1458 status_t error = directory.SetTo(&PackagesDirectoryRef());
1459 if (error != B_OK) {
1460 ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages "
1461 "directory: %s\n", strerror(error));
1462 RETURN_ERROR(error);
1463 }
1464
1465 return FSUtils::OpenSubDirectory(directory, path, create, _directory);
1466 }
1467
1468
1469 void
_CommitTransaction(BMessage * message,const BActivationTransaction * transaction,const PackageSet & packagesAlreadyAdded,const PackageSet & packagesAlreadyRemoved,BCommitTransactionResult & _result)1470 Volume::_CommitTransaction(BMessage* message,
1471 const BActivationTransaction* transaction,
1472 const PackageSet& packagesAlreadyAdded,
1473 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result)
1474 {
1475 _result.Unset();
1476
1477 // perform the request
1478 CommitTransactionHandler handler(this, fPackageFileManager, _result);
1479 BTransactionError error = B_TRANSACTION_INTERNAL_ERROR;
1480 try {
1481 handler.Init(fLatestState, fLatestState == fActiveState,
1482 packagesAlreadyAdded, packagesAlreadyRemoved);
1483
1484 if (message != NULL)
1485 handler.HandleRequest(message);
1486 else if (transaction != NULL)
1487 handler.HandleRequest(*transaction);
1488 else
1489 handler.HandleRequest();
1490
1491 _SetLatestState(handler.DetachVolumeState(),
1492 handler.IsActiveVolumeState());
1493 error = B_TRANSACTION_OK;
1494 } catch (Exception& exception) {
1495 error = exception.Error();
1496 exception.SetOnResult(_result);
1497 if (_result.ErrorPackage().IsEmpty()
1498 && handler.CurrentPackage() != NULL) {
1499 _result.SetErrorPackage(handler.CurrentPackage()->FileName());
1500 }
1501 } catch (std::bad_alloc& exception) {
1502 error = B_TRANSACTION_NO_MEMORY;
1503 }
1504
1505 _result.SetError(error);
1506
1507 // revert on error
1508 if (error != B_TRANSACTION_OK)
1509 handler.Revert();
1510 }
1511