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