xref: /haiku/src/servers/package/Volume.cpp (revision 5e96d7d537fbec23bad4ae9b4c8e7b02e769f0c6)
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 <grp.h>
14 #include <pwd.h>
15 #include <stdlib.h>
16 #include <sys/stat.h>
17 #include <time.h>
18 #include <unistd.h>
19 
20 #include <string>
21 
22 #include <Directory.h>
23 #include <Entry.h>
24 #include <File.h>
25 #include <Looper.h>
26 #include <MessageRunner.h>
27 #include <NodeMonitor.h>
28 #include <Path.h>
29 
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 <CopyEngine.h>
40 #include <NotOwningEntryRef.h>
41 #include <package/DaemonDefs.h>
42 #include <package/PackagesDirectoryDefs.h>
43 #include <RemoveEngine.h>
44 
45 #include "DebugSupport.h"
46 #include "Exception.h"
47 #include "FSTransaction.h"
48 
49 
50 using namespace BPackageKit::BPrivate;
51 
52 typedef std::set<std::string> StringSet;
53 
54 
55 static const char* const kPackageFileNameExtension = ".hpkg";
56 static const char* const kAdminDirectoryName
57 	= PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
58 static const char* const kActivationFileName
59 	= PACKAGES_DIRECTORY_ACTIVATION_FILE;
60 static const char* const kTemporaryActivationFileName
61 	= PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp";
62 static const char* const kWritableFilesDirectoryName = "writable-files";
63 static const char* const kPackageFileAttribute = "SYS:PACKAGE";
64 
65 static const bigtime_t kHandleNodeMonitorEvents = 'nmon';
66 
67 static const bigtime_t kNodeMonitorEventHandlingDelay = 500000;
68 static const bigtime_t kCommunicationTimeout = 1000000;
69 
70 
71 // #pragma mark - Listener
72 
73 
74 Volume::Listener::~Listener()
75 {
76 }
77 
78 
79 // #pragma mark - NodeMonitorEvent
80 
81 
82 struct Volume::NodeMonitorEvent
83 	: public DoublyLinkedListLinkImpl<NodeMonitorEvent> {
84 public:
85 	NodeMonitorEvent(const BString& entryName, bool created)
86 		:
87 		fEntryName(entryName),
88 		fCreated(created)
89 	{
90 	}
91 
92 	const BString& EntryName() const
93 	{
94 		return fEntryName;
95 	}
96 
97 	bool WasCreated() const
98 	{
99 		return fCreated;
100 	}
101 
102 private:
103 	BString	fEntryName;
104 	bool	fCreated;
105 };
106 
107 
108 // #pragma mark - State
109 
110 
111 struct Volume::State {
112 
113 	State()
114 		:
115 		fLock("volume state"),
116 		fPackagesByFileName(),
117 		fPackagesByNodeRef(),
118 		fChangeCount(0),
119 		fPendingPackageJobCount(0)
120 	{
121 	}
122 
123 	~State()
124 	{
125 		fPackagesByFileName.Clear();
126 
127 		Package* package = fPackagesByNodeRef.Clear(true);
128 		while (package != NULL) {
129 			Package* next = package->NodeRefHashTableNext();
130 			delete package;
131 			package = next;
132 		}
133 	}
134 
135 	bool Init()
136 	{
137 		return fLock.InitCheck() == B_OK && fPackagesByFileName.Init() == B_OK
138 			&& fPackagesByNodeRef.Init() == B_OK;
139 	}
140 
141 	bool Lock()
142 	{
143 		return fLock.Lock();
144 	}
145 
146 	void Unlock()
147 	{
148 		fLock.Unlock();
149 	}
150 
151 	int64 ChangeCount() const
152 	{
153 		return fChangeCount;
154 	}
155 
156 	Package* FindPackage(const char* name) const
157 	{
158 		return fPackagesByFileName.Lookup(name);
159 	}
160 
161 	Package* FindPackage(const node_ref& nodeRef) const
162 	{
163 		return fPackagesByNodeRef.Lookup(nodeRef);
164 	}
165 
166 	PackageFileNameHashTable::Iterator ByFileNameIterator() const
167 	{
168 		return fPackagesByFileName.GetIterator();
169 	}
170 
171 	PackageNodeRefHashTable::Iterator ByNodeRefIterator() const
172 	{
173 		return fPackagesByNodeRef.GetIterator();
174 	}
175 
176 	void AddPackage(Package* package)
177 	{
178 		AutoLocker<BLocker> locker(fLock);
179 		fPackagesByFileName.Insert(package);
180 		fPackagesByNodeRef.Insert(package);
181 	}
182 
183 	void RemovePackage(Package* package)
184 	{
185 		AutoLocker<BLocker> locker(fLock);
186 		_RemovePackage(package);
187 	}
188 
189 	void SetPackageActive(Package* package, bool active)
190 	{
191 		AutoLocker<BLocker> locker(fLock);
192 		package->SetActive(active);
193 	}
194 
195 	void ActivationChanged(const PackageSet& activatedPackage,
196 		const PackageSet& deactivatePackages)
197 	{
198 		AutoLocker<BLocker> locker(fLock);
199 
200 		for (PackageSet::iterator it = activatedPackage.begin();
201 				it != activatedPackage.end(); ++it) {
202 			(*it)->SetActive(true);
203 			fChangeCount++;
204 		}
205 
206 		for (PackageSet::iterator it = deactivatePackages.begin();
207 				it != deactivatePackages.end(); ++it) {
208 			Package* package = *it;
209 			_RemovePackage(package);
210 			delete package;
211 		}
212 	}
213 
214 	void PackageJobPending()
215 	{
216 		atomic_add(&fPendingPackageJobCount, 1);
217 	}
218 
219 
220 	void PackageJobFinished()
221 	{
222 		atomic_add(&fPendingPackageJobCount, -1);
223 	}
224 
225 
226 	bool IsPackageJobPending() const
227 	{
228 		return fPendingPackageJobCount != 0;
229 	}
230 
231 private:
232 	void _RemovePackage(Package* package)
233 	{
234 		fPackagesByFileName.Remove(package);
235 		fPackagesByNodeRef.Remove(package);
236 		fChangeCount++;
237 	}
238 
239 private:
240 	BLocker						fLock;
241 	PackageFileNameHashTable	fPackagesByFileName;
242 	PackageNodeRefHashTable		fPackagesByNodeRef;
243 	int64						fChangeCount;
244 	int32						fPendingPackageJobCount;
245 };
246 
247 
248 // #pragma mark - CommitTransactionHandler
249 
250 
251 struct Volume::CommitTransactionHandler {
252 	CommitTransactionHandler(Volume* volume,
253 		const PackageSet& packagesAlreadyAdded,
254 		const PackageSet& packagesAlreadyRemoved)
255 		:
256 		fVolume(volume),
257 		fPackagesToActivate(),
258 		fPackagesToDeactivate(),
259 		fAddedPackages(),
260 		fRemovedPackages(),
261 		fPackagesAlreadyAdded(packagesAlreadyAdded),
262 		fPackagesAlreadyRemoved(packagesAlreadyRemoved),
263 		fAddedGroups(),
264 		fAddedUsers(),
265 		fFSTransaction()
266 	{
267 	}
268 
269 	~CommitTransactionHandler()
270 	{
271 		// Delete Package objects we created in case of error (on success
272 		// fPackagesToActivate will be empty).
273 		int32 count = fPackagesToActivate.CountItems();
274 		for (int32 i = 0; i < count; i++) {
275 			Package* package = fPackagesToActivate.ItemAt(i);
276 			if (fPackagesAlreadyAdded.find(package)
277 					== fPackagesAlreadyAdded.end()) {
278 				delete package;
279 			}
280 		}
281 	}
282 
283 	void HandleRequest(BMessage* request, BMessage* reply)
284 	{
285 		status_t error;
286 		BActivationTransaction transaction(request, &error);
287 		if (error == B_OK)
288 			error = transaction.InitCheck();
289 		if (error != B_OK) {
290 			if (error == B_NO_MEMORY)
291 				throw Exception(B_NO_MEMORY);
292 			throw Exception(B_DAEMON_BAD_REQUEST);
293 		}
294 
295 		HandleRequest(transaction, reply);
296 	}
297 
298 	void HandleRequest(const BActivationTransaction& transaction,
299 		BMessage* reply)
300 	{
301 		// check the change count
302 		if (transaction.ChangeCount() != fVolume->fState->ChangeCount())
303 			throw Exception(B_DAEMON_CHANGE_COUNT_MISMATCH);
304 
305 		// collect the packages to deactivate
306 		_GetPackagesToDeactivate(transaction);
307 
308 		// read the packages to activate
309 		_ReadPackagesToActivate(transaction);
310 
311 		// anything to do at all?
312 		if (fPackagesToActivate.IsEmpty() &&  fPackagesToDeactivate.empty()) {
313 			throw Exception(B_DAEMON_BAD_REQUEST,
314 				"no packages to activate or deactivate");
315 		}
316 
317 		_ApplyChanges(reply);
318 	}
319 
320 	void HandleRequest(const PackageSet& packagesAdded,
321 		const PackageSet& packagesRemoved)
322 	{
323 		// Copy package sets to fPackagesToActivate/fPackagesToDeactivate. The
324 		// given sets are assumed to be identical to the ones specified in the
325 		// constructor invocation (fPackagesAlreadyAdded,
326 		// fPackagesAlreadyRemoved).
327 		for (PackageSet::const_iterator it = packagesAdded.begin();
328 			it != packagesAdded.end(); ++it) {
329 			if (!fPackagesToActivate.AddItem(*it))
330 				throw std::bad_alloc();
331 		}
332 
333 		fPackagesToDeactivate = packagesRemoved;
334 
335 		_ApplyChanges(NULL);
336 	}
337 
338 	void Revert()
339 	{
340 		// move packages to activate back to transaction directory
341 		_RevertAddPackagesToActivate();
342 
343 		// move packages to deactivate back to packages directory
344 		_RevertRemovePackagesToDeactivate();
345 
346 		// revert user and group changes
347 		_RevertUserGroupChanges();
348 
349 		// Revert all other FS operations, i.e. the writable files changes as
350 		// well as the creation of the old state directory.
351 		fFSTransaction.RollBack();
352 	}
353 
354 	const BString& OldStateDirectoryName() const
355 	{
356 		return fOldStateDirectoryName;
357 	}
358 
359 private:
360 	typedef BObjectList<Package> PackageList;
361 
362 	void _GetPackagesToDeactivate(const BActivationTransaction& transaction)
363 	{
364 		// get the number of packages to deactivate
365 		const BStringList& packagesToDeactivate
366 			= transaction.PackagesToDeactivate();
367 		int32 packagesToDeactivateCount = packagesToDeactivate.CountStrings();
368 		if (packagesToDeactivateCount == 0)
369 			return;
370 
371 		for (int32 i = 0; i < packagesToDeactivateCount; i++) {
372 			BString packageName = packagesToDeactivate.StringAt(i);
373 			Package* package = fVolume->fState->FindPackage(packageName);
374 			if (package == NULL) {
375 				throw Exception(B_DAEMON_NO_SUCH_PACKAGE, "no such package",
376 					packageName);
377 			}
378 
379 			fPackagesToDeactivate.insert(package);
380 
381 			if (fPackagesAlreadyRemoved.find(package)
382 					== fPackagesAlreadyRemoved.end()) {
383 				package->IncrementEntryRemovedIgnoreLevel();
384 			}
385 		}
386 	}
387 
388 	void _ReadPackagesToActivate(const BActivationTransaction& transaction)
389 	{
390 		// get the number of packages to activate
391 		const BStringList& packagesToActivate
392 			= transaction.PackagesToActivate();
393 		int32 packagesToActivateCount = packagesToActivate.CountStrings();
394 		if (packagesToActivateCount == 0)
395 			return;
396 
397 		// check the transaction directory name -- we only allow a simple
398 		// subdirectory of the admin directory
399 		const BString& transactionDirectoryName
400 			= transaction.TransactionDirectoryName();
401 		if (transactionDirectoryName.IsEmpty()
402 			|| transactionDirectoryName.FindFirst('/') >= 0
403 			|| transactionDirectoryName == "."
404 			|| transactionDirectoryName == "..") {
405 			throw Exception(B_DAEMON_BAD_REQUEST);
406 		}
407 
408 		// open the directory
409 		RelativePath directoryPath(kAdminDirectoryName,
410 			transactionDirectoryName);
411 		BDirectory directory;
412 		status_t error = fVolume->_OpenPackagesSubDirectory(directoryPath,
413 			false, directory);
414 		if (error != B_OK)
415 			throw Exception(error, "failed to open transaction directory");
416 
417 		error = directory.GetNodeRef(&fTransactionDirectoryRef);
418 		if (error != B_OK) {
419 			throw Exception(error,
420 				"failed to get transaction directory node ref");
421 		}
422 
423 		// read the packages
424 		for (int32 i = 0; i < packagesToActivateCount; i++) {
425 			BString packageName = packagesToActivate.StringAt(i);
426 
427 			// make sure it doesn't clash with an already existing package
428 			Package* package = fVolume->fState->FindPackage(packageName);
429 			if (package != NULL) {
430 				if (fPackagesAlreadyAdded.find(package)
431 						!= fPackagesAlreadyAdded.end()) {
432 					if (!fPackagesToActivate.AddItem(package))
433 						throw Exception(B_NO_MEMORY);
434 					continue;
435 				}
436 
437 				if (fPackagesToDeactivate.find(package)
438 						== fPackagesToDeactivate.end()) {
439 					throw Exception(B_DAEMON_PACKAGE_ALREADY_EXISTS, NULL,
440 						packageName);
441 				}
442 			}
443 
444 			// read the package
445 			entry_ref entryRef;
446 			entryRef.device = fTransactionDirectoryRef.device;
447 			entryRef.directory = fTransactionDirectoryRef.node;
448 			if (entryRef.set_name(packageName) != B_OK)
449 				throw Exception(B_NO_MEMORY);
450 
451 			package = new(std::nothrow) Package;
452 			if (package == NULL || !fPackagesToActivate.AddItem(package)) {
453 				delete package;
454 				throw Exception(B_NO_MEMORY);
455 			}
456 
457 			error = package->Init(entryRef);
458 			if (error != B_OK)
459 				throw Exception(error, "failed to read package", packageName);
460 
461 			package->IncrementEntryCreatedIgnoreLevel();
462 		}
463 	}
464 
465 	void _ApplyChanges(BMessage* reply)
466 	{
467 		// create an old state directory
468 		_CreateOldStateDirectory(reply);
469 
470 		// move packages to deactivate to old state directory
471 		_RemovePackagesToDeactivate();
472 
473 		// move packages to activate to packages directory
474 		_AddPackagesToActivate();
475 
476 		// activate/deactivate packages
477 		fVolume->_ChangePackageActivation(fAddedPackages, fRemovedPackages);
478 
479 		// run post-installation scripts
480 		_RunPostInstallScripts();
481 
482 		// removed packages have been deleted, new packages shall not be deleted
483 		fAddedPackages.clear();
484 		fRemovedPackages.clear();
485 		fPackagesToActivate.MakeEmpty(false);
486 		fPackagesToDeactivate.clear();
487 	}
488 
489 	void _CreateOldStateDirectory(BMessage* reply)
490 	{
491 		// construct a nice name from the current date and time
492 		time_t nowSeconds = time(NULL);
493 		struct tm now;
494 		BString baseName;
495 		if (localtime_r(&nowSeconds, &now) != NULL) {
496 			baseName.SetToFormat("state_%d-%02d-%02d_%02d:%02d:%02d",
497 				1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour,
498 				now.tm_min, now.tm_sec);
499 		} else
500 			baseName = "state";
501 
502 		if (baseName.IsEmpty())
503 			throw Exception(B_NO_MEMORY);
504 
505 		// make sure the directory doesn't exist yet
506 		BDirectory adminDirectory;
507 		status_t error = fVolume->_OpenPackagesSubDirectory(
508 			RelativePath(kAdminDirectoryName), true, adminDirectory);
509 		if (error != B_OK)
510 			throw Exception(error, "failed to open administrative directory");
511 
512 		int uniqueId = 1;
513 		BString directoryName = baseName;
514 		while (BEntry(&adminDirectory, directoryName).Exists()) {
515 			directoryName.SetToFormat("%s-%d", baseName.String(), uniqueId++);
516 			if (directoryName.IsEmpty())
517 				throw Exception(B_NO_MEMORY);
518 		}
519 
520 		// create the directory
521 		FSTransaction::CreateOperation createOldStateDirectoryOperation(
522 			&fFSTransaction, FSUtils::Entry(adminDirectory, directoryName));
523 
524 		error = adminDirectory.CreateDirectory(directoryName,
525 			&fOldStateDirectory);
526 		if (error != B_OK)
527 			throw Exception(error, "failed to create old state directory");
528 
529 		createOldStateDirectoryOperation.Finished();
530 
531 		fOldStateDirectoryName = directoryName;
532 
533 		// write the old activation file
534 		BEntry activationFile;
535 		error = fVolume->_WriteActivationFile(
536 			RelativePath(kAdminDirectoryName, directoryName),
537 			kActivationFileName, PackageSet(), PackageSet(), activationFile);
538 		if (error != B_OK)
539 			throw Exception(error, "failed to write old activation file");
540 
541 		// add the old state directory to the reply
542 		if (reply != NULL) {
543 			error = reply->AddString("old state", fOldStateDirectoryName);
544 			if (error != B_OK)
545 				throw Exception(error, "failed to add field to reply");
546 		}
547 	}
548 
549 	void _RemovePackagesToDeactivate()
550 	{
551 		if (fPackagesToDeactivate.empty())
552 			return;
553 
554 		for (PackageSet::const_iterator it = fPackagesToDeactivate.begin();
555 			it != fPackagesToDeactivate.end(); ++it) {
556 			Package* package = *it;
557 			if (fPackagesAlreadyRemoved.find(package)
558 					!= fPackagesAlreadyRemoved.end()) {
559 				fRemovedPackages.insert(package);
560 				continue;
561 			}
562 
563 			// get a BEntry for the package
564 			NotOwningEntryRef entryRef(fVolume->fPackagesDirectoryRef,
565 				package->FileName());
566 
567 			BEntry entry;
568 			status_t error = entry.SetTo(&entryRef);
569 			if (error != B_OK) {
570 				throw Exception(error, "failed to get package entry",
571 					package->FileName());
572 			}
573 
574 			// move entry
575 			fRemovedPackages.insert(package);
576 
577 			error = entry.MoveTo(&fOldStateDirectory);
578 			if (error != B_OK) {
579 				fRemovedPackages.erase(package);
580 				throw Exception(error,
581 					"failed to move old package from packages directory",
582 					package->FileName());
583 			}
584 		}
585 	}
586 
587 	void _AddPackagesToActivate()
588 	{
589 		if (fPackagesToActivate.IsEmpty())
590 			return;
591 
592 		// open packages directory
593 		BDirectory packagesDirectory;
594 		status_t error
595 			= packagesDirectory.SetTo(&fVolume->fPackagesDirectoryRef);
596 		if (error != B_OK)
597 			throw Exception(error, "failed to open packages directory");
598 
599 		int32 count = fPackagesToActivate.CountItems();
600 		for (int32 i = 0; i < count; i++) {
601 			Package* package = fPackagesToActivate.ItemAt(i);
602 			if (fPackagesAlreadyAdded.find(package)
603 					!= fPackagesAlreadyAdded.end()) {
604 				fAddedPackages.insert(package);
605 				_PreparePackageToActivate(package);
606 				continue;
607 			}
608 
609 			// get a BEntry for the package
610 			entry_ref entryRef;
611 			entryRef.device = fTransactionDirectoryRef.device;
612 			entryRef.directory = fTransactionDirectoryRef.node;
613 			if (entryRef.set_name(package->FileName()) != B_OK)
614 				throw Exception(B_NO_MEMORY);
615 
616 			BEntry entry;
617 			error = entry.SetTo(&entryRef);
618 			if (error != B_OK) {
619 				throw Exception(error, "failed to get package entry",
620 					package->FileName());
621 			}
622 
623 			// move entry
624 			fAddedPackages.insert(package);
625 
626 			error = entry.MoveTo(&packagesDirectory);
627 			if (error != B_OK) {
628 				fAddedPackages.erase(package);
629 				throw Exception(error,
630 					"failed to move new package to packages directory",
631 					package->FileName());
632 			}
633 
634 			// also add the package to the volume
635 			fVolume->_AddPackage(package);
636 
637 			_PreparePackageToActivate(package);
638 		}
639 	}
640 
641 	void _PreparePackageToActivate(Package* package)
642 	{
643 		// add groups
644 		const BStringList& groups = package->Info().Groups();
645 		int32 count = groups.CountStrings();
646 		for (int32 i = 0; i < count; i++)
647 			_AddGroup(package, groups.StringAt(i));
648 
649 		// add users
650 		const BObjectList<BUser>& users = package->Info().Users();
651 		for (int32 i = 0; const BUser* user = users.ItemAt(i); i++)
652 			_AddUser(package, *user);
653 
654 		// handle global writable files
655 		_AddGlobalWritableFiles(package);
656 	}
657 
658 	void _AddGroup(Package* package, const BString& groupName)
659 	{
660 		// Check whether the group already exists.
661 		char buffer[256];
662 		struct group groupBuffer;
663 		struct group* groupFound;
664 		int error = getgrnam_r(groupName, &groupBuffer, buffer, sizeof(buffer),
665 			&groupFound);
666 		if ((error == 0 && groupFound != NULL) || error == ERANGE)
667 			return;
668 
669 		// add it
670 		fAddedGroups.insert(groupName.String());
671 
672 		std::string commandLine("groupadd ");
673 		commandLine += FSUtils::ShellEscapeString(groupName).String();
674 
675 		if (system(commandLine.c_str()) != 0) {
676 			fAddedGroups.erase(groupName.String());
677 			throw Exception(error,
678 				BString().SetToFormat("failed to add group \%s\"",
679 					groupName.String()),
680 				package->FileName());
681 		}
682 	}
683 
684 	void _AddUser(Package* package, const BUser& user)
685 	{
686 		// Check whether the user already exists.
687 		char buffer[256];
688 		struct passwd passwdBuffer;
689 		struct passwd* passwdFound;
690 		int error = getpwnam_r(user.Name(), &passwdBuffer, buffer,
691 			sizeof(buffer), &passwdFound);
692 		if ((error == 0 && passwdFound != NULL) || error == ERANGE)
693 			return;
694 
695 		// add it
696 		fAddedUsers.insert(user.Name().String());
697 
698 		std::string commandLine("useradd ");
699 
700 		if (!user.RealName().IsEmpty()) {
701 			commandLine += std::string("-n ")
702 				+ FSUtils::ShellEscapeString(user.RealName()).String() + " ";
703 		}
704 
705 		if (!user.Home().IsEmpty()) {
706 			commandLine += std::string("-d ")
707 				+ FSUtils::ShellEscapeString(user.Home()).String() + " ";
708 		}
709 
710 		if (!user.Shell().IsEmpty()) {
711 			commandLine += std::string("-s ")
712 				+ FSUtils::ShellEscapeString(user.Shell()).String() + " ";
713 		}
714 
715 		if (!user.Groups().IsEmpty()) {
716 			commandLine += std::string("-g ")
717 				+ FSUtils::ShellEscapeString(user.Groups().First()).String()
718 				+ " ";
719 		}
720 
721 		commandLine += FSUtils::ShellEscapeString(user.Name()).String();
722 
723 		if (system(commandLine.c_str()) != 0) {
724 			fAddedUsers.erase(user.Name().String());
725 			throw Exception(error,
726 				BString().SetToFormat("failed to add user \%s\"",
727 					user.Name().String()),
728 				package->FileName());
729 		}
730 
731 		// add the supplementary groups
732 		int32 groupCount = user.Groups().CountStrings();
733 		for (int32 i = 1; i < groupCount; i++) {
734 			commandLine = std::string("groupmod -A ")
735 				+ FSUtils::ShellEscapeString(user.Name()).String()
736 				+ " "
737 				+ FSUtils::ShellEscapeString(user.Groups().StringAt(i))
738 					.String();
739 			if (system(commandLine.c_str()) != 0) {
740 				fAddedUsers.erase(user.Name().String());
741 				throw Exception(error,
742 					BString().SetToFormat("failed to add user \%s\" to group "
743 						"\"%s\"", user.Name().String(),
744 						user.Groups().StringAt(i).String()),
745 					package->FileName());
746 			}
747 		}
748 	}
749 
750 	void _AddGlobalWritableFiles(Package* package)
751 	{
752 		// get the list of included files
753 		const BObjectList<BGlobalWritableFileInfo>& files
754 			= package->Info().GlobalWritableFileInfos();
755 		BStringList contentPaths;
756 		for (int32 i = 0; const BGlobalWritableFileInfo* file = files.ItemAt(i);
757 			i++) {
758 			if (file->IsIncluded() && !contentPaths.Add(file->Path()))
759 				throw std::bad_alloc();
760 		}
761 
762 		if (contentPaths.IsEmpty())
763 			return;
764 
765 		// Open the root directory of the installation location where we will
766 		// extract the files -- that's the volume's root directory.
767 		BDirectory rootDirectory;
768 		status_t error = rootDirectory.SetTo(&fVolume->fRootDirectoryRef);
769 		if (error != B_OK) {
770 			throw Exception(error,
771 				BString().SetToFormat("failed to get the root directory "
772 					"for writable files"),
773 				package->FileName());
774 		}
775 
776 		// Open writable-files directory in the administrative directory.
777 		if (fWritableFilesDirectory.InitCheck() != B_OK) {
778 			error = fVolume->_OpenPackagesSubDirectory(
779 				RelativePath(kAdminDirectoryName, kWritableFilesDirectoryName),
780 				true, fWritableFilesDirectory);
781 
782 			if (error != B_OK) {
783 				throw Exception(error,
784 					BString().SetToFormat("failed to get the backup directory "
785 						"for writable files"),
786 					package->FileName());
787 			}
788 		}
789 
790 		// extract files into a subdir of the writable-files directory
791 		BDirectory extractedFilesDirectory;
792 		_ExtractPackageContent(package, contentPaths,
793 			fWritableFilesDirectory, extractedFilesDirectory);
794 
795 		for (int32 i = 0; const BGlobalWritableFileInfo* file = files.ItemAt(i);
796 			i++) {
797 			if (file->IsIncluded()) {
798 				_AddGlobalWritableFile(package, *file, rootDirectory,
799 					extractedFilesDirectory);
800 			}
801 		}
802 	}
803 
804 	void _AddGlobalWritableFile(Package* package,
805 		const BGlobalWritableFileInfo& file, const BDirectory& rootDirectory,
806 		const BDirectory& extractedFilesDirectory)
807 	{
808 		// Map the path name to the actual target location. Currently this only
809 		// concerns "settings/", which is mapped to "settings/global/".
810 		BString targetPath(file.Path());
811 		if (fVolume->fMountType == PACKAGE_FS_MOUNT_TYPE_HOME) {
812 			if (targetPath == "settings"
813 				|| targetPath.StartsWith("settings/")) {
814 				targetPath.Insert("/global", 8);
815 				if (targetPath.Length() == file.Path().Length())
816 					throw std::bad_alloc();
817 			}
818 		}
819 
820 		// open parent directory of the source entry
821 		const char* lastSlash = strrchr(file.Path(), '/');
822 		const BDirectory* sourceDirectory;
823 		BDirectory stackSourceDirectory;
824 		if (lastSlash != NULL) {
825 			sourceDirectory = &stackSourceDirectory;
826 			BString sourceParentPath(file.Path(),
827 				lastSlash - file.Path().String());
828 			if (sourceParentPath.Length() == 0)
829 				throw std::bad_alloc();
830 
831 			status_t error = stackSourceDirectory.SetTo(
832 				&extractedFilesDirectory, sourceParentPath);
833 			if (error != B_OK) {
834 				throw Exception(error,
835 					BString().SetToFormat("failed to open directory \"%s\"",
836 						_GetPath(
837 							FSUtils::Entry(extractedFilesDirectory,
838 								sourceParentPath),
839 							sourceParentPath).String()),
840 					package->FileName());
841 			}
842 		} else {
843 			sourceDirectory = &extractedFilesDirectory;
844 		}
845 
846 		// open parent directory of the target entry -- create, if necessary
847 		FSUtils::Path relativeSourcePath(file.Path());
848 		lastSlash = strrchr(targetPath, '/');
849 		if (lastSlash != NULL) {
850 			BString targetParentPath(targetPath,
851 				lastSlash - targetPath.String());
852 			if (targetParentPath.Length() == 0)
853 				throw std::bad_alloc();
854 
855 			BDirectory targetDirectory;
856 			status_t error = FSUtils::OpenSubDirectory(rootDirectory,
857 				RelativePath(targetParentPath), true, targetDirectory);
858 			if (error != B_OK) {
859 				throw Exception(error,
860 					BString().SetToFormat("failed to open/create directory "
861 						"\"%s\"",
862 						_GetPath(
863 							FSUtils::Entry(rootDirectory,targetParentPath),
864 							targetParentPath).String()),
865 					package->FileName());
866 			}
867 			_AddGlobalWritableFileRecurse(package, *sourceDirectory,
868 				relativeSourcePath, targetDirectory, lastSlash + 1,
869 				file.UpdateType());
870 		} else {
871 			_AddGlobalWritableFileRecurse(package, *sourceDirectory,
872 				relativeSourcePath, rootDirectory, targetPath,
873 				file.UpdateType());
874 		}
875 	}
876 
877 	void _AddGlobalWritableFileRecurse(Package* package,
878 		const BDirectory& sourceDirectory, FSUtils::Path& relativeSourcePath,
879 		const BDirectory& targetDirectory, const char* targetName,
880 		BWritableFileUpdateType updateType)
881 	{
882 		// * If the file doesn't exist, just copy the extracted one.
883 		// * If the file does exist, compare with the previous original version:
884 		//   * If unchanged, just overwrite it.
885 		//   * If changed, leave it to the user for now. When we support merging
886 		//     first back the file up, then try the merge.
887 
888 		// Check whether the target location exists and what type the entry at
889 		// both locations are.
890 		struct stat targetStat;
891 		if (targetDirectory.GetStatFor(targetName, &targetStat) != B_OK) {
892 			// target doesn't exist -- just copy
893 			PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
894 				"couldn't get stat for writable file, copying...\n");
895 			FSTransaction::CreateOperation copyOperation(&fFSTransaction,
896 				FSUtils::Entry(targetDirectory, targetName));
897 			status_t error = BCopyEngine(BCopyEngine::COPY_RECURSIVELY)
898 				.CopyEntry(
899 					FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()),
900 					FSUtils::Entry(targetDirectory, targetName));
901 			if (error != B_OK) {
902 				if (targetDirectory.GetStatFor(targetName, &targetStat) == B_OK)
903 					copyOperation.Finished();
904 
905 				throw Exception(error,
906 					BString().SetToFormat("failed to copy entry \"%s\"",
907 						_GetPath(
908 							FSUtils::Entry(sourceDirectory,
909 								relativeSourcePath.Leaf()),
910 							relativeSourcePath).String()),
911 					package->FileName());
912 			}
913 			copyOperation.Finished();
914 			return;
915 		}
916 
917 		struct stat sourceStat;
918 		status_t error = sourceDirectory.GetStatFor(relativeSourcePath.Leaf(),
919 			&sourceStat);
920 		if (error != B_OK) {
921 			throw Exception(error,
922 				BString().SetToFormat("failed to get stat data for entry "
923 					"\"%s\"",
924 					_GetPath(
925 						FSUtils::Entry(targetDirectory, targetName),
926 						targetName).String()),
927 				package->FileName());
928 		}
929 
930 		if ((sourceStat.st_mode & S_IFMT) != (targetStat.st_mode & S_IFMT)
931 			|| (!S_ISDIR(sourceStat.st_mode) && !S_ISREG(sourceStat.st_mode)
932 				&& !S_ISLNK(sourceStat.st_mode))) {
933 			// Source and target entry types don't match or this is an entry
934 			// we cannot handle. The user must handle this manually.
935 			PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
936 				"writable file exists, but type doesn't match previous type\n");
937 // TODO: Notify user!
938 			return;
939 		}
940 
941 		if (S_ISDIR(sourceStat.st_mode)) {
942 			// entry is a directory -- recurse
943 			BDirectory sourceSubDirectory;
944 			error = sourceSubDirectory.SetTo(&sourceDirectory,
945 				relativeSourcePath.Leaf());
946 			if (error != B_OK) {
947 				throw Exception(error,
948 					BString().SetToFormat("failed to open directory \"%s\"",
949 						_GetPath(
950 							FSUtils::Entry(sourceDirectory,
951 								relativeSourcePath.Leaf()),
952 							relativeSourcePath).String()),
953 					package->FileName());
954 			}
955 
956 			BDirectory targetSubDirectory;
957 			error = targetSubDirectory.SetTo(&targetDirectory, targetName);
958 			if (error != B_OK) {
959 				throw Exception(error,
960 					BString().SetToFormat("failed to open directory \"%s\"",
961 						_GetPath(
962 							FSUtils::Entry(targetDirectory, targetName),
963 							targetName).String()),
964 					package->FileName());
965 			}
966 
967 			entry_ref entry;
968 			while (sourceSubDirectory.GetNextRef(&entry) == B_OK) {
969 				relativeSourcePath.AppendComponent(entry.name);
970 				_AddGlobalWritableFileRecurse(package, sourceSubDirectory,
971 					relativeSourcePath, targetSubDirectory, entry.name,
972 					updateType);
973 				relativeSourcePath.RemoveLastComponent();
974 			}
975 
976 			PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
977 				"writable directory, recursion done\n");
978 			return;
979 		}
980 
981 		// get the package the target file originated from
982 		BString originalPackage;
983 		if (BNode(&targetDirectory, targetName).ReadAttrString(
984 				kPackageFileAttribute, &originalPackage) != B_OK) {
985 			// Can't determine the original package. The user must handle this
986 			// manually.
987 // TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD!
988 			PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
989 				"failed to get SYS:PACKAGE attribute\n");
990 			return;
991 		}
992 
993 		// If that's our package, we're happy.
994 		if (originalPackage == package->RevisionedNameThrows()) {
995 			PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
996 				"file tagged with same package version we're activating\n");
997 			return;
998 		}
999 
1000 		// Check, whether the writable-files directory for the original package
1001 		// exists.
1002 		BString originalRelativeSourcePath = BString().SetToFormat("%s/%s",
1003 			originalPackage.String(), relativeSourcePath.ToCString());
1004 		if (originalRelativeSourcePath.IsEmpty())
1005 			throw std::bad_alloc();
1006 
1007 		struct stat originalPackageStat;
1008 		if (fWritableFilesDirectory.GetStatFor(originalRelativeSourcePath,
1009 				&originalPackageStat) != B_OK
1010 			|| (sourceStat.st_mode & S_IFMT)
1011 				!= (originalPackageStat.st_mode & S_IFMT)) {
1012 			// Original entry doesn't exist (either we don't have the data from
1013 			// the original package or the entry really didn't exist) or its
1014 			// type differs from the expected one. The user must handle this
1015 			// manually.
1016 			PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): "
1017 				"original \"%s\" doesn't exist or has other type\n",
1018 				_GetPath(FSUtils::Entry(fWritableFilesDirectory,
1019 						originalRelativeSourcePath),
1020 					originalRelativeSourcePath).String());
1021 			return;
1022 // TODO: Notify user!
1023 		}
1024 
1025 		if (S_ISREG(sourceStat.st_mode)) {
1026 			// compare file content
1027 			bool equal;
1028 			error = FSUtils::CompareFileContent(
1029 				FSUtils::Entry(fWritableFilesDirectory,
1030 					originalRelativeSourcePath),
1031 				FSUtils::Entry(targetDirectory, targetName),
1032 				equal);
1033 			// TODO: Merge support!
1034 			if (error != B_OK || !equal) {
1035 				// The comparison failed or the files differ. The user must
1036 				// handle this manually.
1037 				PRINT("Volume::CommitTransactionHandler::"
1038 					"_AddGlobalWritableFile(): "
1039 					"file comparison failed (%s) or files aren't equal\n",
1040 					strerror(error));
1041 				return;
1042 // TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD!
1043 			}
1044 		} else {
1045 			// compare symlinks
1046 			bool equal;
1047 			error = FSUtils::CompareSymLinks(
1048 				FSUtils::Entry(fWritableFilesDirectory,
1049 					originalRelativeSourcePath),
1050 				FSUtils::Entry(targetDirectory, targetName),
1051 				equal);
1052 			if (error != B_OK || !equal) {
1053 				// The comparison failed or the symlinks differ. The user must
1054 				// handle this manually.
1055 				PRINT("Volume::CommitTransactionHandler::"
1056 					"_AddGlobalWritableFile(): "
1057 					"symlink comparison failed (%s) or symlinks aren't equal\n",
1058 					strerror(error));
1059 				return;
1060 // TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD!
1061 			}
1062 		}
1063 
1064 		// Replace the existing file/symlink. We do that in two steps: First
1065 		// copy the new file to a neighoring location, then move-replace the
1066 		// old file.
1067 		BString tempTargetName;
1068 		tempTargetName.SetToFormat("%s.%s", targetName,
1069 			package->RevisionedNameThrows().String());
1070 		if (tempTargetName.IsEmpty())
1071 			throw std::bad_alloc();
1072 
1073 		// copy
1074 		FSTransaction::CreateOperation copyOperation(&fFSTransaction,
1075 			FSUtils::Entry(targetDirectory, tempTargetName));
1076 
1077 		error = BCopyEngine(BCopyEngine::UNLINK_DESTINATION).CopyEntry(
1078 			FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()),
1079 			FSUtils::Entry(targetDirectory, tempTargetName));
1080 		if (error != B_OK) {
1081 			throw Exception(error,
1082 				BString().SetToFormat("failed to copy entry \"%s\"",
1083 					_GetPath(
1084 						FSUtils::Entry(sourceDirectory,
1085 							relativeSourcePath.Leaf()),
1086 						relativeSourcePath).String()),
1087 				package->FileName());
1088 		}
1089 
1090 		copyOperation.Finished();
1091 
1092 		// rename
1093 		FSTransaction::RemoveOperation renameOperation(&fFSTransaction,
1094 			FSUtils::Entry(targetDirectory, targetName),
1095 			FSUtils::Entry(fWritableFilesDirectory,
1096 				originalRelativeSourcePath));
1097 
1098 		BEntry targetEntry;
1099 		error = targetEntry.SetTo(&targetDirectory, tempTargetName);
1100 		if (error == B_OK)
1101 			error = targetEntry.Rename(targetName, true);
1102 		if (error != B_OK) {
1103 			throw Exception(error,
1104 				BString().SetToFormat("failed to rename entry \"%s\" to \"%s\"",
1105 					_GetPath(
1106 						FSUtils::Entry(targetDirectory, tempTargetName),
1107 						tempTargetName).String(),
1108 					targetName),
1109 				package->FileName());
1110 		}
1111 
1112 		renameOperation.Finished();
1113 		copyOperation.Unregister();
1114 	}
1115 
1116 	void _RevertAddPackagesToActivate()
1117 	{
1118 		if (fAddedPackages.empty())
1119 			return;
1120 
1121 		// open transaction directory
1122 		BDirectory transactionDirectory;
1123 		status_t error = transactionDirectory.SetTo(&fTransactionDirectoryRef);
1124 		if (error != B_OK) {
1125 			ERROR("failed to open transaction directory: %s\n",
1126 				strerror(error));
1127 		}
1128 
1129 		for (PackageSet::iterator it = fAddedPackages.begin();
1130 			it != fAddedPackages.end(); ++it) {
1131 			// remove package from the volume
1132 			Package* package = *it;
1133 
1134 			if (fPackagesAlreadyAdded.find(package)
1135 					!= fPackagesAlreadyAdded.end()) {
1136 				continue;
1137 			}
1138 
1139 			fVolume->_RemovePackage(package);
1140 
1141 			if (transactionDirectory.InitCheck() != B_OK)
1142 				continue;
1143 
1144 			// get BEntry for the package
1145 			NotOwningEntryRef entryRef(fVolume->fPackagesDirectoryRef,
1146 				package->FileName());
1147 
1148 			BEntry entry;
1149 			error = entry.SetTo(&entryRef);
1150 			if (error != B_OK) {
1151 				ERROR("failed to get entry for package \"%s\": %s\n",
1152 					package->FileName().String(), strerror(error));
1153 				continue;
1154 			}
1155 
1156 			// move entry
1157 			error = entry.MoveTo(&transactionDirectory);
1158 			if (error != B_OK) {
1159 				ERROR("failed to move new package \"%s\" back to transaction "
1160 					"directory: %s\n", package->FileName().String(),
1161 					strerror(error));
1162 				continue;
1163 			}
1164 		}
1165 	}
1166 
1167 	void _RevertRemovePackagesToDeactivate()
1168 	{
1169 		if (fRemovedPackages.empty())
1170 			return;
1171 
1172 		// open packages directory
1173 		BDirectory packagesDirectory;
1174 		status_t error
1175 			= packagesDirectory.SetTo(&fVolume->fPackagesDirectoryRef);
1176 		if (error != B_OK) {
1177 			throw Exception(error, "failed to open packages directory");
1178 			ERROR("failed to open packages directory: %s\n",
1179 				strerror(error));
1180 			return;
1181 		}
1182 
1183 		for (PackageSet::iterator it = fRemovedPackages.begin();
1184 			it != fRemovedPackages.end(); ++it) {
1185 			Package* package = *it;
1186 			if (fPackagesAlreadyRemoved.find(package)
1187 					!= fPackagesAlreadyRemoved.end()) {
1188 				continue;
1189 			}
1190 
1191 			// get a BEntry for the package
1192 			BEntry entry;
1193 			status_t error = entry.SetTo(&fOldStateDirectory,
1194 				package->FileName());
1195 			if (error != B_OK) {
1196 				ERROR("failed to get entry for package \"%s\": %s\n",
1197 					package->FileName().String(), strerror(error));
1198 				continue;
1199 			}
1200 
1201 			// move entry
1202 			error = entry.MoveTo(&packagesDirectory);
1203 			if (error != B_OK) {
1204 				ERROR("failed to move old package \"%s\" back to packages "
1205 					"directory: %s\n", package->FileName().String(),
1206 					strerror(error));
1207 				continue;
1208 			}
1209 		}
1210 	}
1211 
1212 	void _RevertUserGroupChanges()
1213 	{
1214 		// delete users
1215 		for (StringSet::const_iterator it = fAddedUsers.begin();
1216 			it != fAddedUsers.end(); ++it) {
1217 			std::string commandLine("userdel ");
1218 			commandLine += FSUtils::ShellEscapeString(it->c_str()).String();
1219 			if (system(commandLine.c_str()) != 0)
1220 				ERROR("failed to remove user \"%s\"\n", it->c_str());
1221 		}
1222 
1223 		// delete groups
1224 		for (StringSet::const_iterator it = fAddedGroups.begin();
1225 			it != fAddedGroups.end(); ++it) {
1226 			std::string commandLine("groupdel ");
1227 			commandLine += FSUtils::ShellEscapeString(it->c_str()).String();
1228 			if (system(commandLine.c_str()) != 0)
1229 				ERROR("failed to remove group \"%s\"\n", it->c_str());
1230 		}
1231 	}
1232 
1233 	void _RunPostInstallScripts()
1234 	{
1235 		for (PackageSet::iterator it = fAddedPackages.begin();
1236 			it != fAddedPackages.end(); ++it) {
1237 			Package* package = *it;
1238 			const BStringList& scripts = package->Info().PostInstallScripts();
1239 			int32 count = scripts.CountStrings();
1240 			for (int32 i = 0; i < count; i++)
1241 				_RunPostInstallScript(package, scripts.StringAt(i));
1242 		}
1243 	}
1244 
1245 	void _RunPostInstallScript(Package* package, const BString& script)
1246 	{
1247 		BDirectory rootDir(&fVolume->fRootDirectoryRef);
1248 		BPath scriptPath(&rootDir, script);
1249 		status_t error = scriptPath.InitCheck();
1250 		if (error != B_OK) {
1251 			ERROR("Volume::CommitTransactionHandler::_RunPostInstallScript(): "
1252 				"failed get path of post-installation script \"%s\" of package "
1253 				"%s: %s\n", script.String(), package->FileName().String(),
1254 				strerror(error));
1255 // TODO: Notify the user!
1256 			return;
1257 		}
1258 
1259 		if (system(scriptPath.Path()) != 0) {
1260 			ERROR("Volume::CommitTransactionHandler::_RunPostInstallScript(): "
1261 				"running post-installation script \"%s\" of package %s "
1262 				"failed: %s\n", script.String(), package->FileName().String(),
1263 				strerror(error));
1264 // TODO: Notify the user!
1265 		}
1266 	}
1267 
1268 	static BString _GetPath(const FSUtils::Entry& entry,
1269 		const BString& fallback)
1270 	{
1271 		BString path = entry.Path();
1272 		return path.IsEmpty() ? fallback : path;
1273 	}
1274 
1275 	void _ExtractPackageContent(Package* package,
1276 		const BStringList& contentPaths, BDirectory& targetDirectory,
1277 		BDirectory& _extractedFilesDirectory)
1278 	{
1279 		// check whether the subdirectory already exists
1280 		BString targetName(package->RevisionedNameThrows());
1281 
1282 		BEntry targetEntry;
1283 		status_t error = targetEntry.SetTo(&targetDirectory, targetName);
1284 		if (error != B_OK) {
1285 			throw Exception(error,
1286 				BString().SetToFormat("failed to init entry \"%s\"",
1287 					_GetPath(
1288 						FSUtils::Entry(targetDirectory, targetName),
1289 						targetName).String()),
1290 				package->FileName());
1291 		}
1292 		if (targetEntry.Exists()) {
1293 			// nothing to do -- the very same version of the package has already
1294 			// been extracted
1295 			error = _extractedFilesDirectory.SetTo(&targetDirectory,
1296 				targetName);
1297 			if (error != B_OK) {
1298 				throw Exception(error,
1299 					BString().SetToFormat("failed to open directory \"%s\"",
1300 						_GetPath(
1301 							FSUtils::Entry(targetDirectory, targetName),
1302 							targetName).String()),
1303 					package->FileName());
1304 			}
1305 			return;
1306 		}
1307 
1308 		// create the subdirectory with a temporary name (remove, if it already
1309 		// exists)
1310 		BString temporaryTargetName = BString().SetToFormat("%s.tmp",
1311 			targetName.String());
1312 		if (temporaryTargetName.IsEmpty())
1313 			throw std::bad_alloc();
1314 
1315 		error = targetEntry.SetTo(&targetDirectory, temporaryTargetName);
1316 		if (error != B_OK) {
1317 			throw Exception(error,
1318 				BString().SetToFormat("failed to init entry \"%s\"",
1319 					_GetPath(
1320 						FSUtils::Entry(targetDirectory, temporaryTargetName),
1321 						temporaryTargetName).String()),
1322 				package->FileName());
1323 		}
1324 
1325 		if (targetEntry.Exists()) {
1326 			// remove pre-existing
1327 			error = BRemoveEngine().RemoveEntry(FSUtils::Entry(targetEntry));
1328 			if (error != B_OK) {
1329 				throw Exception(error,
1330 					BString().SetToFormat("failed to remove directory \"%s\"",
1331 						_GetPath(
1332 							FSUtils::Entry(targetDirectory,
1333 								temporaryTargetName),
1334 							temporaryTargetName).String()),
1335 					package->FileName());
1336 			}
1337 		}
1338 
1339 		BDirectory& subDirectory = _extractedFilesDirectory;
1340 		FSTransaction::CreateOperation createSubDirectoryOperation(
1341 			&fFSTransaction,
1342 			FSUtils::Entry(targetDirectory, temporaryTargetName));
1343 		error = targetDirectory.CreateDirectory(temporaryTargetName,
1344 			&subDirectory);
1345 		if (error != B_OK) {
1346 			throw Exception(error,
1347 				BString().SetToFormat("failed to create directory \"%s\"",
1348 					_GetPath(
1349 						FSUtils::Entry(targetDirectory, temporaryTargetName),
1350 						temporaryTargetName).String()),
1351 				package->FileName());
1352 		}
1353 
1354 		createSubDirectoryOperation.Finished();
1355 
1356 		// extract
1357 		NotOwningEntryRef packageRef(fVolume->fPackagesDirectoryRef,
1358 			package->FileName());
1359 
1360 		int32 contentPathCount = contentPaths.CountStrings();
1361 		for (int32 i = 0; i < contentPathCount; i++) {
1362 			const char* contentPath = contentPaths.StringAt(i);
1363 
1364 			error = FSUtils::ExtractPackageContent(FSUtils::Entry(packageRef),
1365 				contentPath, FSUtils::Entry(subDirectory));
1366 			if (error != B_OK) {
1367 				throw Exception(error,
1368 					BString().SetToFormat(
1369 						"failed to extract \"%s\" from package", contentPath),
1370 					package->FileName());
1371 			}
1372 		}
1373 
1374 		// tag all entries with the package attribute
1375 		error = _TagPackageEntriesRecursively(subDirectory, targetName, true);
1376 		if (error != B_OK) {
1377 			throw Exception(error,
1378 				BString().SetToFormat("failed to tag extract files in \"%s\" "
1379 					"with package attribute",
1380 					_GetPath(
1381 						FSUtils::Entry(targetDirectory, temporaryTargetName),
1382 						temporaryTargetName).String()),
1383 				package->FileName());
1384 		}
1385 
1386 		// rename the subdirectory
1387 		error = targetEntry.Rename(targetName);
1388 		if (error != B_OK) {
1389 			throw Exception(error,
1390 				BString().SetToFormat("failed to rename entry \"%s\" to \"%s\"",
1391 					_GetPath(
1392 						FSUtils::Entry(targetDirectory, temporaryTargetName),
1393 						temporaryTargetName).String(),
1394 					targetName.String()),
1395 				package->FileName());
1396 		}
1397 
1398 		// keep the directory, regardless of whether the transaction is rolled
1399 		// back
1400 		createSubDirectoryOperation.Unregister();
1401 	}
1402 
1403 	static status_t _TagPackageEntriesRecursively(BDirectory& directory,
1404 		const BString& value, bool nonDirectoriesOnly)
1405 	{
1406 		char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH];
1407 		dirent *entry = (dirent*)buffer;
1408 		while (directory.GetNextDirents(entry, sizeof(buffer), 1) == 1) {
1409 			if (strcmp(entry->d_name, ".") == 0
1410 				|| strcmp(entry->d_name, "..") == 0) {
1411 				continue;
1412 			}
1413 
1414 			// determine type
1415 			struct stat st;
1416 			status_t error = directory.GetStatFor(entry->d_name, &st);
1417 			if (error != B_OK)
1418 				return error;
1419 			bool isDirectory = S_ISDIR(st.st_mode);
1420 
1421 			// open the node and set the attribute
1422 			BNode stackNode;
1423 			BDirectory stackDirectory;
1424 			BNode* node;
1425 			if (isDirectory) {
1426 				node = &stackDirectory;
1427 				error = stackDirectory.SetTo(&directory, entry->d_name);
1428 			} else {
1429 				node = &stackNode;
1430 				error = stackNode.SetTo(&directory, entry->d_name);
1431 			}
1432 
1433 			if (error != B_OK)
1434 				return error;
1435 
1436 			if (!isDirectory || !nonDirectoriesOnly) {
1437 				error = node->WriteAttrString(kPackageFileAttribute, &value);
1438 				if (error != B_OK)
1439 					return error;
1440 			}
1441 
1442 			// recurse
1443 			if (isDirectory) {
1444 				error = _TagPackageEntriesRecursively(stackDirectory, value,
1445 					nonDirectoriesOnly);
1446 				if (error != B_OK)
1447 					return error;
1448 			}
1449 		}
1450 
1451 		return B_OK;
1452 	}
1453 
1454 private:
1455 	Volume*				fVolume;
1456 	PackageList			fPackagesToActivate;
1457 	PackageSet			fPackagesToDeactivate;
1458 	PackageSet			fAddedPackages;
1459 	PackageSet			fRemovedPackages;
1460 	const PackageSet&	fPackagesAlreadyAdded;
1461 	const PackageSet&	fPackagesAlreadyRemoved;
1462 	BDirectory			fOldStateDirectory;
1463 	BString				fOldStateDirectoryName;
1464 	node_ref			fTransactionDirectoryRef;
1465 	BDirectory			fWritableFilesDirectory;
1466 	StringSet			fAddedGroups;
1467 	StringSet			fAddedUsers;
1468 	FSTransaction		fFSTransaction;
1469 };
1470 
1471 
1472 // #pragma mark - Volume
1473 
1474 
1475 Volume::Volume(BLooper* looper)
1476 	:
1477 	BHandler(),
1478 	fPath(),
1479 	fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM),
1480 	fRootDirectoryRef(),
1481 	fPackagesDirectoryRef(),
1482 	fRoot(NULL),
1483 	fListener(NULL),
1484 	fState(NULL),
1485 	fPendingNodeMonitorEventsLock("pending node monitor events"),
1486 	fPendingNodeMonitorEvents(),
1487 	fNodeMonitorEventHandleTime(0),
1488 	fPackagesToBeActivated(),
1489 	fPackagesToBeDeactivated(),
1490 	fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY)
1491 {
1492 	looper->AddHandler(this);
1493 }
1494 
1495 
1496 Volume::~Volume()
1497 {
1498 	Unmounted();
1499 		// need for error case in InitPackages()
1500 
1501 	delete fState;
1502 }
1503 
1504 
1505 status_t
1506 Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef)
1507 {
1508 	fState = new(std::nothrow) State;
1509 	if (fState == NULL || !fState->Init())
1510 		RETURN_ERROR(B_NO_MEMORY);
1511 
1512 	fRootDirectoryRef = rootDirectoryRef;
1513 
1514 	// open the root directory
1515 	BDirectory directory;
1516 	status_t error = directory.SetTo(&fRootDirectoryRef);
1517 	if (error != B_OK) {
1518 		ERROR("Volume::Init(): failed to open root directory: %s\n",
1519 			strerror(error));
1520 		RETURN_ERROR(error);
1521 	}
1522 
1523 	// get the directory path
1524 	BEntry entry;
1525 	error = directory.GetEntry(&entry);
1526 
1527 	BPath path;
1528 	if (error == B_OK)
1529 		error = entry.GetPath(&path);
1530 
1531 	if (error != B_OK) {
1532 		ERROR("Volume::Init(): failed to get root directory path: %s\n",
1533 			strerror(error));
1534 		RETURN_ERROR(error);
1535 	}
1536 
1537 	fPath = path.Path();
1538 	if (fPath.IsEmpty())
1539 		RETURN_ERROR(B_NO_MEMORY);
1540 
1541 	// get a volume info from the FS
1542 	int fd = directory.Dup();
1543 	if (fd < 0) {
1544 		ERROR("Volume::Init(): failed to get root directory FD: %s\n",
1545 			strerror(fd));
1546 		RETURN_ERROR(fd);
1547 	}
1548 	FileDescriptorCloser fdCloser(fd);
1549 
1550 	PackageFSVolumeInfo info;
1551 	if (ioctl(fd, PACKAGE_FS_OPERATION_GET_VOLUME_INFO, &info, sizeof(info))
1552 			!= 0) {
1553 		ERROR("Volume::Init(): failed to get volume info: %s\n",
1554 			strerror(errno));
1555 		RETURN_ERROR(errno);
1556 	}
1557 
1558 	fMountType = info.mountType;
1559 	fPackagesDirectoryRef.device = info.packagesDeviceID;
1560 	fPackagesDirectoryRef.node = info.packagesDirectoryID;
1561 
1562 	_packageRootRef.device = info.rootDeviceID;
1563 	_packageRootRef.node = info.rootDirectoryID;
1564 
1565 	return B_OK;
1566 }
1567 
1568 
1569 status_t
1570 Volume::InitPackages(Listener* listener)
1571 {
1572 	// node-monitor the volume's packages directory
1573 	status_t error = watch_node(&fPackagesDirectoryRef, B_WATCH_DIRECTORY,
1574 		BMessenger(this));
1575 	if (error == B_OK) {
1576 		fListener = listener;
1577 	} else {
1578 		ERROR("Volume::InitPackages(): failed to start watching the packages "
1579 			"directory of the volume at \"%s\": %s\n",
1580 			fPath.String(), strerror(error));
1581 		// Not good, but not fatal. Only the manual package operations in the
1582 		// packages directory won't work correctly.
1583 	}
1584 
1585 	// read the packages directory and get the active packages
1586 	int fd = OpenRootDirectory();
1587 	if (fd < 0) {
1588 		ERROR("Volume::InitPackages(): failed to open root directory: %s\n",
1589 			strerror(fd));
1590 		RETURN_ERROR(fd);
1591 	}
1592 	FileDescriptorCloser fdCloser(fd);
1593 
1594 	error = _ReadPackagesDirectory();
1595 	if (error != B_OK)
1596 		RETURN_ERROR(error);
1597 
1598 	error = _GetActivePackages(fd);
1599 	if (error != B_OK)
1600 		RETURN_ERROR(error);
1601 
1602 	// create the admin directory, if it doesn't exist yet
1603 	BDirectory packagesDirectory;
1604 	if (packagesDirectory.SetTo(&fPackagesDirectoryRef) == B_OK) {
1605 		if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists())
1606 			packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL);
1607 	}
1608 
1609 	return B_OK;
1610 }
1611 
1612 
1613 status_t
1614 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly)
1615 {
1616 	for (PackageFileNameHashTable::Iterator it = fState->ByFileNameIterator();
1617 			it.HasNext();) {
1618 		Package* package = it.Next();
1619 		if (activeOnly && !package->IsActive())
1620 			continue;
1621 
1622 		status_t error = repository.AddPackage(package->Info());
1623 		if (error != B_OK) {
1624 			ERROR("Volume::AddPackagesToRepository(): failed to add package %s "
1625 				"to repository: %s\n", package->FileName().String(),
1626 				strerror(error));
1627 			return error;
1628 		}
1629 	}
1630 
1631 	return B_OK;
1632 }
1633 
1634 
1635 void
1636 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume)
1637 {
1638 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume);
1639 	// create the solver
1640 	BSolver* solver;
1641 	status_t error = BSolver::Create(solver);
1642 	if (error != B_OK) {
1643 		ERROR("Volume::InitialVerify(): failed to create solver: %s\n",
1644 			strerror(error));
1645 		return;
1646 	}
1647 	ObjectDeleter<BSolver> solverDeleter(solver);
1648 
1649 	// add a repository with all active packages
1650 	BSolverRepository repository;
1651 	error = _AddRepository(solver, repository, true, true);
1652 	if (error != B_OK) {
1653 		ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
1654 			strerror(error));
1655 		return;
1656 	}
1657 
1658 	// add a repository for the next volume
1659 	BSolverRepository nextRepository;
1660 	if (nextVolume != NULL) {
1661 		nextRepository.SetPriority(1);
1662 		error = nextVolume->_AddRepository(solver, nextRepository, true, false);
1663 		if (error != B_OK) {
1664 			ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
1665 				strerror(error));
1666 			return;
1667 		}
1668 	}
1669 
1670 	// add a repository for the next next volume
1671 	BSolverRepository nextNextRepository;
1672 	if (nextNextVolume != NULL) {
1673 		nextNextRepository.SetPriority(2);
1674 		error = nextNextVolume->_AddRepository(solver, nextNextRepository, true,
1675 			false);
1676 		if (error != B_OK) {
1677 			ERROR("Volume::InitialVerify(): failed to add repository: %s\n",
1678 				strerror(error));
1679 			return;
1680 		}
1681 	}
1682 
1683 	// verify
1684 	error = solver->VerifyInstallation();
1685 	if (error != B_OK) {
1686 		ERROR("Volume::InitialVerify(): failed to verify: %s\n",
1687 			strerror(error));
1688 		return;
1689 	}
1690 
1691 	if (!solver->HasProblems()) {
1692 		INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n",
1693 			Path().String());
1694 		return;
1695 	}
1696 
1697 	// print the problems
1698 // TODO: Notify the user ...
1699 	INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n",
1700 		Path().String());
1701 
1702 	int32 problemCount = solver->CountProblems();
1703 	for (int32 i = 0; i < problemCount; i++) {
1704 		BSolverProblem* problem = solver->ProblemAt(i);
1705 		INFORM("  %" B_PRId32 ": %s\n", i + 1, problem->ToString().String());
1706 		int32 solutionCount = problem->CountSolutions();
1707 		for (int32 k = 0; k < solutionCount; k++) {
1708 			const BSolverProblemSolution* solution = problem->SolutionAt(k);
1709 			INFORM("    solution %" B_PRId32 ":\n", k + 1);
1710 			int32 elementCount = solution->CountElements();
1711 			for (int32 l = 0; l < elementCount; l++) {
1712 				const BSolverProblemSolutionElement* element
1713 					= solution->ElementAt(l);
1714 				INFORM("      - %s\n", element->ToString().String());
1715 			}
1716 		}
1717 	}
1718 }
1719 
1720 
1721 void
1722 Volume::HandleGetLocationInfoRequest(BMessage* message)
1723 {
1724 	AutoLocker<State> stateLocker(fState);
1725 
1726 	// If the cached reply message is up-to-date, just send it.
1727 	int64 changeCount;
1728 	if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK
1729 		&& changeCount == fState->ChangeCount()) {
1730 		stateLocker.Unlock();
1731 		message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
1732 			kCommunicationTimeout);
1733 		return;
1734 	}
1735 
1736 	// rebuild the reply message
1737 	fLocationInfoReply.MakeEmpty();
1738 
1739 	if (fLocationInfoReply.AddInt32("base directory device",
1740 			fRootDirectoryRef.device) != B_OK
1741 		|| fLocationInfoReply.AddInt64("base directory node",
1742 			fRootDirectoryRef.node) != B_OK
1743 		|| fLocationInfoReply.AddInt32("packages directory device",
1744 			fPackagesDirectoryRef.device) != B_OK
1745 		|| fLocationInfoReply.AddInt64("packages directory node",
1746 			fPackagesDirectoryRef.node) != B_OK) {
1747 		return;
1748 	}
1749 
1750 	for (PackageFileNameHashTable::Iterator it = fState->ByFileNameIterator();
1751 			it.HasNext();) {
1752 		Package* package = it.Next();
1753 		const char* fieldName = package->IsActive()
1754 			? "active packages" : "inactive packages";
1755 		BMessage packageArchive;
1756 		if (package->Info().Archive(&packageArchive) != B_OK
1757 			|| fLocationInfoReply.AddMessage(fieldName, &packageArchive)
1758 				!= B_OK) {
1759 			return;
1760 		}
1761 	}
1762 
1763 	if (fLocationInfoReply.AddInt64("change count", fState->ChangeCount())
1764 			!= B_OK) {
1765 		return;
1766 	}
1767 
1768 	stateLocker.Unlock();
1769 
1770 	message->SendReply(&fLocationInfoReply, (BHandler*)NULL,
1771 		kCommunicationTimeout);
1772 }
1773 
1774 
1775 void
1776 Volume::HandleCommitTransactionRequest(BMessage* message)
1777 {
1778 	// Prepare the reply in so far that we can at least set the error code
1779 	// without risk of failure.
1780 	BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY);
1781 	if (reply.AddInt32("error", B_ERROR) != B_OK)
1782 		return;
1783 
1784 	// perform the request
1785 	PackageSet dummy;
1786 	CommitTransactionHandler handler(this, dummy, dummy);
1787 	int32 error;
1788 	try {
1789 		handler.HandleRequest(message, &reply);
1790 		error = B_DAEMON_OK;
1791 	} catch (Exception& exception) {
1792 		error = exception.Error();
1793 
1794 		if (!exception.ErrorMessage().IsEmpty())
1795 			reply.AddString("error message", exception.ErrorMessage());
1796 		if (!exception.PackageName().IsEmpty())
1797 			reply.AddString("error package", exception.PackageName());
1798 	} catch (std::bad_alloc& exception) {
1799 		error = B_NO_MEMORY;
1800 	}
1801 
1802 	// revert on error
1803 	if (error != B_DAEMON_OK)
1804 		handler.Revert();
1805 
1806 	// send the reply
1807 	reply.ReplaceInt32("error", error);
1808 	message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout);
1809 }
1810 
1811 
1812 void
1813 Volume::PackageJobPending()
1814 {
1815 	fState->PackageJobPending();
1816 }
1817 
1818 
1819 void
1820 Volume::PackageJobFinished()
1821 {
1822 	fState->PackageJobFinished();
1823 }
1824 
1825 
1826 bool
1827 Volume::IsPackageJobPending() const
1828 {
1829 	return fState->IsPackageJobPending();
1830 }
1831 
1832 
1833 void
1834 Volume::Unmounted()
1835 {
1836 	if (fListener != NULL) {
1837 		stop_watching(BMessenger(this));
1838 		fListener = NULL;
1839 	}
1840 
1841 	if (BLooper* looper = Looper())
1842 		looper->RemoveHandler(this);
1843 }
1844 
1845 
1846 void
1847 Volume::MessageReceived(BMessage* message)
1848 {
1849 	switch (message->what) {
1850 		case B_NODE_MONITOR:
1851 		{
1852 			int32 opcode;
1853 			if (message->FindInt32("opcode", &opcode) != B_OK)
1854 				break;
1855 
1856 			switch (opcode) {
1857 				case B_ENTRY_CREATED:
1858 					_HandleEntryCreatedOrRemoved(message, true);
1859 					break;
1860 				case B_ENTRY_REMOVED:
1861 					_HandleEntryCreatedOrRemoved(message, false);
1862 					break;
1863 				case B_ENTRY_MOVED:
1864 					_HandleEntryMoved(message);
1865 					break;
1866 				default:
1867 					break;
1868 			}
1869 			break;
1870 		}
1871 
1872 		case kHandleNodeMonitorEvents:
1873 			if (fListener != NULL) {
1874 				if (system_time() >= fNodeMonitorEventHandleTime)
1875 					fListener->VolumeNodeMonitorEventOccurred(this);
1876 			}
1877 			break;
1878 
1879 		default:
1880 			BHandler::MessageReceived(message);
1881 			break;
1882 	}
1883 }
1884 
1885 
1886 BPackageInstallationLocation
1887 Volume::Location() const
1888 {
1889 	switch (fMountType) {
1890 		case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
1891 			return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
1892 		case PACKAGE_FS_MOUNT_TYPE_HOME:
1893 			return B_PACKAGE_INSTALLATION_LOCATION_HOME;
1894 		case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
1895 		default:
1896 			return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT;
1897 	}
1898 }
1899 
1900 
1901 PackageFileNameHashTable::Iterator
1902 Volume::PackagesByFileNameIterator() const
1903 {
1904 	return fState->ByFileNameIterator();
1905 }
1906 
1907 
1908 int
1909 Volume::OpenRootDirectory() const
1910 {
1911 	BDirectory directory;
1912 	status_t error = directory.SetTo(&fRootDirectoryRef);
1913 	if (error != B_OK) {
1914 		ERROR("Volume::OpenRootDirectory(): failed to open root directory: "
1915 			"%s\n", strerror(error));
1916 		RETURN_ERROR(error);
1917 	}
1918 
1919 	return directory.Dup();
1920 }
1921 
1922 
1923 void
1924 Volume::ProcessPendingNodeMonitorEvents()
1925 {
1926 	// get the events
1927 	NodeMonitorEventList events;
1928 	{
1929 		AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
1930 		events.MoveFrom(&fPendingNodeMonitorEvents);
1931 	}
1932 
1933 	// process them
1934 	while (NodeMonitorEvent* event = events.RemoveHead()) {
1935 		ObjectDeleter<NodeMonitorEvent> eventDeleter(event);
1936 		if (event->WasCreated())
1937 			_PackagesEntryCreated(event->EntryName());
1938 		else
1939 			_PackagesEntryRemoved(event->EntryName());
1940 	}
1941 }
1942 
1943 
1944 bool
1945 Volume::HasPendingPackageActivationChanges() const
1946 {
1947 	return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty();
1948 }
1949 
1950 
1951 void
1952 Volume::ProcessPendingPackageActivationChanges()
1953 {
1954 	if (!HasPendingPackageActivationChanges())
1955 		return;
1956 
1957 	// perform the request
1958 	CommitTransactionHandler handler(this, fPackagesToBeActivated,
1959 		fPackagesToBeDeactivated);
1960 	int32 error;
1961 	try {
1962 		handler.HandleRequest(fPackagesToBeActivated, fPackagesToBeDeactivated);
1963 		error = B_DAEMON_OK;
1964 	} catch (Exception& exception) {
1965 		error = exception.Error();
1966 		ERROR("Volume::ProcessPendingPackageActivationChanges(): package "
1967 			"activation failed: %s\n", exception.ToString().String());
1968 // TODO: Notify the user!
1969 	} catch (std::bad_alloc& exception) {
1970 		error = B_NO_MEMORY;
1971 		ERROR("Volume::ProcessPendingPackageActivationChanges(): package "
1972 			"activation failed: out of memory\n");
1973 // TODO: Notify the user!
1974 	}
1975 
1976 	// revert on error
1977 	if (error != B_DAEMON_OK)
1978 		handler.Revert();
1979 
1980 	// clear the activation/deactivation sets in any event
1981 	fPackagesToBeActivated.clear();
1982 	fPackagesToBeDeactivated.clear();
1983 }
1984 
1985 
1986 void
1987 Volume::ClearPackageActivationChanges()
1988 {
1989 	fPackagesToBeActivated.clear();
1990 	fPackagesToBeDeactivated.clear();
1991 }
1992 
1993 
1994 status_t
1995 Volume::CreateTransaction(BPackageInstallationLocation location,
1996 	BActivationTransaction& _transaction, BDirectory& _transactionDirectory)
1997 {
1998 	// open admin directory
1999 	BDirectory adminDirectory;
2000 	status_t error = _OpenPackagesSubDirectory(
2001 		RelativePath(kAdminDirectoryName), true, adminDirectory);
2002 	if (error != B_OK)
2003 		return error;
2004 
2005 	// create a transaction directory
2006 	int uniqueId = 1;
2007 	BString directoryName;
2008 	for (;; uniqueId++) {
2009 		directoryName.SetToFormat("transaction-%d", uniqueId);
2010 		if (directoryName.IsEmpty())
2011 			return B_NO_MEMORY;
2012 
2013 		error = adminDirectory.CreateDirectory(directoryName,
2014 			&_transactionDirectory);
2015 		if (error == B_OK)
2016 			break;
2017 		if (error != B_FILE_EXISTS)
2018 			return error;
2019 	}
2020 
2021 	// init the transaction
2022 	error = _transaction.SetTo(location, fState->ChangeCount(), directoryName);
2023 	if (error != B_OK) {
2024 		BEntry entry;
2025 		_transactionDirectory.GetEntry(&entry);
2026 		_transactionDirectory.Unset();
2027 		if (entry.InitCheck() == B_OK)
2028 			entry.Remove();
2029 		return error;
2030 	}
2031 
2032 	return B_OK;
2033 }
2034 
2035 
2036 void
2037 Volume::CommitTransaction(const BActivationTransaction& transaction,
2038 	const PackageSet& packagesAlreadyAdded,
2039 	const PackageSet& packagesAlreadyRemoved,
2040 	BDaemonClient::BCommitTransactionResult& _result)
2041 {
2042 	// perform the request
2043 	CommitTransactionHandler handler(this, packagesAlreadyAdded,
2044 		packagesAlreadyRemoved);
2045 	int32 error;
2046 	try {
2047 		handler.HandleRequest(transaction, NULL);
2048 		error = B_DAEMON_OK;
2049 		_result.SetTo(error, BString(), BString(),
2050 			handler.OldStateDirectoryName());
2051 	} catch (Exception& exception) {
2052 		error = exception.Error();
2053 		_result.SetTo(error, exception.ErrorMessage(), exception.PackageName(),
2054 			BString());
2055 	} catch (std::bad_alloc& exception) {
2056 		error = B_NO_MEMORY;
2057 		_result.SetTo(error, BString(), BString(), BString());
2058 	}
2059 
2060 	// revert on error
2061 	if (error != B_DAEMON_OK)
2062 		handler.Revert();
2063 }
2064 
2065 
2066 void
2067 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created)
2068 {
2069 	// only moves to or from our packages directory are interesting
2070 	int32 deviceID;
2071 	int64 directoryID;
2072 	const char* name;
2073 	if (message->FindInt32("device", &deviceID) != B_OK
2074 		|| message->FindInt64("directory", &directoryID) != B_OK
2075 		|| message->FindString("name", &name) != B_OK
2076 		|| node_ref(deviceID, directoryID) != fPackagesDirectoryRef) {
2077 		return;
2078 	}
2079 
2080 	_QueueNodeMonitorEvent(name, created);
2081 }
2082 
2083 
2084 void
2085 Volume::_HandleEntryMoved(const BMessage* message)
2086 {
2087 	int32 deviceID;
2088 	int64 fromDirectoryID;
2089 	int64 toDirectoryID;
2090 	const char* fromName;
2091 	const char* toName;
2092 	if (message->FindInt32("device", &deviceID) != B_OK
2093 		|| message->FindInt64("from directory", &fromDirectoryID) != B_OK
2094 		|| message->FindInt64("to directory", &toDirectoryID) != B_OK
2095 		|| message->FindString("from name", &fromName) != B_OK
2096 		|| message->FindString("name", &toName) != B_OK
2097 		|| deviceID != fPackagesDirectoryRef.device
2098 		|| (fromDirectoryID != fPackagesDirectoryRef.node
2099 			&& toDirectoryID != fPackagesDirectoryRef.node)) {
2100 		return;
2101 	}
2102 
2103 	AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
2104 		// make sure for a move the two events cannot get split
2105 
2106 	if (fromDirectoryID == fPackagesDirectoryRef.node)
2107 		_QueueNodeMonitorEvent(fromName, false);
2108 	if (toDirectoryID == fPackagesDirectoryRef.node)
2109 		_QueueNodeMonitorEvent(toName, true);
2110 }
2111 
2112 
2113 void
2114 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated)
2115 {
2116 	if (name.IsEmpty()) {
2117 		ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n");
2118 		return;
2119 	}
2120 
2121 	// ignore entries that don't have the ".hpkg" extension
2122 	if (!name.EndsWith(kPackageFileNameExtension))
2123 		return;
2124 
2125 	NodeMonitorEvent* event
2126 		= new(std::nothrow) NodeMonitorEvent(name, wasCreated);
2127 	if (event == NULL) {
2128 		ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n");
2129 		return;
2130 	}
2131 
2132 	AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock);
2133 	fPendingNodeMonitorEvents.Add(event);
2134 	eventsLock.Unlock();
2135 
2136 	fNodeMonitorEventHandleTime
2137 		= system_time() + kNodeMonitorEventHandlingDelay;
2138 	BMessage message(kHandleNodeMonitorEvents);
2139 	BMessageRunner::StartSending(this, &message, kNodeMonitorEventHandlingDelay,
2140 		1);
2141 }
2142 
2143 
2144 void
2145 Volume::_PackagesEntryCreated(const char* name)
2146 {
2147 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name);
2148 	// Ignore the event, if the package is already known.
2149 	Package* package = fState->FindPackage(name);
2150 	if (package != NULL) {
2151 		if (package->EntryCreatedIgnoreLevel() > 0) {
2152 			package->DecrementEntryCreatedIgnoreLevel();
2153 		} else {
2154 			WARN("node monitoring created event for already known entry "
2155 				"\"%s\"\n", name);
2156 		}
2157 
2158 		return;
2159 	}
2160 
2161 	entry_ref entry;
2162 	entry.device = fPackagesDirectoryRef.device;
2163 	entry.directory = fPackagesDirectoryRef.node;
2164 	status_t error = entry.set_name(name);
2165 	if (error != B_OK) {
2166 		ERROR("out of memory\n");
2167 		return;
2168 	}
2169 
2170 	package = new(std::nothrow) Package;
2171 	if (package == NULL) {
2172 		ERROR("out of memory\n");
2173 		return;
2174 	}
2175 	ObjectDeleter<Package> packageDeleter(package);
2176 
2177 	error = package->Init(entry);
2178 	if (error != B_OK) {
2179 		ERROR("failed to init package for file \"%s\"\n", name);
2180 		return;
2181 	}
2182 
2183 	_AddPackage(package);
2184 	packageDeleter.Detach();
2185 
2186 	try {
2187 		fPackagesToBeActivated.insert(package);
2188 	} catch (std::bad_alloc& exception) {
2189 		ERROR("out of memory\n");
2190 		return;
2191 	}
2192 }
2193 
2194 
2195 void
2196 Volume::_PackagesEntryRemoved(const char* name)
2197 {
2198 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name);
2199 	Package* package = fState->FindPackage(name);
2200 	if (package == NULL)
2201 		return;
2202 
2203 	// Ignore the event, if we generated it ourselves.
2204 	if (package->EntryRemovedIgnoreLevel() > 0) {
2205 		package->DecrementEntryRemovedIgnoreLevel();
2206 		return;
2207 	}
2208 
2209 	// Remove the package from the packages-to-be-activated set, if it is in
2210 	// there (unlikely, unless we see a create-remove-create sequence).
2211 	PackageSet::iterator it = fPackagesToBeActivated.find(package);
2212 	if (it != fPackagesToBeActivated.end())
2213 		fPackagesToBeActivated.erase(it);
2214 
2215 	// If the package isn't active, just remove it for good.
2216 	if (!package->IsActive()) {
2217 		_RemovePackage(package);
2218 		delete package;
2219 		return;
2220 	}
2221 
2222 	// The package must be deactivated.
2223 	try {
2224 		fPackagesToBeDeactivated.insert(package);
2225 	} catch (std::bad_alloc& exception) {
2226 		ERROR("out of memory\n");
2227 		return;
2228 	}
2229 }
2230 
2231 
2232 void
2233 Volume::_FillInActivationChangeItem(PackageFSActivationChangeItem* item,
2234 	PackageFSActivationChangeType type, Package* package, char*& nameBuffer)
2235 {
2236 	item->type = type;
2237 	item->packageDeviceID = package->NodeRef().device;
2238 	item->packageNodeID = package->NodeRef().node;
2239 	item->nameLength = package->FileName().Length();
2240 	item->parentDeviceID = fPackagesDirectoryRef.device;
2241 	item->parentDirectoryID = fPackagesDirectoryRef.node;
2242 	item->name = nameBuffer;
2243 	strcpy(nameBuffer, package->FileName());
2244 	nameBuffer += package->FileName().Length() + 1;
2245 }
2246 
2247 
2248 void
2249 Volume::_AddPackage(Package* package)
2250 {
2251 	fState->AddPackage(package);
2252 }
2253 
2254 void
2255 Volume::_RemovePackage(Package* package)
2256 {
2257 	fState->RemovePackage(package);
2258 }
2259 
2260 
2261 status_t
2262 Volume::_ReadPackagesDirectory()
2263 {
2264 	BDirectory directory;
2265 	status_t error = directory.SetTo(&fPackagesDirectoryRef);
2266 	if (error != B_OK) {
2267 		ERROR("Volume::_ReadPackagesDirectory(): failed to open packages "
2268 			"directory: %s\n", strerror(error));
2269 		RETURN_ERROR(error);
2270 	}
2271 
2272 	entry_ref entry;
2273 	while (directory.GetNextRef(&entry) == B_OK) {
2274 		if (!BString(entry.name).EndsWith(kPackageFileNameExtension))
2275 			continue;
2276 
2277 		Package* package = new(std::nothrow) Package;
2278 		if (package == NULL)
2279 			RETURN_ERROR(B_NO_MEMORY);
2280 		ObjectDeleter<Package> packageDeleter(package);
2281 
2282 		status_t error = package->Init(entry);
2283 		if (error == B_OK) {
2284 			_AddPackage(package);
2285 			packageDeleter.Detach();
2286 		}
2287 	}
2288 
2289 	return B_OK;
2290 }
2291 
2292 
2293 status_t
2294 Volume::_GetActivePackages(int fd)
2295 {
2296 	uint32 maxPackageCount = 16 * 1024;
2297 	PackageFSGetPackageInfosRequest* request = NULL;
2298 	MemoryDeleter requestDeleter;
2299 	size_t bufferSize;
2300 	for (;;) {
2301 		bufferSize = sizeof(PackageFSGetPackageInfosRequest)
2302 			+ (maxPackageCount - 1) * sizeof(PackageFSPackageInfo);
2303 		request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize);
2304 		if (request == NULL)
2305 			RETURN_ERROR(B_NO_MEMORY);
2306 		requestDeleter.SetTo(request);
2307 
2308 		if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request,
2309 				bufferSize) != 0) {
2310 			ERROR("Volume::_GetActivePackages(): failed to get active package "
2311 				"info from package FS: %s\n", strerror(errno));
2312 			RETURN_ERROR(errno);
2313 		}
2314 
2315 		if (request->packageCount <= maxPackageCount)
2316 			break;
2317 
2318 		maxPackageCount = request->packageCount;
2319 		requestDeleter.Unset();
2320 	}
2321 
2322 	// mark the returned packages active
2323 	for (uint32 i = 0; i < request->packageCount; i++) {
2324 		Package* package = fState->FindPackage(
2325 			node_ref(request->infos[i].packageDeviceID,
2326 				request->infos[i].packageNodeID));
2327 		if (package == NULL) {
2328 			WARN("active package (dev: %" B_PRIdDEV ", node: %" B_PRIdINO ") "
2329 				"not found in package directory\n",
2330 				request->infos[i].packageDeviceID,
2331 				request->infos[i].packageNodeID);
2332 // TODO: Deactivate the package right away?
2333 			continue;
2334 		}
2335 
2336 		fState->SetPackageActive(package, true);
2337 INFORM("active package: \"%s\"\n", package->FileName().String());
2338 	}
2339 
2340 for (PackageNodeRefHashTable::Iterator it = fState->ByNodeRefIterator();
2341 	it.HasNext();) {
2342 	Package* package = it.Next();
2343 	if (!package->IsActive())
2344 		INFORM("inactive package: \"%s\"\n", package->FileName().String());
2345 }
2346 
2347 // INFORM("%" B_PRIu32 " active packages:\n", request->packageCount);
2348 // for (uint32 i = 0; i < request->packageCount; i++) {
2349 // 	INFORM("  dev: %" B_PRIdDEV ", node: %" B_PRIdINO "\n",
2350 // 		request->infos[i].packageDeviceID, request->infos[i].packageNodeID);
2351 // }
2352 
2353 	return B_OK;
2354 }
2355 
2356 
2357 status_t
2358 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository,
2359 	bool activeOnly, bool installed)
2360 {
2361 	status_t error = repository.SetTo(Path());
2362 	if (error != B_OK) {
2363 		ERROR("Volume::_AddRepository(): failed to init repository: %s\n",
2364 			strerror(error));
2365 		return error;
2366 	}
2367 
2368 	repository.SetInstalled(installed);
2369 
2370 	error = AddPackagesToRepository(repository, true);
2371 	if (error != B_OK) {
2372 		ERROR("Volume::_AddRepository(): failed to add packages to "
2373 			"repository: %s\n", strerror(error));
2374 		return error;
2375 	}
2376 
2377 	error = solver->AddRepository(&repository);
2378 	if (error != B_OK) {
2379 		ERROR("Volume::_AddRepository(): failed to add repository to solver: "
2380 			"%s\n", strerror(error));
2381 		return error;
2382 	}
2383 
2384 	return B_OK;
2385 }
2386 
2387 
2388 status_t
2389 Volume::_OpenPackagesFile(const RelativePath& subDirectoryPath,
2390 	const char* fileName, uint32 openMode, BFile& _file, BEntry* _entry)
2391 {
2392 	BDirectory directory;
2393 	if (!subDirectoryPath.IsEmpty()) {
2394 		status_t error = _OpenPackagesSubDirectory(subDirectoryPath,
2395 			(openMode & B_CREATE_FILE) != 0, directory);
2396 		if (error != B_OK) {
2397 			ERROR("Volume::_OpenPackagesFile(): failed to open packages "
2398 				"subdirectory \"%s\": %s\n",
2399 				subDirectoryPath.ToString().String(), strerror(error));
2400 			RETURN_ERROR(error);
2401 		}
2402 	} else {
2403 		status_t error = directory.SetTo(&fPackagesDirectoryRef);
2404 		if (error != B_OK) {
2405 			ERROR("Volume::_OpenPackagesFile(): failed to open packages "
2406 				"directory: %s\n", strerror(error));
2407 			RETURN_ERROR(error);
2408 		}
2409 	}
2410 
2411 	BEntry stackEntry;
2412 	BEntry& entry = _entry != NULL ? *_entry : stackEntry;
2413 	status_t error = entry.SetTo(&directory, fileName);
2414 	if (error != B_OK) {
2415 		ERROR("Volume::_OpenPackagesFile(): failed to get entry for file: %s",
2416 			strerror(error));
2417 		RETURN_ERROR(error);
2418 	}
2419 
2420 	return _file.SetTo(&entry, openMode);
2421 }
2422 
2423 
2424 status_t
2425 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create,
2426 	BDirectory& _directory)
2427 {
2428 	// open the packages directory
2429 	BDirectory directory;
2430 	status_t error = directory.SetTo(&fPackagesDirectoryRef);
2431 	if (error != B_OK) {
2432 		ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages "
2433 			"directory: %s\n", strerror(error));
2434 		RETURN_ERROR(error);
2435 	}
2436 
2437 	return FSUtils::OpenSubDirectory(directory, path, create, _directory);
2438 }
2439 
2440 
2441 status_t
2442 Volume::_CreateActivationFileContent(const PackageSet& toActivate,
2443 	const PackageSet& toDeactivate, BString& _content)
2444 {
2445 	BString activationFileContent;
2446 	for (PackageFileNameHashTable::Iterator it = fState->ByFileNameIterator();
2447 			it.HasNext();) {
2448 		Package* package = it.Next();
2449 		if (package->IsActive()
2450 			&& toDeactivate.find(package) == toDeactivate.end()) {
2451 			int32 length = activationFileContent.Length();
2452 			activationFileContent << package->FileName() << '\n';
2453 			if (activationFileContent.Length()
2454 					< length + package->FileName().Length() + 1) {
2455 				return B_NO_MEMORY;
2456 			}
2457 		}
2458 	}
2459 
2460 	for (PackageSet::const_iterator it = toActivate.begin();
2461 		it != toActivate.end(); ++it) {
2462 		Package* package = *it;
2463 		int32 length = activationFileContent.Length();
2464 		activationFileContent << package->FileName() << '\n';
2465 		if (activationFileContent.Length()
2466 				< length + package->FileName().Length() + 1) {
2467 			return B_NO_MEMORY;
2468 		}
2469 	}
2470 
2471 	_content = activationFileContent;
2472 	return B_OK;
2473 }
2474 
2475 
2476 status_t
2477 Volume::_WriteActivationFile(const RelativePath& directoryPath,
2478 	const char* fileName, const PackageSet& toActivate,
2479 	const PackageSet& toDeactivate,
2480 	BEntry& _entry)
2481 {
2482 	// create the content
2483 	BString activationFileContent;
2484 	status_t error = _CreateActivationFileContent(toActivate, toDeactivate,
2485 		activationFileContent);
2486 	if (error != B_OK)
2487 		return error;
2488 
2489 	// write the file
2490 	error = _WriteTextFile(directoryPath, fileName, activationFileContent,
2491 		_entry);
2492 	if (error != B_OK) {
2493 		ERROR("Volume::_WriteActivationFile(): failed to write activation "
2494 			"file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName,
2495 			strerror(error));
2496 		return error;
2497 	}
2498 
2499 	return B_OK;
2500 }
2501 
2502 
2503 status_t
2504 Volume::_WriteTextFile(const RelativePath& directoryPath, const char* fileName,
2505 	const BString& content, BEntry& _entry)
2506 {
2507 	BFile file;
2508 	status_t error = _OpenPackagesFile(directoryPath,
2509 		fileName, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE, file, &_entry);
2510 	if (error != B_OK) {
2511 		ERROR("Volume::_WriteTextFile(): failed to create file \"%s/%s\": %s\n",
2512 			directoryPath.ToString().String(), fileName, strerror(error));
2513 		return error;
2514 	}
2515 
2516 	ssize_t bytesWritten = file.Write(content.String(),
2517 		content.Length());
2518 	if (bytesWritten < 0) {
2519 		ERROR("Volume::_WriteTextFile(): failed to write file \"%s/%s\": %s\n",
2520 			directoryPath.ToString().String(), fileName,
2521 			strerror(bytesWritten));
2522 		return bytesWritten;
2523 	}
2524 
2525 	return B_OK;
2526 }
2527 
2528 
2529 void
2530 Volume::_ChangePackageActivation(const PackageSet& packagesToActivate,
2531 	const PackageSet& packagesToDeactivate)
2532 {
2533 INFORM("Volume::_ChangePackageActivation(): activating %zu, deactivating %zu packages\n",
2534 packagesToActivate.size(), packagesToDeactivate.size());
2535 
2536 	// write the temporary package activation file
2537 	BEntry activationFileEntry;
2538 	status_t error = _WriteActivationFile(RelativePath(kAdminDirectoryName),
2539 		kTemporaryActivationFileName, packagesToActivate, packagesToDeactivate,
2540 		activationFileEntry);
2541 	if (error != B_OK)
2542 		throw Exception(error, "failed to write activation file");
2543 
2544 	// compute the size of the allocation we need for the activation change
2545 	// request
2546 	int32 itemCount = packagesToActivate.size() + packagesToDeactivate.size();
2547 	size_t requestSize = sizeof(PackageFSActivationChangeRequest)
2548 		+ itemCount * sizeof(PackageFSActivationChangeItem);
2549 
2550 	for (PackageSet::iterator it = packagesToActivate.begin();
2551 		 it != packagesToActivate.end(); ++it) {
2552 		requestSize += (*it)->FileName().Length() + 1;
2553 	}
2554 
2555 	for (PackageSet::iterator it = packagesToDeactivate.begin();
2556 		 it != packagesToDeactivate.end(); ++it) {
2557 		requestSize += (*it)->FileName().Length() + 1;
2558 	}
2559 
2560 	// allocate and prepare the request
2561 	PackageFSActivationChangeRequest* request
2562 		= (PackageFSActivationChangeRequest*)malloc(requestSize);
2563 	if (request == NULL)
2564 		throw Exception(B_NO_MEMORY);
2565 	MemoryDeleter requestDeleter(request);
2566 
2567 	request->itemCount = itemCount;
2568 
2569 	PackageFSActivationChangeItem* item = &request->items[0];
2570 	char* nameBuffer = (char*)(item + itemCount);
2571 
2572 	for (PackageSet::iterator it = packagesToActivate.begin();
2573 		it != packagesToActivate.end(); ++it, item++) {
2574 		_FillInActivationChangeItem(item, PACKAGE_FS_ACTIVATE_PACKAGE, *it,
2575 			nameBuffer);
2576 	}
2577 
2578 	for (PackageSet::iterator it = packagesToDeactivate.begin();
2579 		it != packagesToDeactivate.end(); ++it, item++) {
2580 		_FillInActivationChangeItem(item, PACKAGE_FS_DEACTIVATE_PACKAGE, *it,
2581 			nameBuffer);
2582 	}
2583 
2584 	// issue the request
2585 	int fd = OpenRootDirectory();
2586 	if (fd < 0)
2587 		throw Exception(fd, "failed to open root directory");
2588 	FileDescriptorCloser fdCloser(fd);
2589 
2590 	if (ioctl(fd, PACKAGE_FS_OPERATION_CHANGE_ACTIVATION, request, requestSize)
2591 			!= 0) {
2592 // TODO: We need more error information and error handling!
2593 		throw Exception(errno, "ioctl() to de-/activate packages failed");
2594 	}
2595 
2596 	// rename the temporary activation file to the final file
2597 	error = activationFileEntry.Rename(kActivationFileName, true);
2598 	if (error != B_OK) {
2599 		throw Exception(error,
2600 			"failed to rename temporary activation file to final file");
2601 // TODO: We should probably try to reverse the activation changes, though that
2602 // will fail, if this method has been called in response to node monitoring
2603 // events. Alternatively moving the package activation file could be made part
2604 // of the ioctl(), since packagefs should be able to undo package changes until
2605 // the very end, unless running out of memory. In the end the situation would be
2606 // bad anyway, though, since the activation file may refer to removed packages
2607 // and things would be in an inconsistent state after rebooting.
2608 	}
2609 
2610 	// Update our state, i.e. remove deactivated packages and mark activated
2611 	// packages accordingly.
2612 	fState->ActivationChanged(packagesToActivate, packagesToDeactivate);
2613 }
2614