xref: /haiku/src/servers/package/Volume.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 	// try reading the activation file
990 	NotOwningEntryRef entryRef(PackagesDirectoryRef(), kActivationFileName);
991 	BFile file;
992 	status_t error = file.SetTo(&entryRef, B_READ_ONLY);
993 	if (error != B_OK) {
994 		INFORM("Failed to open packages activation file: %s\n",
995 			strerror(error));
996 		RETURN_ERROR(error);
997 	}
998 
999 	// read the whole file into memory to simplify things
1000 	off_t size;
1001 	error = file.GetSize(&size);
1002 	if (error != B_OK) {
1003 		ERROR("Failed to packages activation file size: %s\n",
1004 			strerror(error));
1005 		RETURN_ERROR(error);
1006 	}
1007 
1008 	if (size > (off_t)kMaxActivationFileSize) {
1009 		ERROR("The packages activation file is too big.\n");
1010 		RETURN_ERROR(B_BAD_DATA);
1011 	}
1012 
1013 	char* fileContent = (char*)malloc(size + 1);
1014 	if (fileContent == NULL)
1015 		RETURN_ERROR(B_NO_MEMORY);
1016 	MemoryDeleter fileContentDeleter(fileContent);
1017 
1018 	ssize_t bytesRead = file.Read(fileContent, size);
1019 	if (bytesRead < 0) {
1020 		ERROR("Failed to read packages activation file: %s\n",
1021 			strerror(bytesRead));
1022 		RETURN_ERROR(errno);
1023 	}
1024 
1025 	if (bytesRead != size) {
1026 		ERROR("Failed to read whole packages activation file.\n");
1027 		RETURN_ERROR(B_ERROR);
1028 	}
1029 
1030 	// null-terminate to simplify parsing
1031 	fileContent[size] = '\0';
1032 
1033 	AutoLocker<BLocker> locker(fLock);
1034 
1035 	// parse the file and mark the respective packages active
1036 	const char* packageName = fileContent;
1037 	char* const fileContentEnd = fileContent + size;
1038 	while (packageName < fileContentEnd) {
1039 		char* packageNameEnd = strchr(packageName, '\n');
1040 		if (packageNameEnd == NULL)
1041 			packageNameEnd = fileContentEnd;
1042 
1043 		// skip empty lines
1044 		if (packageName == packageNameEnd) {
1045 			packageName++;
1046 			continue;
1047 		}
1048 		*packageNameEnd = '\0';
1049 
1050 		if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
1051 			ERROR("Invalid packages activation file content.\n");
1052 			RETURN_ERROR(B_BAD_DATA);
1053 		}
1054 
1055 		Package* package = fLatestState->FindPackage(packageName);
1056 		if (package != NULL) {
1057 			fLatestState->SetPackageActive(package, true);
1058 			fChangeCount++;
1059 		} else {
1060 			WARN("Package \"%s\" from activation file not in packages "
1061 				"directory.\n", packageName);
1062 		}
1063 
1064 		packageName = packageNameEnd + 1;
1065 	}
1066 
1067 	return B_OK;
1068 }
1069 
1070 
1071 status_t
1072 Volume::_GetActivePackages(int fd)
1073 {
1074 	// get the info from packagefs
1075 	PackageFSGetPackageInfosRequest* request = NULL;
1076 	MemoryDeleter requestDeleter;
1077 	size_t bufferSize = 64 * 1024;
1078 	for (;;) {
1079 		request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize);
1080 		if (request == NULL)
1081 			RETURN_ERROR(B_NO_MEMORY);
1082 		requestDeleter.SetTo(request);
1083 
1084 		if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request,
1085 				bufferSize) != 0) {
1086 			ERROR("Volume::_GetActivePackages(): failed to get active package "
1087 				"info from package FS: %s\n", strerror(errno));
1088 			RETURN_ERROR(errno);
1089 		}
1090 
1091 		if (request->bufferSize <= bufferSize)
1092 			break;
1093 
1094 		bufferSize = request->bufferSize;
1095 		requestDeleter.Unset();
1096 	}
1097 
1098 	INFORM("latest volume state:\n");
1099 	_DumpState(fLatestState);
1100 
1101 	// check whether that matches the expected state
1102 	if (_CheckActivePackagesMatchLatestState(request)) {
1103 		INFORM("The latest volume state is also the currently active one\n");
1104 		fActiveState = fLatestState;
1105 		return B_OK;
1106 	}
1107 
1108 	// There's a mismatch. We need a new state that reflects the actual
1109 	// activation situation.
1110 	VolumeState* state = new(std::nothrow) VolumeState;
1111 	if (state == NULL)
1112 		RETURN_ERROR(B_NO_MEMORY);
1113 	ObjectDeleter<VolumeState> stateDeleter(state);
1114 
1115 	for (uint32 i = 0; i < request->packageCount; i++) {
1116 		const PackageFSPackageInfo& info = request->infos[i];
1117 		NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID,
1118 			info.name);
1119 		Package* package;
1120 		status_t error = fPackageFileManager->CreatePackage(entryRef, package);
1121 		if (error != B_OK) {
1122 			WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %"
1123 				B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID,
1124 				info.directoryNodeID, info.name, strerror(error));
1125 			continue;
1126 		}
1127 
1128 		state->AddPackage(package);
1129 		state->SetPackageActive(package, true);
1130 	}
1131 
1132 	INFORM("currently active volume state:\n");
1133 	_DumpState(state);
1134 
1135 	fActiveState = stateDeleter.Detach();
1136 	return B_OK;
1137 }
1138 
1139 
1140 void
1141 Volume::_RunQueuedScripts()
1142 {
1143 	BDirectory adminDirectory;
1144 	status_t error = _OpenPackagesSubDirectory(
1145 		RelativePath(kAdminDirectoryName), false, adminDirectory);
1146 	if (error != B_OK)
1147 		return;
1148 
1149 	BDirectory scriptsDirectory;
1150 	error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName);
1151 	if (error != B_OK)
1152 		return;
1153 
1154 	// enumerate all the symlinks in the queued scripts directory
1155 	BEntry scriptEntry;
1156 	while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) {
1157 		BPath scriptPath;
1158 		scriptEntry.GetPath(&scriptPath);
1159 		error = scriptPath.InitCheck();
1160 		if (error != B_OK) {
1161 			INFORM("failed to get path of post-installation script \"%s\"\n",
1162 				strerror(error));
1163 			continue;
1164 		}
1165 
1166 		errno = 0;
1167 		int result = system(scriptPath.Path());
1168 		if (result != 0) {
1169 			INFORM("running post-installation script \"%s\" "
1170 				"failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno));
1171 		}
1172 
1173 		// remove the symlink, now that we've run the post-installation script
1174 		error = scriptEntry.Remove();
1175 		if (error != B_OK) {
1176 			INFORM("removing queued post-install script failed \"%s\"\n",
1177 				strerror(error));
1178 		}
1179 	}
1180 }
1181 
1182 
1183 bool
1184 Volume::_CheckActivePackagesMatchLatestState(
1185 	PackageFSGetPackageInfosRequest* request)
1186 {
1187 	if (fPackagesDirectoryCount != 1) {
1188 		INFORM("An old packages state (\"%s\") seems to be active.\n",
1189 			fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String());
1190 		return false;
1191 	}
1192 
1193 	const node_ref packagesDirRef(PackagesDirectoryRef());
1194 
1195 	// mark the returned packages active
1196 	for (uint32 i = 0; i < request->packageCount; i++) {
1197 		const PackageFSPackageInfo& info = request->infos[i];
1198 		if (node_ref(info.directoryDeviceID, info.directoryNodeID)
1199 				!= packagesDirRef) {
1200 			WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1201 				") not in packages directory\n", info.name,
1202 				info.packageDeviceID, info.packageNodeID);
1203 			return false;
1204 		}
1205 
1206 		Package* package = fLatestState->FindPackage(
1207 			node_ref(info.packageDeviceID, info.packageNodeID));
1208 		if (package == NULL || !package->IsActive()) {
1209 			WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO
1210 				") not %s\n", info.name,
1211 				info.packageDeviceID, info.packageNodeID,
1212 				package == NULL
1213 					? "found in packages directory" : "supposed to be active");
1214 			return false;
1215 		}
1216 	}
1217 
1218 	// Check whether there are packages that aren't active but should be.
1219 	uint32 count = 0;
1220 	for (PackageNodeRefHashTable::Iterator it
1221 			= fLatestState->ByNodeRefIterator(); it.HasNext();) {
1222 		Package* package = it.Next();
1223 		if (package->IsActive())
1224 			count++;
1225 	}
1226 
1227 	if (count != request->packageCount) {
1228 		INFORM("There seem to be packages in the packages directory that "
1229 			"should be active.\n");
1230 		return false;
1231 	}
1232 
1233 	return true;
1234 }
1235 
1236 
1237 void
1238 Volume::_SetLatestState(VolumeState* state, bool isActive)
1239 {
1240 	AutoLocker<BLocker> locker(fLock);
1241 	if (isActive) {
1242 		if (fLatestState != fActiveState)
1243 			delete fActiveState;
1244 		fActiveState = state;
1245 	}
1246 
1247 	if (fLatestState != fActiveState)
1248 		delete fLatestState;
1249 	fLatestState = state;
1250 	fChangeCount++;
1251 
1252 	locker.Unlock();
1253 
1254 	// Send a notification, if this is a system root volume.
1255 	if (fRoot->IsSystemRoot()) {
1256 		BMessage message(B_PACKAGE_UPDATE);
1257 		if (message.AddInt32("event",
1258 				(int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK
1259 			&& message.AddInt32("location", (int32)Location()) == B_OK
1260 			&& message.AddInt64("change count", fChangeCount) == B_OK) {
1261 			BRoster::Private().SendTo(&message, NULL, false);
1262 		}
1263 	}
1264 }
1265 
1266 
1267 void
1268 Volume::_DumpState(VolumeState* state)
1269 {
1270 	uint32 inactiveCount = 0;
1271 	for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1272 			it.HasNext();) {
1273 		Package* package = it.Next();
1274 		if (package->IsActive()) {
1275 			INFORM("active package: \"%s\"\n", package->FileName().String());
1276 		} else
1277 			inactiveCount++;
1278 	}
1279 
1280 	if (inactiveCount == 0)
1281 		return;
1282 
1283 	for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator();
1284 			it.HasNext();) {
1285 		Package* package = it.Next();
1286 		if (!package->IsActive())
1287 			INFORM("inactive package: \"%s\"\n", package->FileName().String());
1288 	}
1289 }
1290 
1291 
1292 status_t
1293 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository,
1294 	bool activeOnly, bool installed)
1295 {
1296 	status_t error = repository.SetTo(Path());
1297 	if (error != B_OK) {
1298 		ERROR("Volume::_AddRepository(): failed to init repository: %s\n",
1299 			strerror(error));
1300 		return error;
1301 	}
1302 
1303 	repository.SetInstalled(installed);
1304 
1305 	error = AddPackagesToRepository(repository, true);
1306 	if (error != B_OK) {
1307 		ERROR("Volume::_AddRepository(): failed to add packages to "
1308 			"repository: %s\n", strerror(error));
1309 		return error;
1310 	}
1311 
1312 	error = solver->AddRepository(&repository);
1313 	if (error != B_OK) {
1314 		ERROR("Volume::_AddRepository(): failed to add repository to solver: "
1315 			"%s\n", strerror(error));
1316 		return error;
1317 	}
1318 
1319 	return B_OK;
1320 }
1321 
1322 
1323 status_t
1324 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
1325 	BDirectory& _directory)
1326 {
1327 	// open the packages directory
1328 	BDirectory directory;
1329 	status_t error = directory.SetTo(&PackagesDirectoryRef());
1330 	if (error != B_OK) {
1331 		ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages "
1332 			"directory: %s\n", strerror(error));
1333 		RETURN_ERROR(error);
1334 	}
1335 
1336 	return FSUtils::OpenSubDirectory(directory, path, create, _directory);
1337 }
1338 
1339 
1340 void
1341 Volume::_CommitTransaction(BMessage* message,
1342 	const BActivationTransaction* transaction,
1343 	const PackageSet& packagesAlreadyAdded,
1344 	const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result)
1345 {
1346 	_result.Unset();
1347 
1348 	// perform the request
1349 	CommitTransactionHandler handler(this, fPackageFileManager, _result);
1350 	BTransactionError error = B_TRANSACTION_INTERNAL_ERROR;
1351 	try {
1352 		handler.Init(fLatestState, fLatestState == fActiveState,
1353 			packagesAlreadyAdded, packagesAlreadyRemoved);
1354 
1355 		if (message != NULL)
1356 			handler.HandleRequest(message);
1357 		else if (transaction != NULL)
1358 			handler.HandleRequest(*transaction);
1359 		else
1360 			handler.HandleRequest();
1361 
1362 		_SetLatestState(handler.DetachVolumeState(),
1363 			handler.IsActiveVolumeState());
1364 		error = B_TRANSACTION_OK;
1365 	} catch (Exception& exception) {
1366 		error = exception.Error();
1367 		exception.SetOnResult(_result);
1368 		if (_result.ErrorPackage().IsEmpty()
1369 			&& handler.CurrentPackage() != NULL) {
1370 			_result.SetErrorPackage(handler.CurrentPackage()->FileName());
1371 		}
1372 	} catch (std::bad_alloc& exception) {
1373 		error = B_TRANSACTION_NO_MEMORY;
1374 	}
1375 
1376 	_result.SetError(B_TRANSACTION_OK);
1377 
1378 	// revert on error
1379 	if (error != B_TRANSACTION_OK)
1380 		handler.Revert();
1381 }
1382