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