xref: /haiku/src/servers/package/Volume.cpp (revision 204dee708a999d5a71d0cb9497650ee7cef85d0a)
1 /*
2  * Copyright 2013, 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 <NodeMonitor.h>
23 #include <Path.h>
24 
25 #include <package/solver/Solver.h>
26 #include <package/solver/SolverPackage.h>
27 #include <package/solver/SolverProblem.h>
28 #include <package/solver/SolverProblemSolution.h>
29 #include <package/solver/SolverRepository.h>
30 #include <package/solver/SolverResult.h>
31 
32 #include <AutoDeleter.h>
33 #include <AutoLocker.h>
34 #include <package/DaemonDefs.h>
35 #include <package/PackagesDirectoryDefs.h>
36 
37 #include "DebugSupport.h"
38 
39 
40 using namespace BPackageKit::BPrivate;
41 
42 
43 static const char* const kPackageFileNameExtension = ".hpkg";
44 static const char* const kAdminDirectoryName
45 	= PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
46 static const char* const kActivationFileName
47 	= PACKAGES_DIRECTORY_ACTIVATION_FILE;
48 static const char* const kTemporaryActivationFileName
49 	= PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp";
50 
51 static bigtime_t kCommunicationTimeout = 1000000;
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 - RelativePath
92 
93 
94 struct Volume::RelativePath {
95 	RelativePath(const char* component1 = NULL, const char* component2 = NULL,
96 		const char* component3 = NULL)
97 		:
98 		fComponentCount(kMaxComponentCount)
99 	{
100 		fComponents[0] = component1;
101 		fComponents[1] = component2;
102 		fComponents[2] = component3;
103 
104 		for (size_t i = 0; i < kMaxComponentCount; i++) {
105 			if (fComponents[i] == NULL) {
106 				fComponentCount = i;
107 				break;
108 			}
109 		}
110 	}
111 
112 	bool IsEmpty() const
113 	{
114 		return fComponentCount == 0;
115 	}
116 
117 	RelativePath HeadPath(size_t componentsToDropCount = 1)
118 	{
119 		RelativePath result;
120 		if (componentsToDropCount < fComponentCount) {
121 			result.fComponentCount = fComponentCount - componentsToDropCount;
122 			for (size_t i = 0; i < result.fComponentCount; i++)
123 				result.fComponents[i] = fComponents[i];
124 		}
125 
126 		return result;
127 	}
128 
129 	const char* LastComponent() const
130 	{
131 		return fComponentCount > 0 ? fComponents[fComponentCount - 1] : NULL;
132 	}
133 
134 	BString ToString() const
135 	{
136 		if (fComponentCount == 0)
137 			return BString();
138 
139 		size_t length = fComponentCount - 1;
140 		for (size_t i = 0; i < fComponentCount; i++)
141 			length += strlen(fComponents[i]);
142 
143 		BString result;
144 		char* buffer = result.LockBuffer(length + 1);
145 		if (buffer == NULL)
146 			return BString();
147 
148 		for (size_t i = 0; i < fComponentCount; i++) {
149 			if (i > 0) {
150 				*buffer = '/';
151 				buffer++;
152 			}
153 			strcpy(buffer, fComponents[i]);
154 			buffer += strlen(buffer);
155 		}
156 
157 		return result.UnlockBuffer();
158 	}
159 
160 private:
161 	static const size_t	kMaxComponentCount = 3;
162 
163 	const char*	fComponents[kMaxComponentCount];
164 	size_t		fComponentCount;
165 };
166 
167 
168 // #pragma mark - Exception
169 
170 
171 struct Volume::Exception {
172 	Exception(int32 error, const char* errorMessage = NULL,
173 		const char* packageName = NULL)
174 		:
175 		fError(error),
176 		fErrorMessage(errorMessage),
177 		fPackageName(packageName)
178 	{
179 	}
180 
181 	int32 Error() const
182 	{
183 		return fError;
184 	}
185 
186 	const BString& ErrorMessage() const
187 	{
188 		return fErrorMessage;
189 	}
190 
191 	const BString& PackageName() const
192 	{
193 		return fPackageName;
194 	}
195 
196 	BString ToString() const
197 	{
198 		const char* error;
199 		if (fError >= 0) {
200 			switch (fError) {
201 				case B_DAEMON_OK:
202 					error = "no error";
203 					break;
204 				case B_DAEMON_CHANGE_COUNT_MISMATCH:
205 					error = "transaction out of date";
206 					break;
207 				case B_DAEMON_BAD_REQUEST:
208 					error = "invalid transaction";
209 					break;
210 				case B_DAEMON_NO_SUCH_PACKAGE:
211 					error = "no such package";
212 					break;
213 				case B_DAEMON_PACKAGE_ALREADY_EXISTS:
214 					error = "package already exists";
215 					break;
216 				default:
217 					error = "unknown error";
218 					break;
219 			}
220 		} else
221 			error = strerror(fError);
222 
223 		BString string;
224 		if (!fErrorMessage.IsEmpty()) {
225 			string = fErrorMessage;
226 			string << ": ";
227 		}
228 
229 		string << error;
230 
231 		if (!fPackageName.IsEmpty())
232 			string << ", package: \"" << fPackageName << '"';
233 
234 		return string;
235 	}
236 
237 private:
238 	int32		fError;
239 	BString		fErrorMessage;
240 	BString		fPackageName;
241 };
242 
243 
244 // #pragma mark - CommitTransactionHandler
245 
246 
247 struct Volume::CommitTransactionHandler {
248 	CommitTransactionHandler(Volume* volume, BMessage* request, BMessage& reply)
249 		:
250 		fVolume(volume),
251 		fRequest(request),
252 		fReply(reply),
253 		fPackagesToActivate(20, true),
254 		fPackagesToDeactivate(),
255 		fAddedPackages(),
256 		fRemovedPackages()
257 	{
258 	}
259 
260 	void HandleRequest()
261 	{
262 		// check the change count
263 		int64 changeCount;
264 		if (fRequest->FindInt64("change count", &changeCount) != B_OK)
265 			throw Exception(B_DAEMON_CHANGE_COUNT_MISMATCH);
266 
267 		// collect the packages to deactivate
268 		_GetPackagesToDeactivate();
269 
270 		// read the packages to activate
271 		_ReadPackagesToActivate();
272 
273 		// anything to do at all?
274 		if (fPackagesToActivate.IsEmpty() &&  fPackagesToDeactivate.empty()) {
275 			throw Exception(B_DAEMON_BAD_REQUEST,
276 				"no packages to activate or deactivate");
277 		}
278 
279 		// create an old state directory
280 		_CreateOldStateDirectory();
281 
282 		// move packages to deactivate to old state directory
283 		_RemovePackagesToDeactivate();
284 
285 		// move packages to activate to packages directory
286 		_AddPackagesToActivate();
287 
288 		// activate/deactivate packages
289 		fVolume->_ChangePackageActivation(fAddedPackages, fRemovedPackages);
290 
291 		// removed packages have been deleted, new packages shall not be deleted
292 		fAddedPackages.clear();
293 		fRemovedPackages.clear();
294 		fPackagesToActivate.MakeEmpty(false);
295 		fPackagesToDeactivate.clear();
296 	}
297 
298 	void Revert()
299 	{
300 		// move packages to activate back to transaction directory
301 		_RevertAddPackagesToActivate();
302 
303 		// move packages to deactivate back to packages directory
304 		_RevertRemovePackagesToDeactivate();
305 
306 		// remove old state directory
307 		_RemoveOldStateDirectory();
308 	}
309 
310 private:
311 	typedef BObjectList<Package> PackageList;
312 
313 	void _GetPackagesToDeactivate()
314 	{
315 		static const char* const kPackagesToDeactivateFieldName = "deactivate";
316 
317 		// get the number of packages to activate
318 		type_code type;
319 		int32 packagesToDeactivateCount;
320 		if (fRequest->GetInfo(kPackagesToDeactivateFieldName, &type,
321 				&packagesToDeactivateCount) != B_OK) {
322 			// the field is missing, i.e. no packages shall be deactivated
323 			return;
324 		}
325 
326 		for (int32 i = 0; i < packagesToDeactivateCount; i++) {
327 			const char* packageName;
328 			status_t error = fRequest->FindString(
329 				kPackagesToDeactivateFieldName, i, &packageName);
330 			if (error != B_OK)
331 				throw Exception(error);
332 
333 			Package* package = fVolume->fPackagesByFileName.Lookup(packageName);
334 			if (package == NULL) {
335 				throw Exception(B_DAEMON_NO_SUCH_PACKAGE, "no such package",
336 					packageName);
337 			}
338 
339 			fPackagesToDeactivate.insert(package);
340 
341 			package->IncrementEntryRemovedIgnoreLevel();
342 		}
343 	}
344 
345 	void _ReadPackagesToActivate()
346 	{
347 		static const char* const kPackagesToActivateFieldName = "activate";
348 
349 		// get the number of packages to activate
350 		type_code type;
351 		int32 packagesToActivateCount;
352 		if (fRequest->GetInfo(kPackagesToActivateFieldName, &type,
353 				&packagesToActivateCount) != B_OK) {
354 			// the field is missing, i.e. no packages shall be activated
355 			return;
356 		}
357 
358 		// get the name of the transaction directory
359 		BString transactionDirectoryName;
360 		if (packagesToActivateCount > 0) {
361 			if (fRequest->FindString("transaction", &transactionDirectoryName)
362 					!= B_OK) {
363 				throw Exception(B_DAEMON_BAD_REQUEST);
364 			}
365 		}
366 
367 		// check the name -- we only allow a simple subdirectory of the admin
368 		// directory
369 		if (transactionDirectoryName.IsEmpty()
370 			|| transactionDirectoryName.FindFirst('/') >= 0
371 			|| transactionDirectoryName == "."
372 			|| transactionDirectoryName == "..") {
373 			throw Exception(B_DAEMON_BAD_REQUEST);
374 		}
375 
376 		// open the directory
377 		RelativePath directoryPath(kAdminDirectoryName,
378 			transactionDirectoryName);
379 		BDirectory directory;
380 		status_t error = fVolume->_OpenPackagesSubDirectory(directoryPath,
381 			false, directory);
382 		if (error != B_OK)
383 			throw Exception(error, "failed to open transaction directory");
384 
385 		error = directory.GetNodeRef(&fTransactionDirectoryRef);
386 		if (error != B_OK) {
387 			throw Exception(error,
388 				"failed to get transaction directory node ref");
389 		}
390 
391 		// read the packages
392 		for (int32 i = 0; i < packagesToActivateCount; i++) {
393 			const char* packageName;
394 			error = fRequest->FindString(kPackagesToActivateFieldName, i,
395 				&packageName);
396 			if (error != B_OK)
397 				throw Exception(error);
398 
399 			// make sure it doesn't clash with an already existing package
400 			Package* package = fVolume->fPackagesByFileName.Lookup(packageName);
401 			if (package != NULL
402 				&& fPackagesToDeactivate.find(package)
403 					== fPackagesToDeactivate.end()) {
404 				throw Exception(B_DAEMON_PACKAGE_ALREADY_EXISTS, NULL,
405 					packageName);
406 			}
407 
408 			// read the package
409 			entry_ref entryRef;
410 			entryRef.device = fTransactionDirectoryRef.device;
411 			entryRef.directory = fTransactionDirectoryRef.node;
412 			if (entryRef.set_name(packageName) != B_OK)
413 				throw Exception(B_NO_MEMORY);
414 
415 			package = new(std::nothrow) Package;
416 			if (package == NULL || !fPackagesToActivate.AddItem(package)) {
417 				delete package;
418 				throw Exception(B_NO_MEMORY);
419 			}
420 
421 			error = package->Init(entryRef);
422 			if (error != B_OK)
423 				throw Exception(error, "failed to read package", packageName);
424 
425 			package->IncrementEntryCreatedIgnoreLevel();
426 		}
427 	}
428 
429 	void _CreateOldStateDirectory()
430 	{
431 		// construct a nice name from the current date and time
432 		time_t nowSeconds = time(NULL);
433 		struct tm now;
434 		BString baseName;
435 		if (localtime_r(&nowSeconds, &now) != NULL) {
436 			baseName.SetToFormat("state_%d-%02d-%02d_%02d:%02d:%02d",
437 				1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour,
438 				now.tm_min, now.tm_sec);
439 		} else
440 			baseName = "state";
441 
442 		if (baseName.IsEmpty())
443 			throw Exception(B_NO_MEMORY);
444 
445 		// make sure the directory doesn't exist yet
446 		BDirectory adminDirectory;
447 		status_t error = fVolume->_OpenPackagesSubDirectory(
448 			RelativePath(kAdminDirectoryName), true, adminDirectory);
449 		if (error != B_OK)
450 			throw Exception(error, "failed to open administrative directory");
451 
452 		int uniqueId = 1;
453 		BString directoryName = baseName;
454 		while (BEntry(&adminDirectory, directoryName).Exists()) {
455 			directoryName.SetToFormat("%s-%d", baseName.String(), uniqueId++);
456 			if (directoryName.IsEmpty())
457 				throw Exception(B_NO_MEMORY);
458 		}
459 
460 		// create the directory
461 		error = adminDirectory.CreateDirectory(directoryName,
462 			&fOldStateDirectory);
463 		if (error != B_OK)
464 			throw Exception(error, "failed to create old state directory");
465 
466 		fOldStateDirectoryName = directoryName;
467 
468 		// write the old activation file
469 		BEntry activationFile;
470 		error = fVolume->_WriteActivationFile(
471 			RelativePath(kAdminDirectoryName, directoryName),
472 			kActivationFileName, PackageSet(), PackageSet(), activationFile);
473 		if (error != B_OK)
474 			throw Exception(error, "failed to write old activation file");
475 
476 		// add the old state directory to the reply
477 		error = fReply.AddString("old state", fOldStateDirectoryName);
478 		if (error != B_OK)
479 			throw Exception(error, "failed to add field to reply");
480 	}
481 
482 	void _RemovePackagesToDeactivate()
483 	{
484 		if (fPackagesToDeactivate.empty())
485 			return;
486 
487 		for (PackageSet::const_iterator it = fPackagesToDeactivate.begin();
488 			it != fPackagesToDeactivate.end(); ++it) {
489 			// get an BEntry for the package
490 			Package* package = *it;
491 			entry_ref entryRef;
492 			entryRef.device = fVolume->fPackagesDirectoryRef.device;
493 			entryRef.directory = fVolume->fPackagesDirectoryRef.node;
494 			if (entryRef.set_name(package->FileName()) != B_OK)
495 				throw Exception(B_NO_MEMORY);
496 
497 			BEntry entry;
498 			status_t error = entry.SetTo(&entryRef);
499 			if (error != B_OK) {
500 				throw Exception(error, "failed to get package entry",
501 					package->FileName());
502 			}
503 
504 			// move entry
505 			fRemovedPackages.insert(package);
506 
507 			error = entry.MoveTo(&fOldStateDirectory);
508 			if (error != B_OK) {
509 				fRemovedPackages.erase(package);
510 				throw Exception(error,
511 					"failed to move old package from packages directory",
512 					package->FileName());
513 			}
514 		}
515 	}
516 
517 	void _AddPackagesToActivate()
518 	{
519 		if (fPackagesToActivate.IsEmpty())
520 			return;
521 
522 		// open packages directory
523 		BDirectory packagesDirectory;
524 		status_t error
525 			= packagesDirectory.SetTo(&fVolume->fPackagesDirectoryRef);
526 		if (error != B_OK)
527 			throw Exception(error, "failed to open packages directory");
528 
529 		int32 count = fPackagesToActivate.CountItems();
530 		for (int32 i = 0; i < count; i++) {
531 			// get an BEntry for the package
532 			Package* package = fPackagesToActivate.ItemAt(i);
533 			entry_ref entryRef;
534 			entryRef.device = fTransactionDirectoryRef.device;
535 			entryRef.directory = fTransactionDirectoryRef.node;
536 			if (entryRef.set_name(package->FileName()) != B_OK)
537 				throw Exception(B_NO_MEMORY);
538 
539 			BEntry entry;
540 			error = entry.SetTo(&entryRef);
541 			if (error != B_OK) {
542 				throw Exception(error, "failed to get package entry",
543 					package->FileName());
544 			}
545 
546 			// move entry
547 			fAddedPackages.insert(package);
548 
549 			error = entry.MoveTo(&packagesDirectory);
550 			if (error != B_OK) {
551 				fAddedPackages.erase(package);
552 				throw Exception(error,
553 					"failed to move new package to packages directory",
554 					package->FileName());
555 			}
556 
557 			// also add the package to the volume
558 			fVolume->_AddPackage(package);
559 		}
560 	}
561 
562 	void _RevertAddPackagesToActivate()
563 	{
564 		if (fAddedPackages.empty())
565 			return;
566 
567 		// open transaction directory
568 		BDirectory transactionDirectory;
569 		status_t error = transactionDirectory.SetTo(&fTransactionDirectoryRef);
570 		if (error != B_OK) {
571 			ERROR("failed to open transaction directory: %s\n",
572 				strerror(error));
573 		}
574 
575 		for (PackageSet::iterator it = fAddedPackages.begin();
576 			it != fAddedPackages.end(); ++it) {
577 			// remove package from the volume
578 			Package* package = *it;
579 			fVolume->_RemovePackage(package);
580 
581 			if (transactionDirectory.InitCheck() != B_OK)
582 				continue;
583 
584 			// get BEntry for the package
585 			entry_ref entryRef;
586 			entryRef.device = fVolume->fPackagesDirectoryRef.device;
587 			entryRef.directory = fVolume->fPackagesDirectoryRef.node;
588 			if (entryRef.set_name(package->FileName()) != B_OK) {
589 				ERROR("out of memory\n");
590 				continue;
591 			}
592 
593 			BEntry entry;
594 			error = entry.SetTo(&entryRef);
595 			if (error != B_OK) {
596 				ERROR("failed to get entry for package \"%s\": %s\n",
597 					package->FileName().String(), strerror(error));
598 				continue;
599 			}
600 
601 			// move entry
602 			error = entry.MoveTo(&transactionDirectory);
603 			if (error != B_OK) {
604 				ERROR("failed to move new package \"%s\" back to transaction "
605 					"directory: %s\n", package->FileName().String(),
606 					strerror(error));
607 				continue;
608 			}
609 		}
610 	}
611 
612 	void _RevertRemovePackagesToDeactivate()
613 	{
614 		if (fRemovedPackages.empty())
615 			return;
616 
617 		// open packages directory
618 		BDirectory packagesDirectory;
619 		status_t error
620 			= packagesDirectory.SetTo(&fVolume->fPackagesDirectoryRef);
621 		if (error != B_OK) {
622 			throw Exception(error, "failed to open packages directory");
623 			ERROR("failed to open packages directory: %s\n",
624 				strerror(error));
625 			return;
626 		}
627 
628 		for (PackageSet::iterator it = fRemovedPackages.begin();
629 			it != fRemovedPackages.end(); ++it) {
630 			// get an BEntry for the package
631 			Package* package = *it;
632 			BEntry entry;
633 			status_t error = entry.SetTo(&fOldStateDirectory,
634 				package->FileName());
635 			if (error != B_OK) {
636 				ERROR("failed to get entry for package \"%s\": %s\n",
637 					package->FileName().String(), strerror(error));
638 				continue;
639 			}
640 
641 			// move entry
642 			error = entry.MoveTo(&packagesDirectory);
643 			if (error != B_OK) {
644 				ERROR("failed to move old package \"%s\" back to packages "
645 					"directory: %s\n", package->FileName().String(),
646 					strerror(error));
647 				continue;
648 			}
649 		}
650 	}
651 
652 	void _RemoveOldStateDirectory()
653 	{
654 		if (fOldStateDirectory.InitCheck() != B_OK)
655 			return;
656 
657 		// remove the old activation file (it won't exist, if creating it
658 		// failed)
659 		BEntry(&fOldStateDirectory, kActivationFileName).Remove();
660 
661 		// Now the directory should be empty. If it isn't, it still contains
662 		// some old package file, which we failed to move back.
663 		BEntry entry;
664 		status_t error = fOldStateDirectory.GetEntry(&entry);
665 		if (error != B_OK) {
666 			ERROR("failed to get entry for old state directory: %s\n",
667 				strerror(error));
668 			return;
669 		}
670 
671 		error = entry.Remove();
672 		if (error != B_OK) {
673 			ERROR("failed to remove old state directory: %s\n",
674 				strerror(error));
675 			return;
676 		}
677 	}
678 
679 private:
680 	Volume*		fVolume;
681 	BMessage*	fRequest;
682 	BMessage&	fReply;
683 	PackageList	fPackagesToActivate;
684 	PackageSet	fPackagesToDeactivate;
685 	PackageSet	fAddedPackages;
686 	PackageSet	fRemovedPackages;
687 	BDirectory	fOldStateDirectory;
688 	BString		fOldStateDirectoryName;
689 	node_ref	fTransactionDirectoryRef;
690 };
691 
692 
693 // #pragma mark - Volume
694 
695 
696 Volume::Volume(BLooper* looper)
697 	:
698 	BHandler(),
699 	fPath(),
700 	fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM),
701 	fRootDirectoryRef(),
702 	fPackagesDirectoryRef(),
703 	fRoot(NULL),
704 	fListener(NULL),
705 	fPackagesByFileName(),
706 	fPackagesByNodeRef(),
707 	fPendingNodeMonitorEventsLock("pending node monitor events"),
708 	fPendingNodeMonitorEvents(),
709 	fPackagesToBeActivated(),
710 	fPackagesToBeDeactivated(),
711 	fChangeCount(0),
712 	fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY)
713 {
714 	looper->AddHandler(this);
715 }
716 
717 
718 Volume::~Volume()
719 {
720 	Unmounted();
721 		// need for error case in InitPackages()
722 
723 	fPackagesByFileName.Clear();
724 
725 	Package* package = fPackagesByNodeRef.Clear(true);
726 	while (package != NULL) {
727 		Package* next = package->NodeRefHashTableNext();
728 		delete package;
729 		package = next;
730 	}
731 }
732 
733 
734 status_t
735 Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef)
736 {
737 	if (fPackagesByFileName.Init() != B_OK || fPackagesByNodeRef.Init() != B_OK)
738 		RETURN_ERROR(B_NO_MEMORY);
739 
740 	fRootDirectoryRef = rootDirectoryRef;
741 
742 	// open the root directory
743 	BDirectory directory;
744 	status_t error = directory.SetTo(&fRootDirectoryRef);
745 	if (error != B_OK) {
746 		ERROR("Volume::Init(): failed to open root directory: %s\n",
747 			strerror(error));
748 		RETURN_ERROR(error);
749 	}
750 
751 	// get the directory path
752 	BEntry entry;
753 	error = directory.GetEntry(&entry);
754 
755 	BPath path;
756 	if (error == B_OK)
757 		error = entry.GetPath(&path);
758 
759 	if (error != B_OK) {
760 		ERROR("Volume::Init(): failed to get root directory path: %s\n",
761 			strerror(error));
762 		RETURN_ERROR(error);
763 	}
764 
765 	fPath = path.Path();
766 	if (fPath.IsEmpty())
767 		RETURN_ERROR(B_NO_MEMORY);
768 
769 	// get a volume info from the FS
770 	int fd = directory.Dup();
771 	if (fd < 0) {
772 		ERROR("Volume::Init(): failed to get root directory FD: %s\n",
773 			strerror(fd));
774 		RETURN_ERROR(fd);
775 	}
776 	FileDescriptorCloser fdCloser(fd);
777 
778 	PackageFSVolumeInfo info;
779 	if (ioctl(fd, PACKAGE_FS_OPERATION_GET_VOLUME_INFO, &info, sizeof(info))
780 			!= 0) {
781 		ERROR("Volume::Init(): failed to get volume info: %s\n",
782 			strerror(errno));
783 		RETURN_ERROR(errno);
784 	}
785 
786 	fMountType = info.mountType;
787 	fPackagesDirectoryRef.device = info.packagesDeviceID;
788 	fPackagesDirectoryRef.node = info.packagesDirectoryID;
789 
790 	_packageRootRef.device = info.rootDeviceID;
791 	_packageRootRef.node = info.rootDirectoryID;
792 
793 	return B_OK;
794 }
795 
796 
797 status_t
798 Volume::InitPackages(Listener* listener)
799 {
800 	// node-monitor the volume's packages directory
801 	status_t error = watch_node(&fPackagesDirectoryRef, B_WATCH_DIRECTORY,
802 		BMessenger(this));
803 	if (error == B_OK) {
804 		fListener = listener;
805 	} else {
806 		ERROR("Volume::InitPackages(): failed to start watching the packages "
807 			"directory of the volume at \"%s\": %s\n",
808 			fPath.String(), strerror(error));
809 		// Not good, but not fatal. Only the manual package operations in the
810 		// packages directory won't work correctly.
811 	}
812 
813 	// read the packages directory and get the active packages
814 	int fd = OpenRootDirectory();
815 	if (fd < 0) {
816 		ERROR("Volume::InitPackages(): failed to open root directory: %s\n",
817 			strerror(fd));
818 		RETURN_ERROR(fd);
819 	}
820 	FileDescriptorCloser fdCloser(fd);
821 
822 	error = _ReadPackagesDirectory();
823 	if (error != B_OK)
824 		RETURN_ERROR(error);
825 
826 	error = _GetActivePackages(fd);
827 	if (error != B_OK)
828 		RETURN_ERROR(error);
829 
830 	return B_OK;
831 }
832 
833 
834 status_t
835 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly)
836 {
837 	for (PackageFileNameHashTable::Iterator it
838 			= fPackagesByFileName.GetIterator(); it.HasNext();) {
839 		Package* package = it.Next();
840 		if (activeOnly && !package->IsActive())
841 			continue;
842 
843 		status_t error = repository.AddPackage(package->Info());
844 		if (error != B_OK) {
845 			ERROR("Volume::AddPackagesToRepository(): failed to add package %s "
846 				"to repository: %s\n", package->FileName().String(),
847 				strerror(error));
848 			return error;
849 		}
850 	}
851 
852 	return B_OK;
853 }
854 
855 
856 void
857 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume)
858 {
859 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume);
860 	// create the solver
861 	BSolver* solver;
862 	status_t error = BSolver::Create(solver);
863 	if (error != B_OK) {
864 		ERROR("Volume::InitialVerify(): failed to create solver: %s\n",
865 			strerror(error));
866 		return;
867 	}
868 	ObjectDeleter<BSolver> solverDeleter(solver);
869 
870 	// add a repository with all active packages
871 	BSolverRepository repository;
872 	error = _AddRepository(solver, repository, true, true);
873 	if (error != B_OK) {
874 		ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
875 			strerror(error));
876 		return;
877 	}
878 
879 	// add a repository for the next volume
880 	BSolverRepository nextRepository;
881 	if (nextVolume != NULL) {
882 		nextRepository.SetPriority(1);
883 		error = nextVolume->_AddRepository(solver, nextRepository, true, false);
884 		if (error != B_OK) {
885 			ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
886 				strerror(error));
887 			return;
888 		}
889 	}
890 
891 	// add a repository for the next next volume
892 	BSolverRepository nextNextRepository;
893 	if (nextNextVolume != NULL) {
894 		nextNextRepository.SetPriority(2);
895 		error = nextNextVolume->_AddRepository(solver, nextNextRepository, true,
896 			false);
897 		if (error != B_OK) {
898 			ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
899 				strerror(error));
900 			return;
901 		}
902 	}
903 
904 	// verify
905 	error = solver->VerifyInstallation();
906 	if (error != B_OK) {
907 		ERROR("Volume::InitialVerify(): failed to verify: %s\n",
908 			strerror(error));
909 		return;
910 	}
911 
912 	if (!solver->HasProblems()) {
913 		INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n",
914 			Path().String());
915 		return;
916 	}
917 
918 	// print the problems
919 // TODO: Notify the user ...
920 	INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n",
921 		Path().String());
922 
923 	int32 problemCount = solver->CountProblems();
924 	for (int32 i = 0; i < problemCount; i++) {
925 		BSolverProblem* problem = solver->ProblemAt(i);
926 		INFORM("  %" B_PRId32 ": %s\n", i + 1, problem->ToString().String());
927 		int32 solutionCount = problem->CountSolutions();
928 		for (int32 k = 0; k < solutionCount; k++) {
929 			const BSolverProblemSolution* solution = problem->SolutionAt(k);
930 			INFORM("    solution %" B_PRId32 ":\n", k + 1);
931 			int32 elementCount = solution->CountElements();
932 			for (int32 l = 0; l < elementCount; l++) {
933 				const BSolverProblemSolutionElement* element
934 					= solution->ElementAt(l);
935 				INFORM("      - %s\n", element->ToString().String());
936 			}
937 		}
938 	}
939 }
940 
941 
942 void
943 Volume::HandleGetLocationInfoRequest(BMessage* message)
944 {
945 	// If the cached reply message is up-to-date, just send it.
946 	int64 changeCount;
947 	if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK
948 		&& changeCount == fChangeCount) {
949 		message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
950 			kCommunicationTimeout);
951 		return;
952 	}
953 
954 	// rebuild the reply message
955 	fLocationInfoReply.MakeEmpty();
956 
957 	if (fLocationInfoReply.AddInt32("base directory device",
958 			fRootDirectoryRef.device) != B_OK
959 		|| fLocationInfoReply.AddInt64("base directory node",
960 			fRootDirectoryRef.node) != B_OK
961 		|| fLocationInfoReply.AddInt32("packages directory device",
962 			fPackagesDirectoryRef.device) != B_OK
963 		|| fLocationInfoReply.AddInt64("packages directory node",
964 			fPackagesDirectoryRef.node) != B_OK) {
965 		return;
966 	}
967 
968 	for (PackageFileNameHashTable::Iterator it
969 			= fPackagesByFileName.GetIterator(); it.HasNext();) {
970 		Package* package = it.Next();
971 		const char* fieldName = package->IsActive()
972 			? "active packages" : "inactive packages";
973 		BMessage packageArchive;
974 		if (package->Info().Archive(&packageArchive) != B_OK
975 			|| fLocationInfoReply.AddMessage(fieldName, &packageArchive)
976 				!= B_OK) {
977 			return;
978 		}
979 	}
980 
981 	if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK)
982 		return;
983 
984 	message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
985 		kCommunicationTimeout);
986 }
987 
988 
989 void
990 Volume::HandleCommitTransactionRequest(BMessage* message)
991 {
992 	// Prepare the reply in so far that we can at least set the error code
993 	// without risk of failure.
994 	BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY);
995 	if (reply.AddInt32("error", B_ERROR) != B_OK)
996 		return;
997 
998 	// perform the request
999 	CommitTransactionHandler handler(this, message, reply);
1000 	int32 error;
1001 	try {
1002 		handler.HandleRequest();
1003 		error = B_DAEMON_OK;
1004 	} catch (Exception& exception) {
1005 		error = exception.Error();
1006 
1007 		if (!exception.ErrorMessage().IsEmpty())
1008 			reply.AddString("error message", exception.ErrorMessage());
1009 		if (!exception.PackageName().IsEmpty())
1010 			reply.AddString("error package", exception.PackageName());
1011 	} catch (std::bad_alloc& exception) {
1012 		error = B_NO_MEMORY;
1013 	}
1014 
1015 	// revert on error
1016 	if (error != B_DAEMON_OK)
1017 		handler.Revert();
1018 
1019 	// send the reply
1020 	reply.ReplaceInt32("error", error);
1021 	message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout);
1022 }
1023 
1024 
1025 void
1026 Volume::Unmounted()
1027 {
1028 	if (fListener != NULL) {
1029 		stop_watching(BMessenger(this));
1030 		fListener = NULL;
1031 	}
1032 
1033 	if (BLooper* looper = Looper())
1034 		looper->RemoveHandler(this);
1035 }
1036 
1037 
1038 void
1039 Volume::MessageReceived(BMessage* message)
1040 {
1041 	switch (message->what) {
1042 		case B_NODE_MONITOR:
1043 		{
1044 			int32 opcode;
1045 			if (message->FindInt32("opcode", &opcode) != B_OK)
1046 				break;
1047 
1048 			switch (opcode) {
1049 				case B_ENTRY_CREATED:
1050 					_HandleEntryCreatedOrRemoved(message, true);
1051 					break;
1052 				case B_ENTRY_REMOVED:
1053 					_HandleEntryCreatedOrRemoved(message, false);
1054 					break;
1055 				case B_ENTRY_MOVED:
1056 					_HandleEntryMoved(message);
1057 					break;
1058 				default:
1059 					break;
1060 			}
1061 			break;
1062 		}
1063 
1064 		default:
1065 			BHandler::MessageReceived(message);
1066 			break;
1067 	}
1068 }
1069 
1070 
1071 int
1072 Volume::OpenRootDirectory() const
1073 {
1074 	BDirectory directory;
1075 	status_t error = directory.SetTo(&fRootDirectoryRef);
1076 	if (error != B_OK) {
1077 		ERROR("Volume::OpenRootDirectory(): failed to open root directory: "
1078 			"%s\n", strerror(error));
1079 		RETURN_ERROR(error);
1080 	}
1081 
1082 	return directory.Dup();
1083 }
1084 
1085 
1086 void
1087 Volume::ProcessPendingNodeMonitorEvents()
1088 {
1089 	// get the events
1090 	NodeMonitorEventList events;
1091 	{
1092 		AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
1093 		events.MoveFrom(&fPendingNodeMonitorEvents);
1094 	}
1095 
1096 	// process them
1097 	while (NodeMonitorEvent* event = events.RemoveHead()) {
1098 		ObjectDeleter<NodeMonitorEvent> eventDeleter(event);
1099 		if (event->WasCreated())
1100 			_PackagesEntryCreated(event->EntryName());
1101 		else
1102 			_PackagesEntryRemoved(event->EntryName());
1103 	}
1104 }
1105 
1106 
1107 bool
1108 Volume::HasPendingPackageActivationChanges() const
1109 {
1110 	return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty();
1111 }
1112 
1113 
1114 void
1115 Volume::ProcessPendingPackageActivationChanges()
1116 {
1117 	if (!HasPendingPackageActivationChanges())
1118 		return;
1119 
1120 	try {
1121 		_ChangePackageActivation(fPackagesToBeActivated,
1122 			fPackagesToBeDeactivated);
1123 	} catch (Exception& exception) {
1124 		ERROR("Volume::ProcessPendingPackageActivationChanges(): package "
1125 			"activation failed: %s\n", exception.ToString().String());
1126 // TODO: Notify the user!
1127 	}
1128 
1129 	// clear the activation/deactivation sets in any event
1130 	fPackagesToBeActivated.clear();
1131 	fPackagesToBeDeactivated.clear();
1132 }
1133 
1134 
1135 void
1136 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created)
1137 {
1138 	// only moves to or from our packages directory are interesting
1139 	int32 deviceID;
1140 	int64 directoryID;
1141 	const char* name;
1142 	if (message->FindInt32("device", &deviceID) != B_OK
1143 		|| message->FindInt64("directory", &directoryID) != B_OK
1144 		|| message->FindString("name", &name) != B_OK
1145 		|| node_ref(deviceID, directoryID) != fPackagesDirectoryRef) {
1146 		return;
1147 	}
1148 
1149 	_QueueNodeMonitorEvent(name, created);
1150 }
1151 
1152 
1153 void
1154 Volume::_HandleEntryMoved(const BMessage* message)
1155 {
1156 	int32 deviceID;
1157 	int64 fromDirectoryID;
1158 	int64 toDirectoryID;
1159 	const char* fromName;
1160 	const char* toName;
1161 	if (message->FindInt32("device", &deviceID) != B_OK
1162 		|| message->FindInt64("from directory", &fromDirectoryID) != B_OK
1163 		|| message->FindInt64("to directory", &toDirectoryID) != B_OK
1164 		|| message->FindString("from name", &fromName) != B_OK
1165 		|| message->FindString("name", &toName) != B_OK
1166 		|| deviceID != fPackagesDirectoryRef.device
1167 		|| (fromDirectoryID != fPackagesDirectoryRef.node
1168 			&& toDirectoryID != fPackagesDirectoryRef.node)) {
1169 		return;
1170 	}
1171 
1172 	AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
1173 		// make sure for a move the two events cannot get split
1174 
1175 	if (fromDirectoryID == fPackagesDirectoryRef.node)
1176 		_QueueNodeMonitorEvent(fromName, false);
1177 	if (toDirectoryID == fPackagesDirectoryRef.node)
1178 		_QueueNodeMonitorEvent(toName, true);
1179 }
1180 
1181 
1182 void
1183 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated)
1184 {
1185 	if (name.IsEmpty()) {
1186 		ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n");
1187 		return;
1188 	}
1189 
1190 	// ignore entries that don't have the ".hpkg" extension
1191 	if (!name.EndsWith(kPackageFileNameExtension))
1192 		return;
1193 
1194 	NodeMonitorEvent* event
1195 		= new(std::nothrow) NodeMonitorEvent(name, wasCreated);
1196 	if (event == NULL) {
1197 		ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n");
1198 		return;
1199 	}
1200 
1201 	AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
1202 	bool firstEvent = fPendingNodeMonitorEvents.IsEmpty();
1203 	fPendingNodeMonitorEvents.Add(event);
1204 	eventsLock.Unlock();
1205 
1206 	if (firstEvent && fListener != NULL)
1207 		fListener->VolumeNodeMonitorEventOccurred(this);
1208 }
1209 
1210 
1211 void
1212 Volume::_PackagesEntryCreated(const char* name)
1213 {
1214 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name);
1215 	// Ignore the event, if the package is already known.
1216 	Package* package = fPackagesByFileName.Lookup(name);
1217 	if (package != NULL) {
1218 		if (package->EntryCreatedIgnoreLevel() > 0) {
1219 			package->DecrementEntryCreatedIgnoreLevel();
1220 		} else {
1221 			WARN("node monitoring created event for already known entry "
1222 				"\"%s\"\n", name);
1223 		}
1224 
1225 		return;
1226 	}
1227 
1228 	entry_ref entry;
1229 	entry.device = fPackagesDirectoryRef.device;
1230 	entry.directory = fPackagesDirectoryRef.node;
1231 	status_t error = entry.set_name(name);
1232 	if (error != B_OK) {
1233 		ERROR("out of memory\n");
1234 		return;
1235 	}
1236 
1237 	package = new(std::nothrow) Package;
1238 	if (package == NULL) {
1239 		ERROR("out of memory\n");
1240 		return;
1241 	}
1242 	ObjectDeleter<Package> packageDeleter(package);
1243 
1244 	error = package->Init(entry);
1245 	if (error != B_OK) {
1246 		ERROR("failed to init package for file \"%s\"\n", name);
1247 		return;
1248 	}
1249 
1250 	_AddPackage(package);
1251 	packageDeleter.Detach();
1252 
1253 	try {
1254 		fPackagesToBeActivated.insert(package);
1255 	} catch (std::bad_alloc& exception) {
1256 		ERROR("out of memory\n");
1257 		return;
1258 	}
1259 }
1260 
1261 
1262 void
1263 Volume::_PackagesEntryRemoved(const char* name)
1264 {
1265 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name);
1266 	Package* package = fPackagesByFileName.Lookup(name);
1267 	if (package == NULL)
1268 		return;
1269 
1270 	// Ignore the event, if we generated it ourselves.
1271 	if (package->EntryRemovedIgnoreLevel() > 0) {
1272 		package->DecrementEntryRemovedIgnoreLevel();
1273 		return;
1274 	}
1275 
1276 	// Remove the package from the packages-to-be-activated set, if it is in
1277 	// there (unlikely, unless we see a create-remove-create sequence).
1278 	PackageSet::iterator it = fPackagesToBeActivated.find(package);
1279 	if (it != fPackagesToBeActivated.end())
1280 		fPackagesToBeActivated.erase(it);
1281 
1282 	// If the package isn't active, just remove it for good.
1283 	if (!package->IsActive()) {
1284 		_RemovePackage(package);
1285 		delete package;
1286 		return;
1287 	}
1288 
1289 	// The package must be deactivated.
1290 	try {
1291 		fPackagesToBeDeactivated.insert(package);
1292 	} catch (std::bad_alloc& exception) {
1293 		ERROR("out of memory\n");
1294 		return;
1295 	}
1296 }
1297 
1298 
1299 void
1300 Volume::_FillInActivationChangeItem(PackageFSActivationChangeItem* item,
1301 	PackageFSActivationChangeType type, Package* package, char*& nameBuffer)
1302 {
1303 	item->type = type;
1304 	item->packageDeviceID = package->NodeRef().device;
1305 	item->packageNodeID = package->NodeRef().node;
1306 	item->nameLength = package->FileName().Length();
1307 	item->parentDeviceID = fPackagesDirectoryRef.device;
1308 	item->parentDirectoryID = fPackagesDirectoryRef.node;
1309 	item->name = nameBuffer;
1310 	strcpy(nameBuffer, package->FileName());
1311 	nameBuffer += package->FileName().Length() + 1;
1312 }
1313 
1314 
1315 void
1316 Volume::_AddPackage(Package* package)
1317 {
1318 	fPackagesByFileName.Insert(package);
1319 	fPackagesByNodeRef.Insert(package);
1320 }
1321 
1322 void
1323 Volume::_RemovePackage(Package* package)
1324 {
1325 	fPackagesByFileName.Remove(package);
1326 	fPackagesByNodeRef.Remove(package);
1327 	fChangeCount++;
1328 }
1329 
1330 
1331 status_t
1332 Volume::_ReadPackagesDirectory()
1333 {
1334 	BDirectory directory;
1335 	status_t error = directory.SetTo(&fPackagesDirectoryRef);
1336 	if (error != B_OK) {
1337 		ERROR("Volume::_ReadPackagesDirectory(): failed to open packages "
1338 			"directory: %s\n", strerror(error));
1339 		RETURN_ERROR(error);
1340 	}
1341 
1342 	entry_ref entry;
1343 	while (directory.GetNextRef(&entry) == B_OK) {
1344 		if (!BString(entry.name).EndsWith(kPackageFileNameExtension))
1345 			continue;
1346 
1347 		Package* package = new(std::nothrow) Package;
1348 		if (package == NULL)
1349 			RETURN_ERROR(B_NO_MEMORY);
1350 		ObjectDeleter<Package> packageDeleter(package);
1351 
1352 		status_t error = package->Init(entry);
1353 		if (error == B_OK) {
1354 			_AddPackage(package);
1355 			packageDeleter.Detach();
1356 		}
1357 	}
1358 
1359 	return B_OK;
1360 }
1361 
1362 
1363 status_t
1364 Volume::_GetActivePackages(int fd)
1365 {
1366 	uint32 maxPackageCount = 16 * 1024;
1367 	PackageFSGetPackageInfosRequest* request = NULL;
1368 	MemoryDeleter requestDeleter;
1369 	size_t bufferSize;
1370 	for (;;) {
1371 		bufferSize = sizeof(PackageFSGetPackageInfosRequest)
1372 			+ (maxPackageCount - 1) * sizeof(PackageFSPackageInfo);
1373 		request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize);
1374 		if (request == NULL)
1375 			RETURN_ERROR(B_NO_MEMORY);
1376 		requestDeleter.SetTo(request);
1377 
1378 		if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request,
1379 				bufferSize) != 0) {
1380 			ERROR("Volume::_GetActivePackages(): failed to get active package "
1381 				"info from package FS: %s\n", strerror(errno));
1382 			RETURN_ERROR(errno);
1383 		}
1384 
1385 		if (request->packageCount <= maxPackageCount)
1386 			break;
1387 
1388 		maxPackageCount = request->packageCount;
1389 		requestDeleter.Unset();
1390 	}
1391 
1392 	// mark the returned packages active
1393 	for (uint32 i = 0; i < request->packageCount; i++) {
1394 		Package* package = fPackagesByNodeRef.Lookup(
1395 			node_ref(request->infos[i].packageDeviceID,
1396 				request->infos[i].packageNodeID));
1397 		if (package == NULL) {
1398 			WARN("active package (dev: %" B_PRIdDEV ", node: %" B_PRIdINO ") "
1399 				"not found in package directory\n",
1400 				request->infos[i].packageDeviceID,
1401 				request->infos[i].packageNodeID);
1402 // TODO: Deactivate the package right away?
1403 			continue;
1404 		}
1405 
1406 		package->SetActive(true);
1407 INFORM("active package: \"%s\"\n", package->FileName().String());
1408 	}
1409 
1410 for (PackageNodeRefHashTable::Iterator it = fPackagesByNodeRef.GetIterator();
1411 	it.HasNext();) {
1412 	Package* package = it.Next();
1413 	if (!package->IsActive())
1414 		INFORM("inactive package: \"%s\"\n", package->FileName().String());
1415 }
1416 
1417 			PackageNodeRefHashTable fPackagesByNodeRef;
1418 // INFORM("%" B_PRIu32 " active packages:\n", request->packageCount);
1419 // for (uint32 i = 0; i < request->packageCount; i++) {
1420 // 	INFORM("  dev: %" B_PRIdDEV ", node: %" B_PRIdINO "\n",
1421 // 		request->infos[i].packageDeviceID, request->infos[i].packageNodeID);
1422 // }
1423 
1424 	return B_OK;
1425 }
1426 
1427 
1428 status_t
1429 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository,
1430 	bool activeOnly, bool installed)
1431 {
1432 	status_t error = repository.SetTo(Path());
1433 	if (error != B_OK) {
1434 		ERROR("Volume::_AddRepository(): failed to init repository: %s\n",
1435 			strerror(error));
1436 		return error;
1437 	}
1438 
1439 	repository.SetInstalled(installed);
1440 
1441 	error = AddPackagesToRepository(repository, true);
1442 	if (error != B_OK) {
1443 		ERROR("Volume::_AddRepository(): failed to add packages to "
1444 			"repository: %s\n", strerror(error));
1445 		return error;
1446 	}
1447 
1448 	error = solver->AddRepository(&repository);
1449 	if (error != B_OK) {
1450 		ERROR("Volume::_AddRepository(): failed to add repository to solver: "
1451 			"%s\n", strerror(error));
1452 		return error;
1453 	}
1454 
1455 	return B_OK;
1456 }
1457 
1458 
1459 status_t
1460 Volume::_OpenPackagesFile(const RelativePath& subDirectoryPath,
1461 	const char* fileName, uint32 openMode, BFile& _file, BEntry* _entry)
1462 {
1463 	BDirectory directory;
1464 	if (!subDirectoryPath.IsEmpty()) {
1465 		status_t error = _OpenPackagesSubDirectory(subDirectoryPath,
1466 			(openMode & B_CREATE_FILE) != 0, directory);
1467 		if (error != B_OK) {
1468 			ERROR("Volume::_OpenPackagesFile(): failed to open packages "
1469 				"subdirectory \"%s\": %s\n",
1470 				subDirectoryPath.ToString().String(), strerror(error));
1471 			RETURN_ERROR(error);
1472 		}
1473 	} else {
1474 		status_t error = directory.SetTo(&fPackagesDirectoryRef);
1475 		if (error != B_OK) {
1476 			ERROR("Volume::_OpenPackagesFile(): failed to open packages "
1477 				"directory: %s\n", strerror(error));
1478 			RETURN_ERROR(error);
1479 		}
1480 	}
1481 
1482 	BEntry stackEntry;
1483 	BEntry& entry = _entry != NULL ? *_entry : stackEntry;
1484 	status_t error = entry.SetTo(&directory, fileName);
1485 	if (error != B_OK) {
1486 		ERROR("Volume::_OpenPackagesFile(): failed to get entry for file: %s",
1487 			strerror(error));
1488 		RETURN_ERROR(error);
1489 	}
1490 
1491 	return _file.SetTo(&entry, openMode);
1492 }
1493 
1494 
1495 status_t
1496 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
1497 	BDirectory& _directory)
1498 {
1499 	// open the packages directory
1500 	BDirectory directory;
1501 	status_t error = directory.SetTo(&fPackagesDirectoryRef);
1502 	if (error != B_OK) {
1503 		ERROR("Volume::_OpenConfigSubDirectory(): failed to open packages "
1504 			"directory: %s\n", strerror(error));
1505 		RETURN_ERROR(error);
1506 	}
1507 
1508 	// get a string for the path
1509 	BString pathString = path.ToString();
1510 	if (pathString.IsEmpty())
1511 		RETURN_ERROR(B_NO_MEMORY);
1512 
1513 	// If creating is not allowed, just try to open it.
1514 	if (!create)
1515 		RETURN_ERROR(_directory.SetTo(&directory, pathString));
1516 
1517 	// get an absolute path and create the subdirectory
1518 	BPath absolutePath;
1519 	error = absolutePath.SetTo(&directory, pathString);
1520 	if (error != B_OK) {
1521 		ERROR("Volume::_OpenConfigSubDirectory(): failed to get absolute path "
1522 			"for subdirectory \"%s\": %s\n", pathString.String(),
1523 			strerror(error));
1524 		RETURN_ERROR(error);
1525 	}
1526 
1527 	error = create_directory(absolutePath.Path(),
1528 		S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
1529 	if (error != B_OK) {
1530 		ERROR("Volume::_OpenConfigSubDirectory(): failed to create packages "
1531 			"subdirectory \"%s\": %s\n", pathString.String(),
1532 			strerror(error));
1533 		RETURN_ERROR(error);
1534 	}
1535 
1536 	RETURN_ERROR(_directory.SetTo(&directory, pathString));
1537 }
1538 
1539 
1540 status_t
1541 Volume::_CreateActivationFileContent(const PackageSet& toActivate,
1542 	const PackageSet& toDeactivate, BString& _content)
1543 {
1544 	BString activationFileContent;
1545 	for (PackageFileNameHashTable::Iterator it
1546 			= fPackagesByFileName.GetIterator(); it.HasNext();) {
1547 		Package* package = it.Next();
1548 		if (package->IsActive()
1549 			&& toDeactivate.find(package) == toDeactivate.end()) {
1550 			int32 length = activationFileContent.Length();
1551 			activationFileContent << package->FileName() << '\n';
1552 			if (activationFileContent.Length()
1553 					< length + package->FileName().Length() + 1) {
1554 				return B_NO_MEMORY;
1555 			}
1556 		}
1557 	}
1558 
1559 	for (PackageSet::const_iterator it = toActivate.begin();
1560 		it != toActivate.end(); ++it) {
1561 		Package* package = *it;
1562 		int32 length = activationFileContent.Length();
1563 		activationFileContent << package->FileName() << '\n';
1564 		if (activationFileContent.Length()
1565 				< length + package->FileName().Length() + 1) {
1566 			return B_NO_MEMORY;
1567 		}
1568 	}
1569 
1570 	_content = activationFileContent;
1571 	return B_OK;
1572 }
1573 
1574 
1575 status_t
1576 Volume::_WriteActivationFile(const RelativePath& directoryPath,
1577 	const char* fileName, const PackageSet& toActivate,
1578 	const PackageSet& toDeactivate,
1579 	BEntry& _entry)
1580 {
1581 	// create the content
1582 	BString activationFileContent;
1583 	status_t error = _CreateActivationFileContent(toActivate, toDeactivate,
1584 		activationFileContent);
1585 	if (error != B_OK)
1586 		return error;
1587 
1588 	// write the file
1589 	error = _WriteTextFile(directoryPath, fileName, activationFileContent,
1590 		_entry);
1591 	if (error != B_OK) {
1592 		ERROR("Volume::_WriteActivationFile(): failed to write activation "
1593 			"file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName,
1594 			strerror(error));
1595 		return error;
1596 	}
1597 
1598 	return B_OK;
1599 }
1600 
1601 
1602 status_t
1603 Volume::_WriteTextFile(const RelativePath& directoryPath, const char* fileName,
1604 	const BString& content, BEntry& _entry)
1605 {
1606 	BFile file;
1607 	status_t error = _OpenPackagesFile(directoryPath,
1608 		fileName, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE, file, &_entry);
1609 	if (error != B_OK) {
1610 		ERROR("Volume::_WriteTextFile(): failed to create file \"%s/%s\": %s\n",
1611 			directoryPath.ToString().String(), fileName, strerror(error));
1612 		return error;
1613 	}
1614 
1615 	ssize_t bytesWritten = file.Write(content.String(),
1616 		content.Length());
1617 	if (bytesWritten < 0) {
1618 		ERROR("Volume::_WriteTextFile(): failed to write file \"%s/%s\": %s\n",
1619 			directoryPath.ToString().String(), fileName,
1620 			strerror(bytesWritten));
1621 		return bytesWritten;
1622 	}
1623 
1624 	return B_OK;
1625 }
1626 
1627 
1628 void
1629 Volume::_ChangePackageActivation(const PackageSet& packagesToActivate,
1630 	const PackageSet& packagesToDeactivate)
1631 {
1632 INFORM("Volume::_ChangePackageActivation(): activating %zu, deactivating %zu packages\n",
1633 packagesToActivate.size(), packagesToDeactivate.size());
1634 
1635 	// write the temporary package activation file
1636 	BEntry activationFileEntry;
1637 	status_t error = _WriteActivationFile(RelativePath(kAdminDirectoryName),
1638 		kTemporaryActivationFileName, packagesToActivate, packagesToDeactivate,
1639 		activationFileEntry);
1640 	if (error != B_OK)
1641 		throw Exception(error, "failed to write activation file");
1642 
1643 	// compute the size of the allocation we need for the activation change
1644 	// request
1645 	int32 itemCount = packagesToActivate.size() + packagesToDeactivate.size();
1646 	size_t requestSize = sizeof(PackageFSActivationChangeRequest)
1647 		+ itemCount * sizeof(PackageFSActivationChangeItem);
1648 
1649 	for (PackageSet::iterator it = packagesToActivate.begin();
1650 		 it != packagesToActivate.end(); ++it) {
1651 		requestSize += (*it)->FileName().Length() + 1;
1652 	}
1653 
1654 	for (PackageSet::iterator it = packagesToDeactivate.begin();
1655 		 it != packagesToDeactivate.end(); ++it) {
1656 		requestSize += (*it)->FileName().Length() + 1;
1657 	}
1658 
1659 	// allocate and prepare the request
1660 	PackageFSActivationChangeRequest* request
1661 		= (PackageFSActivationChangeRequest*)malloc(requestSize);
1662 	if (request == NULL)
1663 		throw Exception(B_NO_MEMORY);
1664 	MemoryDeleter requestDeleter(request);
1665 
1666 	request->itemCount = itemCount;
1667 
1668 	PackageFSActivationChangeItem* item = &request->items[0];
1669 	char* nameBuffer = (char*)(item + itemCount);
1670 
1671 	for (PackageSet::iterator it = packagesToActivate.begin();
1672 		it != packagesToActivate.end(); ++it, item++) {
1673 		_FillInActivationChangeItem(item, PACKAGE_FS_ACTIVATE_PACKAGE, *it,
1674 			nameBuffer);
1675 	}
1676 
1677 	for (PackageSet::iterator it = packagesToDeactivate.begin();
1678 		it != packagesToDeactivate.end(); ++it, item++) {
1679 		_FillInActivationChangeItem(item, PACKAGE_FS_DEACTIVATE_PACKAGE, *it,
1680 			nameBuffer);
1681 	}
1682 
1683 	// issue the request
1684 	int fd = OpenRootDirectory();
1685 	if (fd < 0)
1686 		throw Exception(fd, "failed to open root directory");
1687 	FileDescriptorCloser fdCloser(fd);
1688 
1689 	if (ioctl(fd, PACKAGE_FS_OPERATION_CHANGE_ACTIVATION, request, requestSize)
1690 			!= 0) {
1691 // TODO: We need more error information and error handling!
1692 		throw Exception(errno, "ioctl() to de-/activate packages failed");
1693 	}
1694 
1695 	// rename the temporary activation file to the final file
1696 	error = activationFileEntry.Rename(kActivationFileName, true);
1697 	if (error != B_OK) {
1698 		throw Exception(error,
1699 			"failed to rename temporary activation file to final file");
1700 // TODO: We should probably try to reverse the activation changes, though that
1701 // will fail, if this method has been called in response to node monitoring
1702 // events. Alternatively moving the package activation file could be made part
1703 // of the ioctl(), since packagefs should be able to undo package changes until
1704 // the very end, unless running out of memory. In the end the situation would be
1705 // bad anyway, though, since the activation file may refer to removed packages
1706 // and things would be in an inconsistent state after rebooting.
1707 	}
1708 
1709 	// Update our state, i.e. remove deactivated packages and mark activated
1710 	// packages accordingly.
1711 	for (PackageSet::iterator it = packagesToActivate.begin();
1712 		it != packagesToActivate.end(); ++it) {
1713 		(*it)->SetActive(true);
1714 		fChangeCount++;
1715 	}
1716 
1717 	for (PackageSet::iterator it = fPackagesToBeDeactivated.begin();
1718 		it != fPackagesToBeDeactivated.end(); ++it) {
1719 		Package* package = *it;
1720 		_RemovePackage(package);
1721 		delete package;
1722 	}
1723 }
1724