xref: /haiku/src/servers/package/Volume.cpp (revision 9e25244c5e9051f6cd333820d6332397361abd6c)
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 	INFORM("latest volume state:\n");
1182 	_DumpState(fLatestState);
1183 
1184 	// check whether that matches the expected state
1185 	if (_CheckActivePackagesMatchLatestState(request)) {
1186 		INFORM("The latest volume state is also the currently active one\n");
1187 		fActiveState = fLatestState;
1188 		return B_OK;
1189 	}
1190 
1191 	// There's a mismatch. We need a new state that reflects the actual
1192 	// activation situation.
1193 	VolumeState* state = new(std::nothrow) VolumeState;
1194 	if (state == NULL)
1195 		RETURN_ERROR(B_NO_MEMORY);
1196 	ObjectDeleter<VolumeState> stateDeleter(state);
1197 
1198 	for (uint32 i = 0; i < request->packageCount; i++) {
1199 		const PackageFSPackageInfo& info = request->infos[i];
1200 		NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID,
1201 			info.name);
1202 		Package* package;
1203 		status_t error = fPackageFileManager->CreatePackage(entryRef, package);
1204 		if (error != B_OK) {
1205 			WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %"
1206 				B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID,
1207 				info.directoryNodeID, info.name, strerror(error));
1208 			continue;
1209 		}
1210 
1211 		state->AddPackage(package);
1212 		state->SetPackageActive(package, true);
1213 	}
1214 
1215 	INFORM("currently active volume state:\n");
1216 	_DumpState(state);
1217 
1218 	fActiveState = stateDeleter.Detach();
1219 	return B_OK;
1220 }
1221 
1222 
1223 void
1224 Volume::_RunQueuedScripts()
1225 {
1226 	BDirectory adminDirectory;
1227 	status_t error = _OpenPackagesSubDirectory(
1228 		RelativePath(kAdminDirectoryName), false, adminDirectory);
1229 	if (error != B_OK)
1230 		return;
1231 
1232 	BDirectory scriptsDirectory;
1233 	error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName);
1234 	if (error != B_OK)
1235 		return;
1236 
1237 	// enumerate all the symlinks in the queued scripts directory
1238 	BEntry scriptEntry;
1239 	while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) {
1240 		BPath scriptPath;
1241 		scriptEntry.GetPath(&scriptPath);
1242 		error = scriptPath.InitCheck();
1243 		if (error != B_OK) {
1244 			INFORM("failed to get path of post-installation script \"%s\"\n",
1245 				strerror(error));
1246 			continue;
1247 		}
1248 
1249 		errno = 0;
1250 		int result = system(scriptPath.Path());
1251 		if (result != 0) {
1252 			INFORM("running post-installation script \"%s\" "
1253 				"failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno));
1254 		}
1255 
1256 		// remove the symlink, now that we've run the post-installation script
1257 		error = scriptEntry.Remove();
1258 		if (error != B_OK) {
1259 			INFORM("removing queued post-install script failed \"%s\"\n",
1260 				strerror(error));
1261 		}
1262 	}
1263 }
1264 
1265 
1266 bool
1267 Volume::_CheckActivePackagesMatchLatestState(
1268 	PackageFSGetPackageInfosRequest* request)
1269 {
1270 	if (fPackagesDirectoryCount != 1) {
1271 		INFORM("An old packages state (\"%s\") seems to be active.\n",
1272 			fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String());
1273 		return false;
1274 	}
1275 
1276 	const node_ref packagesDirRef(PackagesDirectoryRef());
1277 
1278 	// mark the returned packages active
1279 	for (uint32 i = 0; i < request->packageCount; i++) {
1280 		const PackageFSPackageInfo& info = request->infos[i];
1281 		if (node_ref(info.directoryDeviceID, info.directoryNodeID)
1282 				!= packagesDirRef) {
1283 			WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1284 				") not in packages directory\n", info.name,
1285 				info.packageDeviceID, info.packageNodeID);
1286 			return false;
1287 		}
1288 
1289 		Package* package = fLatestState->FindPackage(
1290 			node_ref(info.packageDeviceID, info.packageNodeID));
1291 		if (package == NULL || !package->IsActive()) {
1292 			WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1293 				") not %s\n", info.name,
1294 				info.packageDeviceID, info.packageNodeID,
1295 				package == NULL
1296 					? "found in packages directory" : "supposed to be active");
1297 			return false;
1298 		}
1299 	}
1300 
1301 	// Check whether there are packages that aren't active but should be.
1302 	uint32 count = 0;
1303 	for (PackageNodeRefHashTable::Iterator it
1304 			= fLatestState->ByNodeRefIterator(); it.HasNext();) {
1305 		Package* package = it.Next();
1306 		if (package->IsActive())
1307 			count++;
1308 	}
1309 
1310 	if (count != request->packageCount) {
1311 		INFORM("There seem to be packages in the packages directory that "
1312 			"should be active.\n");
1313 		return false;
1314 	}
1315 
1316 	return true;
1317 }
1318 
1319 
1320 void
1321 Volume::_SetLatestState(VolumeState* state, bool isActive)
1322 {
1323 	AutoLocker<BLocker> locker(fLock);
1324 
1325 	bool sendNotification = fRoot->IsSystemRoot();
1326 		// Send a notification, if this is a system root volume.
1327 	BStringList addedPackageNames;
1328 	BStringList removedPackageNames;
1329 
1330 	// If a notification should be sent then assemble the latest and incoming
1331 	// set of the packages' names.  This can be used to figure out which
1332 	// packages are added and which are removed.
1333 
1334 	if (sendNotification) {
1335 		_CollectPackageNamesAdded(fLatestState, state, addedPackageNames);
1336 		_CollectPackageNamesAdded(state, fLatestState, removedPackageNames);
1337 	}
1338 
1339 	if (isActive) {
1340 		if (fLatestState != fActiveState)
1341 			delete fActiveState;
1342 		fActiveState = state;
1343 	}
1344 
1345 	if (fLatestState != fActiveState)
1346 		delete fLatestState;
1347 	fLatestState = state;
1348 	fChangeCount++;
1349 
1350 	locker.Unlock();
1351 
1352 	// Send a notification, if this is a system root volume.
1353 	if (sendNotification) {
1354 		BMessage message(B_PACKAGE_UPDATE);
1355 		if (message.AddInt32("event",
1356 				(int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK
1357 			&& message.AddStrings("added package names",
1358 				addedPackageNames) == B_OK
1359 			&& message.AddStrings("removed package names",
1360 				removedPackageNames) == B_OK
1361 			&& message.AddInt32("location", (int32)Location()) == B_OK
1362 			&& message.AddInt64("change count", fChangeCount) == B_OK) {
1363 			BRoster::Private().SendTo(&message, NULL, false);
1364 		}
1365 	}
1366 }
1367 
1368 
1369 /*static*/ void
1370 Volume::_CollectPackageNamesAdded(const VolumeState* oldState,
1371 	const VolumeState* newState, BStringList& addedPackageNames)
1372 {
1373 	if (newState == NULL)
1374 		return;
1375 
1376 	for (PackageFileNameHashTable::Iterator it
1377 			= newState->ByFileNameIterator(); it.HasNext();) {
1378 		Package* package = it.Next();
1379 		BString packageName = package->Info().Name();
1380 		if (oldState == NULL)
1381 			addedPackageNames.Add(packageName);
1382 		else {
1383 			Package* oldStatePackage = oldState->FindPackage(
1384 				package->FileName());
1385 			if (oldStatePackage == NULL)
1386 				addedPackageNames.Add(packageName);
1387 		}
1388 	}
1389 }
1390 
1391 
1392 void
1393 Volume::_DumpState(VolumeState* state)
1394 {
1395 	uint32 inactiveCount = 0;
1396 	for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1397 			it.HasNext();) {
1398 		Package* package = it.Next();
1399 		if (package->IsActive()) {
1400 			INFORM("active package: \"%s\"\n", package->FileName().String());
1401 		} else
1402 			inactiveCount++;
1403 	}
1404 
1405 	if (inactiveCount == 0)
1406 		return;
1407 
1408 	for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1409 			it.HasNext();) {
1410 		Package* package = it.Next();
1411 		if (!package->IsActive())
1412 			INFORM("inactive package: \"%s\"\n", package->FileName().String());
1413 	}
1414 }
1415 
1416 
1417 status_t
1418 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository,
1419 	bool activeOnly, bool installed)
1420 {
1421 	status_t error = repository.SetTo(Path());
1422 	if (error != B_OK) {
1423 		ERROR("Volume::_AddRepository(): failed to init repository: %s\n",
1424 			strerror(error));
1425 		return error;
1426 	}
1427 
1428 	repository.SetInstalled(installed);
1429 
1430 	error = AddPackagesToRepository(repository, true);
1431 	if (error != B_OK) {
1432 		ERROR("Volume::_AddRepository(): failed to add packages to "
1433 			"repository: %s\n", strerror(error));
1434 		return error;
1435 	}
1436 
1437 	error = solver->AddRepository(&repository);
1438 	if (error != B_OK) {
1439 		ERROR("Volume::_AddRepository(): failed to add repository to solver: "
1440 			"%s\n", strerror(error));
1441 		return error;
1442 	}
1443 
1444 	return B_OK;
1445 }
1446 
1447 
1448 status_t
1449 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
1450 	BDirectory& _directory)
1451 {
1452 	// open the packages directory
1453 	BDirectory directory;
1454 	status_t error = directory.SetTo(&PackagesDirectoryRef());
1455 	if (error != B_OK) {
1456 		ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages "
1457 			"directory: %s\n", strerror(error));
1458 		RETURN_ERROR(error);
1459 	}
1460 
1461 	return FSUtils::OpenSubDirectory(directory, path, create, _directory);
1462 }
1463 
1464 
1465 void
1466 Volume::_CommitTransaction(BMessage* message,
1467 	const BActivationTransaction* transaction,
1468 	const PackageSet& packagesAlreadyAdded,
1469 	const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result)
1470 {
1471 	_result.Unset();
1472 
1473 	// perform the request
1474 	CommitTransactionHandler handler(this, fPackageFileManager, _result);
1475 	BTransactionError error = B_TRANSACTION_INTERNAL_ERROR;
1476 	try {
1477 		handler.Init(fLatestState, fLatestState == fActiveState,
1478 			packagesAlreadyAdded, packagesAlreadyRemoved);
1479 
1480 		if (message != NULL)
1481 			handler.HandleRequest(message);
1482 		else if (transaction != NULL)
1483 			handler.HandleRequest(*transaction);
1484 		else
1485 			handler.HandleRequest();
1486 
1487 		_SetLatestState(handler.DetachVolumeState(),
1488 			handler.IsActiveVolumeState());
1489 		error = B_TRANSACTION_OK;
1490 	} catch (Exception& exception) {
1491 		error = exception.Error();
1492 		exception.SetOnResult(_result);
1493 		if (_result.ErrorPackage().IsEmpty()
1494 			&& handler.CurrentPackage() != NULL) {
1495 			_result.SetErrorPackage(handler.CurrentPackage()->FileName());
1496 		}
1497 	} catch (std::bad_alloc& exception) {
1498 		error = B_TRANSACTION_NO_MEMORY;
1499 	}
1500 
1501 	_result.SetError(error);
1502 
1503 	// revert on error
1504 	if (error != B_TRANSACTION_OK)
1505 		handler.Revert();
1506 }
1507