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