xref: /haiku/src/servers/package/Volume.cpp (revision 084e24d0bf3808808e2bf58d4d65f493bd2b8f49)
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 
59 Volume::Listener::~Listener()
60 {
61 }
62 
63 
64 // #pragma mark - NodeMonitorEvent
65 
66 
67 struct Volume::NodeMonitorEvent
68 	: public DoublyLinkedListLinkImpl<NodeMonitorEvent> {
69 public:
70 	NodeMonitorEvent(const BString& entryName, bool created)
71 		:
72 		fEntryName(entryName),
73 		fCreated(created)
74 	{
75 	}
76 
77 	const BString& EntryName() const
78 	{
79 		return fEntryName;
80 	}
81 
82 	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:
98 	PackagesDirectory()
99 		:
100 		fNodeRef(),
101 		fName()
102 	{
103 	}
104 
105 	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 
123 	const node_ref& NodeRef() const
124 	{
125 		return fNodeRef;
126 	}
127 
128 	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 
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 
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
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
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
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
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
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
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
610 Volume::PackageJobPending()
611 {
612 	atomic_add(&fPendingPackageJobCount, 1);
613 }
614 
615 
616 void
617 Volume::PackageJobFinished()
618 {
619 	atomic_add(&fPendingPackageJobCount, -1);
620 }
621 
622 
623 bool
624 Volume::IsPackageJobPending() const
625 {
626 	return fPendingPackageJobCount != 0;
627 }
628 
629 
630 void
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
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
684 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&
699 Volume::PackagesDirectoryRef() const
700 {
701 	return fPackagesDirectories[0].NodeRef();
702 }
703 
704 
705 PackageFileNameHashTable::Iterator
706 Volume::PackagesByFileNameIterator() const
707 {
708 	return fLatestState->ByFileNameIterator();
709 }
710 
711 
712 int
713 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
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
749 Volume::HasPendingPackageActivationChanges() const
750 {
751 	return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty();
752 }
753 
754 
755 void
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
779 Volume::ClearPackageActivationChanges()
780 {
781 	fPackagesToBeActivated.clear();
782 	fPackagesToBeDeactivated.clear();
783 }
784 
785 
786 status_t
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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