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