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