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
53using namespace BPackageKit::BPrivate;
54
55
56// #pragma mark - Listener
57
58
59Volume::Listener::~Listener()
60{
61}
62
63
64// #pragma mark - NodeMonitorEvent
65
66
67struct Volume::NodeMonitorEvent
68	: public DoublyLinkedListLinkImpl<NodeMonitorEvent> {
69public:
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
87private:
88	BString	fEntryName;
89	bool	fCreated;
90};
91
92
93// #pragma mark - PackagesDirectory
94
95
96struct Volume::PackagesDirectory {
97public:
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
133private:
134	node_ref	fNodeRef;
135	BString		fName;
136};
137
138
139// #pragma mark - Volume
140
141
142Volume::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
169Volume::~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
181status_t
182Volume::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
296status_t
297Volume::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
409status_t
410Volume::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
431void
432Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume)
433{
434INFORM("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
517void
518Volume::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
590void
591Volume::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
609void
610Volume::PackageJobPending()
611{
612	atomic_add(&fPendingPackageJobCount, 1);
613}
614
615
616void
617Volume::PackageJobFinished()
618{
619	atomic_add(&fPendingPackageJobCount, -1);
620}
621
622
623bool
624Volume::IsPackageJobPending() const
625{
626	return fPendingPackageJobCount != 0;
627}
628
629
630void
631Volume::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
643void
644Volume::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
683BPackageInstallationLocation
684Volume::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
698const node_ref&
699Volume::PackagesDirectoryRef() const
700{
701	return fPackagesDirectories[0].NodeRef();
702}
703
704
705PackageFileNameHashTable::Iterator
706Volume::PackagesByFileNameIterator() const
707{
708	return fLatestState->ByFileNameIterator();
709}
710
711
712int
713Volume::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
727void
728Volume::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
748bool
749Volume::HasPendingPackageActivationChanges() const
750{
751	return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty();
752}
753
754
755void
756Volume::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
778void
779Volume::ClearPackageActivationChanges()
780{
781	fPackagesToBeActivated.clear();
782	fPackagesToBeDeactivated.clear();
783}
784
785
786status_t
787Volume::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
828void
829Volume::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
838void
839Volume::_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
856void
857Volume::_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
885void
886Volume::_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
916void
917Volume::_PackagesEntryCreated(const char* name)
918{
919INFORM("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
961void
962Volume::_PackagesEntryRemoved(const char* name)
963{
964INFORM("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
1000status_t
1001Volume::_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
1029status_t
1030Volume::_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
1051status_t
1052Volume::_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
1154status_t
1155Volume::_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
1227void
1228Volume::_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
1270bool
1271Volume::_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
1324void
1325Volume::_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
1374Volume::_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
1396void
1397Volume::_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
1421status_t
1422Volume::_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
1452status_t
1453Volume::_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
1469void
1470Volume::_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