xref: /haiku/src/add-ons/kernel/file_systems/packagefs/volume/Volume.cpp (revision 040a81419dda83d1014e9dc94936a4cb3f027303)
1 /*
2  * Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "Volume.h"
8 
9 #include <dirent.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <string.h>
13 #include <sys/param.h>
14 #include <sys/stat.h>
15 
16 #include <new>
17 
18 #include <AppDefs.h>
19 #include <driver_settings.h>
20 #include <KernelExport.h>
21 #include <NodeMonitor.h>
22 #include <package/PackageInfoAttributes.h>
23 
24 #include <AutoDeleter.h>
25 #include <PackagesDirectoryDefs.h>
26 
27 #include <fs/KPath.h>
28 #include <team.h>
29 #include <vfs.h>
30 
31 #include "AttributeIndex.h"
32 #include "DebugSupport.h"
33 #include "kernel_interface.h"
34 #include "LastModifiedIndex.h"
35 #include "NameIndex.h"
36 #include "OldUnpackingNodeAttributes.h"
37 #include "PackageFSRoot.h"
38 #include "PackageLinkDirectory.h"
39 #include "PackageLinksDirectory.h"
40 #include "Resolvable.h"
41 #include "SizeIndex.h"
42 #include "UnpackingLeafNode.h"
43 #include "UnpackingDirectory.h"
44 #include "Utils.h"
45 #include "Version.h"
46 
47 
48 // node ID of the root directory
49 static const ino_t kRootDirectoryID = 1;
50 
51 static const uint32 kAllStatFields = B_STAT_MODE | B_STAT_UID | B_STAT_GID
52 	| B_STAT_SIZE | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
53 	| B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME;
54 
55 // shine-through directories
56 const char* const kSystemShineThroughDirectories[] = {
57 	"packages", NULL
58 };
59 const char* const kCommonShineThroughDirectories[] = {
60 	"cache", "non-packaged", "packages", "settings", "var", NULL
61 };
62 const char* const* kHomeShineThroughDirectories
63 	= kCommonShineThroughDirectories;
64 
65 // sanity limit for activation change request
66 const size_t kMaxActivationRequestSize = 10 * 1024 * 1024;
67 
68 // sanity limit for activation file size
69 const size_t kMaxActivationFileSize = 10 * 1024 * 1024;
70 
71 static const char* const kActivationFilePath
72 	= PACKAGES_DIRECTORY_ADMIN_DIRECTORY "/"
73 		PACKAGES_DIRECTORY_ACTIVATION_FILE;
74 
75 
76 // #pragma mark - ShineThroughDirectory
77 
78 
79 struct Volume::PackagesDirectory {
80 	PackagesDirectory()
81 		:
82 		fPath(NULL),
83 		fDirFD(-1)
84 	{
85 	}
86 
87 
88 	~PackagesDirectory()
89 	{
90 		if (fDirFD >= 0)
91 			close(fDirFD);
92 
93 		free(fPath);
94 	}
95 
96 	const char* Path() const
97 	{
98 		return fPath;
99 	}
100 
101 	int DirectoryFD() const
102 	{
103 		return fDirFD;
104 	}
105 
106 	dev_t DeviceID() const
107 	{
108 		return fDeviceID;
109 	}
110 
111 	ino_t NodeID() const
112 	{
113 		return fNodeID;
114 	}
115 
116 	status_t Init(const char* path, dev_t mountPointDeviceID,
117 		ino_t mountPointNodeID, struct stat& _st)
118 	{
119 		// Open the directory. We want the path be interpreted depending on from
120 		// where it came (kernel or userland), but we always want a FD in the
121 		// kernel I/O context. There's no VFS service method to do that for us,
122 		// so we need to do that ourselves.
123 		bool calledFromKernel
124 			= team_get_current_team_id() == team_get_kernel_team_id();
125 			// Not entirely correct, but good enough for now. The only
126 			// alternative is to have that information passed in as well.
127 
128 		struct vnode* vnode;
129 		status_t error;
130 		if (path != NULL) {
131 			error = vfs_get_vnode_from_path(path, calledFromKernel, &vnode);
132 		} else {
133 			// No path given -- use the "packages" directory at our mount point.
134 			error = vfs_entry_ref_to_vnode(mountPointDeviceID, mountPointNodeID,
135 				"packages", &vnode);
136 		}
137 		if (error != B_OK) {
138 			ERROR("Failed to open package domain \"%s\"\n", strerror(error));
139 			RETURN_ERROR(error);
140 		}
141 
142 		fDirFD = vfs_open_vnode(vnode, O_RDONLY, true);
143 
144 		if (fDirFD < 0) {
145 			ERROR("Failed to open package domain \"%s\"\n", strerror(fDirFD));
146 			vfs_put_vnode(vnode);
147 			RETURN_ERROR(fDirFD);
148 		}
149 		// Our vnode reference has been transferred to the FD.
150 
151 		// Is it a directory at all?
152 		struct stat& st = _st;
153 		if (fstat(fDirFD, &st) < 0)
154 			RETURN_ERROR(errno);
155 
156 		fDeviceID = st.st_dev;
157 		fNodeID = st.st_ino;
158 
159 		// get a normalized path
160 		KPath normalizedPath;
161 		if (normalizedPath.InitCheck() != B_OK)
162 			RETURN_ERROR(normalizedPath.InitCheck());
163 
164 		char* normalizedPathBuffer = normalizedPath.LockBuffer();
165 		error = vfs_entry_ref_to_path(fDeviceID, fNodeID, NULL, true,
166 			normalizedPathBuffer, normalizedPath.BufferSize());
167 		if (error != B_OK)
168 			RETURN_ERROR(error);
169 
170 		fPath = strdup(normalizedPathBuffer);
171 		if (fPath == NULL)
172 			RETURN_ERROR(B_NO_MEMORY);
173 
174 		return B_OK;
175 	}
176 
177 private:
178 	char*	fPath;
179 	int		fDirFD;
180 	dev_t	fDeviceID;
181 	ino_t	fNodeID;
182 };
183 
184 
185 // #pragma mark - ShineThroughDirectory
186 
187 
188 struct Volume::ShineThroughDirectory : public Directory {
189 	ShineThroughDirectory(ino_t id)
190 		:
191 		Directory(id)
192 	{
193 		get_real_time(fModifiedTime);
194 	}
195 
196 	virtual timespec ModifiedTime() const
197 	{
198 		return fModifiedTime;
199 	}
200 
201 private:
202 	timespec	fModifiedTime;
203 };
204 
205 
206 // #pragma mark - ActivationChangeRequest
207 
208 
209 struct Volume::ActivationChangeRequest {
210 public:
211 	ActivationChangeRequest()
212 		:
213 		fRequest(NULL),
214 		fRequestSize(0)
215 	{
216 	}
217 
218 	~ActivationChangeRequest()
219 	{
220 		free(fRequest);
221 	}
222 
223 	status_t Init(const void* userRequest, size_t requestSize)
224 	{
225 		// copy request to kernel
226 		if (requestSize > kMaxActivationRequestSize)
227 			RETURN_ERROR(B_BAD_VALUE);
228 
229 		fRequest = (PackageFSActivationChangeRequest*)malloc(requestSize);
230 		if (fRequest == NULL)
231 			RETURN_ERROR(B_NO_MEMORY);
232 		fRequestSize = requestSize;
233 
234 		status_t error = user_memcpy(fRequest, userRequest, fRequestSize);
235 		if (error != B_OK)
236 			RETURN_ERROR(error);
237 
238 		uint32 itemCount = fRequest->itemCount;
239 		const char* requestEnd = (const char*)fRequest + requestSize;
240 		if (&fRequest->items[itemCount] > (void*)requestEnd)
241 			RETURN_ERROR(B_BAD_VALUE);
242 
243 		// adjust the item name pointers and check their validity
244 		addr_t nameDelta = (addr_t)fRequest - (addr_t)userRequest;
245 		for (uint32 i = 0; i < itemCount; i++) {
246 			PackageFSActivationChangeItem& item = fRequest->items[i];
247 			item.name += nameDelta;
248 			if (item.name < (char*)fRequest || item.name >= requestEnd)
249 				RETURN_ERROR(B_BAD_VALUE);
250 			size_t maxNameSize = requestEnd - item.name;
251 			if (strnlen(item.name, maxNameSize) == maxNameSize)
252 				RETURN_ERROR(B_BAD_VALUE);
253 		}
254 
255 		return B_OK;
256 	}
257 
258 	uint32 CountItems() const
259 	{
260 		return fRequest->itemCount;
261 	}
262 
263 	PackageFSActivationChangeItem* ItemAt(uint32 index) const
264 	{
265 		return index < CountItems() ? &fRequest->items[index] : NULL;
266 	}
267 
268 private:
269 	PackageFSActivationChangeRequest*	fRequest;
270 	size_t								fRequestSize;
271 };
272 
273 
274 // #pragma mark - Volume
275 
276 
277 Volume::Volume(fs_volume* fsVolume)
278 	:
279 	fFSVolume(fsVolume),
280 	fRootDirectory(NULL),
281 	fPackageFSRoot(NULL),
282 	fPackagesDirectory(NULL),
283 	fNextNodeID(kRootDirectoryID + 1)
284 {
285 	rw_lock_init(&fLock, "packagefs volume");
286 }
287 
288 
289 Volume::~Volume()
290 {
291 	// remove the packages from the node tree
292 	{
293 		VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
294 		VolumeWriteLocker volumeLocker(this);
295 		for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
296 			Package* package = it.Next();) {
297 			_RemovePackageContent(package, NULL, false);
298 		}
299 	}
300 
301 	// delete the packages
302 	_RemoveAllPackages();
303 
304 	// delete all indices
305 	Index* index = fIndices.Clear(true);
306 	while (index != NULL) {
307 		Index* next = index->IndexHashLink();
308 		delete index;
309 		index = next;
310 	}
311 
312 	// remove all nodes from the ID hash table
313 	Node* node = fNodes.Clear(true);
314 	while (node != NULL) {
315 		Node* next = node->IDHashTableNext();
316 		node->ReleaseReference();
317 		node = next;
318 	}
319 
320 	if (fPackageFSRoot != NULL) {
321 		if (this == fPackageFSRoot->SystemVolume())
322 			_RemovePackageLinksDirectory();
323 
324 		fPackageFSRoot->UnregisterVolume(this);
325 	}
326 
327 	if (fRootDirectory != NULL)
328 		fRootDirectory->ReleaseReference();
329 
330 	rw_lock_destroy(&fLock);
331 }
332 
333 
334 int
335 Volume::PackagesDirectoryFD() const
336 {
337 	return fPackagesDirectory->DirectoryFD();
338 }
339 
340 
341 status_t
342 Volume::Mount(const char* parameterString)
343 {
344 	// init the hash tables
345 	status_t error = fNodes.Init();
346 	if (error != B_OK)
347 		RETURN_ERROR(error);
348 
349 	error = fNodeListeners.Init();
350 	if (error != B_OK)
351 		RETURN_ERROR(error);
352 
353 	error = fPackages.Init();
354 	if (error != B_OK)
355 		RETURN_ERROR(error);
356 
357 	error = fIndices.Init();
358 	if (error != B_OK)
359 		RETURN_ERROR(error);
360 
361 	// create the name index
362 	{
363 		NameIndex* index = new(std::nothrow) NameIndex;
364 		if (index == NULL)
365 			RETURN_ERROR(B_NO_MEMORY);
366 
367 		error = index->Init(this);
368 		if (error != B_OK) {
369 			delete index;
370 			RETURN_ERROR(error);
371 		}
372 
373 		fIndices.Insert(index);
374 	}
375 
376 	// create the size index
377 	{
378 		SizeIndex* index = new(std::nothrow) SizeIndex;
379 		if (index == NULL)
380 			RETURN_ERROR(B_NO_MEMORY);
381 
382 		error = index->Init(this);
383 		if (error != B_OK) {
384 			delete index;
385 			RETURN_ERROR(error);
386 		}
387 
388 		fIndices.Insert(index);
389 	}
390 
391 	// create the last modified index
392 	{
393 		LastModifiedIndex* index = new(std::nothrow) LastModifiedIndex;
394 		if (index == NULL)
395 			RETURN_ERROR(B_NO_MEMORY);
396 
397 		error = index->Init(this);
398 		if (error != B_OK) {
399 			delete index;
400 			RETURN_ERROR(error);
401 		}
402 
403 		fIndices.Insert(index);
404 	}
405 
406 	// create a BEOS:APP_SIG index
407 	{
408 		AttributeIndex* index = new(std::nothrow) AttributeIndex;
409 		if (index == NULL)
410 			RETURN_ERROR(B_NO_MEMORY);
411 
412 		error = index->Init(this, "BEOS:APP_SIG", B_MIME_STRING_TYPE, 0);
413 		if (error != B_OK) {
414 			delete index;
415 			RETURN_ERROR(error);
416 		}
417 
418 		fIndices.Insert(index);
419 	}
420 
421 	// get the mount parameters
422 	const char* packages = NULL;
423 	const char* volumeName = NULL;
424 	const char* mountType = NULL;
425 	const char* shineThrough = NULL;
426 
427 	void* parameterHandle = parse_driver_settings_string(parameterString);
428 	if (parameterHandle != NULL) {
429 		packages = get_driver_parameter(parameterHandle, "packages", NULL,
430 			NULL);
431 		volumeName = get_driver_parameter(parameterHandle, "volume-name", NULL,
432 			NULL);
433 		mountType = get_driver_parameter(parameterHandle, "type", NULL, NULL);
434 		shineThrough = get_driver_parameter(parameterHandle, "shine-through",
435 			NULL, NULL);
436 	}
437 
438 	CObjectDeleter<void, status_t> parameterHandleDeleter(parameterHandle,
439 		&delete_driver_settings);
440 
441 	if (packages != NULL && packages[0] == '\0') {
442 		FATAL("invalid package folder ('packages' parameter)!\n");
443 		RETURN_ERROR(B_BAD_VALUE);
444 	}
445 
446 	error = _InitMountType(mountType);
447 	if (error != B_OK) {
448 		FATAL("invalid mount type: \"%s\"\n", mountType);
449 		RETURN_ERROR(error);
450 	}
451 
452 	// get our mount point
453 	error = vfs_get_mount_point(fFSVolume->id, &fMountPoint.deviceID,
454 		&fMountPoint.nodeID);
455 	if (error != B_OK)
456 		RETURN_ERROR(error);
457 
458 	// create package domain
459 	fPackagesDirectory = new(std::nothrow) PackagesDirectory;
460 	if (fPackagesDirectory == NULL)
461 		RETURN_ERROR(B_NO_MEMORY);
462 
463 	struct stat st;
464 	error = fPackagesDirectory->Init(packages, fMountPoint.deviceID,
465 		fMountPoint.nodeID, st);
466 	if (error != B_OK)
467 		RETURN_ERROR(error);
468 
469 	// If no volume name is given, infer it from the mount type.
470 	if (volumeName == NULL) {
471 		switch (fMountType) {
472 			case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
473 				volumeName = "system";
474 				break;
475 			case PACKAGE_FS_MOUNT_TYPE_COMMON:
476 				volumeName = "common";
477 				break;
478 			case PACKAGE_FS_MOUNT_TYPE_HOME:
479 				volumeName = "home";
480 				break;
481 			case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
482 			default:
483 				volumeName = "Package FS";
484 				break;
485 		}
486 	}
487 
488 	String volumeNameString;
489 	if (!volumeNameString.SetTo(volumeName))
490 		RETURN_ERROR(B_NO_MEMORY);
491 
492 	// create the root node
493 	fRootDirectory
494 		= new(std::nothrow) ::RootDirectory(kRootDirectoryID, st.st_mtim);
495 	if (fRootDirectory == NULL)
496 		RETURN_ERROR(B_NO_MEMORY);
497 	fRootDirectory->Init(NULL, volumeNameString);
498 	fNodes.Insert(fRootDirectory);
499 	fRootDirectory->AcquireReference();
500 		// one reference for the table
501 
502 	// register with packagefs root
503 	error = ::PackageFSRoot::RegisterVolume(this);
504 	if (error != B_OK)
505 		RETURN_ERROR(error);
506 
507 	if (this == fPackageFSRoot->SystemVolume()) {
508 		error = _AddPackageLinksDirectory();
509 		if (error != B_OK)
510 			RETURN_ERROR(error);
511 	}
512 
513 	// create shine-through directories
514 	error = _CreateShineThroughDirectories(shineThrough);
515 	if (error != B_OK)
516 		RETURN_ERROR(error);
517 
518 	// add initial packages
519 	error = _AddInitialPackages();
520 	if (error != B_OK)
521 		RETURN_ERROR(error);
522 
523 	// publish the root node
524 	fRootDirectory->AcquireReference();
525 	error = PublishVNode(fRootDirectory);
526 	if (error != B_OK) {
527 		fRootDirectory->ReleaseReference();
528 		RETURN_ERROR(error);
529 	}
530 
531 	// bind and publish the shine-through directories
532 	error = _PublishShineThroughDirectories();
533 	if (error != B_OK)
534 		RETURN_ERROR(error);
535 
536 	StringPool::DumpUsageStatistics();
537 
538 	return B_OK;
539 }
540 
541 
542 void
543 Volume::Unmount()
544 {
545 }
546 
547 
548 status_t
549 Volume::IOCtl(Node* node, uint32 operation, void* buffer, size_t size)
550 {
551 	switch (operation) {
552 		case PACKAGE_FS_OPERATION_GET_VOLUME_INFO:
553 		{
554 			if (size != sizeof(PackageFSVolumeInfo))
555 				RETURN_ERROR(B_BAD_VALUE);
556 
557 			VolumeReadLocker volumeReadLocker(this);
558 
559 			PackageFSVolumeInfo info;
560 			info.mountType = fMountType;
561 			info.rootDeviceID = fPackageFSRoot->DeviceID();
562 			info.rootDirectoryID = fPackageFSRoot->NodeID();
563 			info.packagesDeviceID = fPackagesDirectory->DeviceID();
564 			info.packagesDirectoryID = fPackagesDirectory->NodeID();
565 
566 			RETURN_ERROR(user_memcpy(buffer, &info, sizeof(info)));
567 		}
568 
569 		case PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS:
570 		{
571 			if (size < sizeof(PackageFSGetPackageInfosRequest))
572 				RETURN_ERROR(B_BAD_VALUE);
573 
574 			PackageFSGetPackageInfosRequest* request
575 				= (PackageFSGetPackageInfosRequest*)buffer;
576 
577 			VolumeReadLocker volumeReadLocker(this);
578 
579 			uint32 packageIndex = 0;
580 			for (PackageFileNameHashTable::Iterator it
581 					= fPackages.GetIterator(); it.HasNext();
582 				packageIndex++) {
583 				Package* package = it.Next();
584 				PackageFSPackageInfo info;
585 				info.packageDeviceID = package->DeviceID();
586 				info.packageNodeID = package->NodeID();
587 				PackageFSPackageInfo* userInfo = request->infos + packageIndex;
588 				if (addr_t(userInfo + 1) > (addr_t)buffer + size)
589 					break;
590 
591 				if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
592 					return B_BAD_ADDRESS;
593 			}
594 
595 			uint32 packageCount = fPackages.CountElements();
596 			RETURN_ERROR(user_memcpy(&request->packageCount, &packageCount,
597 				sizeof(packageCount)));
598 		}
599 
600 		case PACKAGE_FS_OPERATION_CHANGE_ACTIVATION:
601 		{
602 			ActivationChangeRequest request;
603 			status_t error = request.Init(buffer, size);
604 			if (error != B_OK)
605 				RETURN_ERROR(B_BAD_VALUE);
606 
607 			return _ChangeActivation(request);
608 		}
609 
610 		default:
611 			return B_BAD_VALUE;
612 	}
613 }
614 
615 
616 void
617 Volume::AddNodeListener(NodeListener* listener, Node* node)
618 {
619 	ASSERT(!listener->IsListening());
620 
621 	listener->StartedListening(node);
622 
623 	if (NodeListener* list = fNodeListeners.Lookup(node))
624 		list->AddNodeListener(listener);
625 	else
626 		fNodeListeners.Insert(listener);
627 }
628 
629 
630 void
631 Volume::RemoveNodeListener(NodeListener* listener)
632 {
633 	ASSERT(listener->IsListening());
634 
635 	Node* node = listener->ListenedNode();
636 
637 	if (NodeListener* next = listener->RemoveNodeListener()) {
638 		// list not empty yet -- if we removed the head, add a new head to the
639 		// hash table
640 		NodeListener* list = fNodeListeners.Lookup(node);
641 		if (list == listener) {
642 			fNodeListeners.Remove(listener);
643 			fNodeListeners.Insert(next);
644 		}
645 	} else
646 		fNodeListeners.Remove(listener);
647 
648 	listener->StoppedListening();
649 }
650 
651 
652 void
653 Volume::AddQuery(Query* query)
654 {
655 	fQueries.Add(query);
656 }
657 
658 
659 void
660 Volume::RemoveQuery(Query* query)
661 {
662 	fQueries.Remove(query);
663 }
664 
665 
666 void
667 Volume::UpdateLiveQueries(Node* node, const char* attribute, int32 type,
668 	const void* oldKey, size_t oldLength, const void* newKey,
669 	size_t newLength)
670 {
671 	for (QueryList::Iterator it = fQueries.GetIterator();
672 			Query* query = it.Next();) {
673 		query->LiveUpdate(node, attribute, type, oldKey, oldLength, newKey,
674 			newLength);
675 	}
676 }
677 
678 
679 status_t
680 Volume::GetVNode(ino_t nodeID, Node*& _node)
681 {
682 	return get_vnode(fFSVolume, nodeID, (void**)&_node);
683 }
684 
685 
686 status_t
687 Volume::PutVNode(ino_t nodeID)
688 {
689 	return put_vnode(fFSVolume, nodeID);
690 }
691 
692 
693 status_t
694 Volume::RemoveVNode(ino_t nodeID)
695 {
696 	return remove_vnode(fFSVolume, nodeID);
697 }
698 
699 
700 status_t
701 Volume::PublishVNode(Node* node)
702 {
703 	return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps,
704 		node->Mode() & S_IFMT, 0);
705 }
706 
707 
708 void
709 Volume::PackageLinkNodeAdded(Node* node)
710 {
711 	_AddPackageLinksNode(node);
712 
713 	notify_entry_created(ID(), node->Parent()->ID(), node->Name(), node->ID());
714 	_NotifyNodeAdded(node);
715 }
716 
717 
718 void
719 Volume::PackageLinkNodeRemoved(Node* node)
720 {
721 	_RemovePackageLinksNode(node);
722 
723 	notify_entry_removed(ID(), node->Parent()->ID(), node->Name(), node->ID());
724 	_NotifyNodeRemoved(node);
725 }
726 
727 
728 void
729 Volume::PackageLinkNodeChanged(Node* node, uint32 statFields,
730 	const OldNodeAttributes& oldAttributes)
731 {
732 	notify_stat_changed(ID(), node->ID(), statFields);
733 	_NotifyNodeChanged(node, statFields, oldAttributes);
734 }
735 
736 
737 status_t
738 Volume::_AddInitialPackages()
739 {
740 	dprintf("packagefs: Adding packages from \"%s\"\n",
741 		fPackagesDirectory->Path());
742 
743 	// try reading the activation file
744 	status_t error = _AddInitialPackagesFromActivationFile();
745 	if (error != B_OK) {
746 		INFORM("Loading packages from activation file failed. Loading all "
747 			"packages in packages directory.\n");
748 
749 		// remove all packages already added
750 		{
751 			VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
752 			VolumeWriteLocker volumeLocker(this);
753 			_RemoveAllPackages();
754 		}
755 
756 		// read the whole directory
757 		error = _AddInitialPackagesFromDirectory();
758 		if (error != B_OK)
759 			RETURN_ERROR(error);
760 	}
761 
762 	// add the packages to the node tree
763 	VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
764 	VolumeWriteLocker volumeLocker(this);
765 	for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
766 		Package* package = it.Next();) {
767 		error = _AddPackageContent(package, false);
768 		if (error != B_OK) {
769 			for (it.Rewind(); Package* activePackage = it.Next();) {
770 				if (activePackage == package)
771 					break;
772 				_RemovePackageContent(activePackage, NULL, false);
773 			}
774 			RETURN_ERROR(error);
775 		}
776 	}
777 
778 	return B_OK;
779 }
780 
781 
782 status_t
783 Volume::_AddInitialPackagesFromActivationFile()
784 {
785 	// try reading the activation file
786 	int fd = openat(fPackagesDirectory->DirectoryFD(),
787 		kActivationFilePath, O_RDONLY);
788 	if (fd < 0) {
789 		INFORM("Failed to open packages activation file: %s\n",
790 			strerror(errno));
791 		RETURN_ERROR(errno);
792 	}
793 	FileDescriptorCloser fdCloser(fd);
794 
795 	// read the whole file into memory to simplify things
796 	struct stat st;
797 	if (fstat(fd, &st) != 0) {
798 		ERROR("Failed to stat packages activation file: %s\n",
799 			strerror(errno));
800 		RETURN_ERROR(errno);
801 	}
802 
803 	if (st.st_size > kMaxActivationFileSize) {
804 		ERROR("The packages activation file is too big.\n");
805 		RETURN_ERROR(B_BAD_DATA);
806 	}
807 
808 	char* fileContent = (char*)malloc(st.st_size + 1);
809 	if (fileContent == NULL)
810 		RETURN_ERROR(B_NO_MEMORY);
811 	MemoryDeleter fileContentDeleter(fileContent);
812 
813 	ssize_t bytesRead = read(fd, fileContent, st.st_size);
814 	if (bytesRead < 0) {
815 		ERROR("Failed to read packages activation file: %s\n", strerror(errno));
816 		RETURN_ERROR(errno);
817 	}
818 
819 	if (bytesRead != st.st_size) {
820 		ERROR("Failed to read whole packages activation file\n");
821 		RETURN_ERROR(B_ERROR);
822 	}
823 
824 	// null-terminate to simplify parsing
825 	fileContent[st.st_size] = '\0';
826 
827 	// parse the file and add the respective packages
828 	const char* packageName = fileContent;
829 	char* const fileContentEnd = fileContent + st.st_size;
830 	while (packageName < fileContentEnd) {
831 		char* packageNameEnd = strchr(packageName, '\n');
832 		if (packageNameEnd == NULL)
833 			packageNameEnd = fileContentEnd;
834 
835 		// skip empty lines
836 		if (packageName == packageNameEnd) {
837 			packageName++;
838 			continue;
839 		}
840 		*packageNameEnd = '\0';
841 
842 		if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
843 			ERROR("Invalid packages activation file content.\n");
844 			RETURN_ERROR(B_BAD_DATA);
845 		}
846 
847 		status_t error = _LoadAndAddInitialPackage(packageName);
848 		if (error != B_OK)
849 			RETURN_ERROR(error);
850 
851 		packageName = packageNameEnd + 1;
852 	}
853 
854 	return B_OK;
855 }
856 
857 
858 status_t
859 Volume::_AddInitialPackagesFromDirectory()
860 {
861 	// iterate through the dir and create packages
862 	int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY);
863 	if (fd < 0) {
864 		ERROR("Failed to open packages directory: %s\n", strerror(errno));
865 		RETURN_ERROR(errno);
866 	}
867 
868 	DIR* dir = fdopendir(fd);
869 	if (dir == NULL) {
870 		ERROR("Failed to open packages directory \"%s\": %s\n",
871 			fPackagesDirectory->Path(), strerror(errno));
872 		RETURN_ERROR(errno);
873 	}
874 	CObjectDeleter<DIR, int> dirCloser(dir, closedir);
875 
876 	while (dirent* entry = readdir(dir)) {
877 		// skip "." and ".."
878 		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
879 			continue;
880 
881 		// also skip any entry without a ".hpkg" extension
882 		size_t nameLength = strlen(entry->d_name);
883 		if (nameLength < 5
884 			|| memcmp(entry->d_name + nameLength - 5, ".hpkg", 5) != 0) {
885 			continue;
886 		}
887 
888 		_LoadAndAddInitialPackage(entry->d_name);
889 	}
890 
891 	return B_OK;
892 }
893 
894 
895 status_t
896 Volume::_LoadAndAddInitialPackage(const char* name)
897 {
898 	Package* package;
899 	status_t error = _LoadPackage(name, package);
900 	if (error != B_OK) {
901 		ERROR("Failed to load package \"%s\": %s\n", name, strerror(error));
902 		RETURN_ERROR(error);
903 	}
904 	BReference<Package> packageReference(package, true);
905 
906 	VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
907 	VolumeWriteLocker volumeLocker(this);
908 	_AddPackage(package);
909 
910 	return B_OK;
911 }
912 
913 
914 inline void
915 Volume::_AddPackage(Package* package)
916 {
917 	fPackages.Insert(package);
918 	package->AcquireReference();
919 }
920 
921 
922 inline void
923 Volume::_RemovePackage(Package* package)
924 {
925 	fPackages.Remove(package);
926 	package->ReleaseReference();
927 }
928 
929 
930 void
931 Volume::_RemoveAllPackages()
932 {
933 	Package* package = fPackages.Clear(true);
934 	while (package != NULL) {
935 		Package* next = package->FileNameHashTableNext();
936 		package->ReleaseReference();
937 		package = next;
938 	}
939 }
940 
941 
942 inline Package*
943 Volume::_FindPackage(const char* fileName) const
944 {
945 	return fPackages.Lookup(fileName);
946 }
947 
948 
949 status_t
950 Volume::_AddPackageContent(Package* package, bool notify)
951 {
952 	// Open the package. We don't need the FD here, but this is an optimization.
953 	// The attribute indices may want to read the package nodes' attributes and
954 	// the package file would be opened and closed for each attribute instance.
955 	// Since Package keeps and shares the FD as long as at least one party has
956 	// the package open, we prevent that.
957 	int fd = package->Open();
958 	if (fd < 0)
959 		RETURN_ERROR(fd);
960 	PackageCloser packageCloser(package);
961 
962 	status_t error = fPackageFSRoot->AddPackage(package);
963 	if (error != B_OK)
964 		RETURN_ERROR(error);
965 
966 	for (PackageNodeList::Iterator it = package->Nodes().GetIterator();
967 			PackageNode* node = it.Next();) {
968 		// skip over ".PackageInfo" file, it isn't part of the package content
969 		if (strcmp(node->Name(),
970 				BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
971 			continue;
972 		}
973 		error = _AddPackageContentRootNode(package, node, notify);
974 		if (error != B_OK) {
975 			_RemovePackageContent(package, node, notify);
976 			RETURN_ERROR(error);
977 		}
978 	}
979 
980 	return B_OK;
981 }
982 
983 
984 void
985 Volume::_RemovePackageContent(Package* package, PackageNode* endNode,
986 	bool notify)
987 {
988 	PackageNode* node = package->Nodes().Head();
989 	while (node != NULL) {
990 		if (node == endNode)
991 			break;
992 
993 		PackageNode* nextNode = package->Nodes().GetNext(node);
994 
995 		// skip over ".PackageInfo" file, it isn't part of the package content
996 		if (strcmp(node->Name(),
997 				BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) != 0) {
998 			_RemovePackageContentRootNode(package, node, NULL, notify);
999 		}
1000 
1001 		node = nextNode;
1002 	}
1003 
1004 	fPackageFSRoot->RemovePackage(package);;
1005 }
1006 
1007 
1008 /*!	This method recursively iterates through the descendents of the given
1009 	package root node and adds all package nodes to the node tree in
1010 	pre-order.
1011 	Due to limited kernel stack space we avoid deep recursive function calls
1012 	and rather use the package node stack implied by the tree.
1013 */
1014 status_t
1015 Volume::_AddPackageContentRootNode(Package* package,
1016 	PackageNode* rootPackageNode, bool notify)
1017 {
1018 	PackageNode* packageNode = rootPackageNode;
1019 	Directory* directory = fRootDirectory;
1020 	directory->WriteLock();
1021 
1022 	do {
1023 		Node* node;
1024 		status_t error = _AddPackageNode(directory, packageNode, notify, node);
1025 			// returns B_OK with a NULL node, when skipping the node
1026 		if (error != B_OK) {
1027 			// unlock all directories
1028 			while (directory != NULL) {
1029 				directory->WriteUnlock();
1030 				directory = directory->Parent();
1031 			}
1032 
1033 			// remove the added package nodes
1034 			_RemovePackageContentRootNode(package, rootPackageNode, packageNode,
1035 				notify);
1036 			RETURN_ERROR(error);
1037 		}
1038 
1039 		// recurse into directory, unless we're supposed to skip the node
1040 		if (node != NULL) {
1041 			if (PackageDirectory* packageDirectory
1042 					= dynamic_cast<PackageDirectory*>(packageNode)) {
1043 				if (packageDirectory->FirstChild() != NULL) {
1044 					directory = dynamic_cast<Directory*>(node);
1045 					packageNode = packageDirectory->FirstChild();
1046 					directory->WriteLock();
1047 					continue;
1048 				}
1049 			}
1050 		}
1051 
1052 		// continue with the next available (ancestors's) sibling
1053 		do {
1054 			PackageDirectory* packageDirectory = packageNode->Parent();
1055 			PackageNode* sibling = packageDirectory != NULL
1056 				? packageDirectory->NextChild(packageNode) : NULL;
1057 
1058 			if (sibling != NULL) {
1059 				packageNode = sibling;
1060 				break;
1061 			}
1062 
1063 			// no more siblings -- go back up the tree
1064 			packageNode = packageDirectory;
1065 			directory->WriteUnlock();
1066 			directory = directory->Parent();
1067 				// the parent is still locked, so this is safe
1068 		} while (packageNode != NULL);
1069 	} while (packageNode != NULL);
1070 
1071 	return B_OK;
1072 }
1073 
1074 
1075 /*!	Recursively iterates through the descendents of the given package root node
1076 	and removes all package nodes from the node tree in post-order, until
1077 	encountering \a endPackageNode (if non-null).
1078 	Due to limited kernel stack space we avoid deep recursive function calls
1079 	and rather use the package node stack implied by the tree.
1080 */
1081 void
1082 Volume::_RemovePackageContentRootNode(Package* package,
1083 	PackageNode* rootPackageNode, PackageNode* endPackageNode, bool notify)
1084 {
1085 	PackageNode* packageNode = rootPackageNode;
1086 	Directory* directory = fRootDirectory;
1087 	directory->WriteLock();
1088 
1089 	do {
1090 		if (packageNode == endPackageNode)
1091 			break;
1092 
1093 		// recurse into directory
1094 		if (PackageDirectory* packageDirectory
1095 				= dynamic_cast<PackageDirectory*>(packageNode)) {
1096 			if (packageDirectory->FirstChild() != NULL) {
1097 				if (Directory* childDirectory = dynamic_cast<Directory*>(
1098 						directory->FindChild(packageNode->Name()))) {
1099 					directory = childDirectory;
1100 					packageNode = packageDirectory->FirstChild();
1101 					directory->WriteLock();
1102 					continue;
1103 				}
1104 			}
1105 		}
1106 
1107 		// continue with the next available (ancestors's) sibling
1108 		do {
1109 			PackageDirectory* packageDirectory = packageNode->Parent();
1110 			PackageNode* sibling = packageDirectory != NULL
1111 				? packageDirectory->NextChild(packageNode) : NULL;
1112 
1113 			// we're done with the node -- remove it
1114 			_RemovePackageNode(directory, packageNode,
1115 				directory->FindChild(packageNode->Name()), notify);
1116 
1117 			if (sibling != NULL) {
1118 				packageNode = sibling;
1119 				break;
1120 			}
1121 
1122 			// no more siblings -- go back up the tree
1123 			packageNode = packageDirectory;
1124 			directory->WriteUnlock();
1125 			directory = directory->Parent();
1126 				// the parent is still locked, so this is safe
1127 		} while (packageNode != NULL/* && packageNode != rootPackageNode*/);
1128 	} while (packageNode != NULL/* && packageNode != rootPackageNode*/);
1129 }
1130 
1131 
1132 status_t
1133 Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
1134 	bool notify, Node*& _node)
1135 {
1136 	bool newNode = false;
1137 	UnpackingNode* unpackingNode;
1138 	Node* node = directory->FindChild(packageNode->Name());
1139 	PackageNode* oldPackageNode = NULL;
1140 
1141 	if (node != NULL) {
1142 		unpackingNode = dynamic_cast<UnpackingNode*>(node);
1143 		if (unpackingNode == NULL) {
1144 			_node = NULL;
1145 			return B_OK;
1146 		}
1147 		oldPackageNode = unpackingNode->GetPackageNode();
1148 	} else {
1149 		status_t error = _CreateUnpackingNode(packageNode->Mode(), directory,
1150 			packageNode->Name(), unpackingNode);
1151 		if (error != B_OK)
1152 			RETURN_ERROR(error);
1153 
1154 		node = unpackingNode->GetNode();
1155 		newNode = true;
1156 	}
1157 
1158 	BReference<Node> nodeReference(node);
1159 	NodeWriteLocker nodeWriteLocker(node);
1160 
1161 	BReference<Node> newNodeReference;
1162 	NodeWriteLocker newNodeWriteLocker;
1163 	Node* oldNode = NULL;
1164 
1165 	if (!newNode && !S_ISDIR(node->Mode()) && oldPackageNode != NULL
1166 		&& unpackingNode->WillBeFirstPackageNode(packageNode)) {
1167 		// The package node we're going to add will represent the node,
1168 		// replacing the current head package node. Since the node isn't a
1169 		// directory, we must make sure that clients having opened or mapped the
1170 		// node won't be surprised. So we create a new node and remove the
1171 		// current one.
1172 		// create a new node and transfer the package nodes to it
1173 		UnpackingNode* newUnpackingNode;
1174 		status_t error = unpackingNode->CloneTransferPackageNodes(
1175 			fNextNodeID++, newUnpackingNode);
1176 		if (error != B_OK)
1177 			RETURN_ERROR(error);
1178 
1179 		// remove the old node
1180 		_NotifyNodeRemoved(node);
1181 		_RemoveNodeAndVNode(node);
1182 		oldNode = node;
1183 
1184 		// add the new node
1185 		unpackingNode = newUnpackingNode;
1186 		node = unpackingNode->GetNode();
1187 		newNodeReference.SetTo(node);
1188 		newNodeWriteLocker.SetTo(node, false);
1189 
1190 		directory->AddChild(node);
1191 		fNodes.Insert(node);
1192 		newNode = true;
1193 	}
1194 
1195 	status_t error = unpackingNode->AddPackageNode(packageNode, ID());
1196 	if (error != B_OK) {
1197 		// Remove the node, if created before. If the node was created to
1198 		// replace the previous node, send out notifications instead.
1199 		if (newNode) {
1200 			if (oldNode != NULL) {
1201 				_NotifyNodeAdded(node);
1202 				if (notify) {
1203 					notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
1204 						oldNode->ID());
1205 					notify_entry_created(ID(), directory->ID(), node->Name(),
1206 						node->ID());
1207 				}
1208 			} else
1209 				_RemoveNode(node);
1210 		}
1211 		RETURN_ERROR(error);
1212 	}
1213 
1214 	if (newNode) {
1215 		_NotifyNodeAdded(node);
1216 	} else if (packageNode == unpackingNode->GetPackageNode()) {
1217 		_NotifyNodeChanged(node, kAllStatFields,
1218 			OldUnpackingNodeAttributes(oldPackageNode));
1219 	}
1220 
1221 	if (notify) {
1222 		if (newNode) {
1223 			if (oldNode != NULL) {
1224 				notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
1225 					oldNode->ID());
1226 			}
1227 			notify_entry_created(ID(), directory->ID(), node->Name(),
1228 				node->ID());
1229 		} else if (packageNode == unpackingNode->GetPackageNode()) {
1230 			// The new package node has become the one representing the node.
1231 			// Send stat changed notification for directories and entry
1232 			// removed + created notifications for files and symlinks.
1233 			notify_stat_changed(ID(), node->ID(), kAllStatFields);
1234 			// TODO: Actually the attributes might change, too!
1235 		}
1236 	}
1237 
1238 	_node = node;
1239 	return B_OK;
1240 }
1241 
1242 
1243 void
1244 Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
1245 	Node* node, bool notify)
1246 {
1247 	UnpackingNode* unpackingNode = dynamic_cast<UnpackingNode*>(node);
1248 	if (unpackingNode == NULL)
1249 		return;
1250 
1251 	BReference<Node> nodeReference(node);
1252 	NodeWriteLocker nodeWriteLocker(node);
1253 
1254 	PackageNode* headPackageNode = unpackingNode->GetPackageNode();
1255 	bool nodeRemoved = false;
1256 	Node* newNode = NULL;
1257 
1258 	BReference<Node> newNodeReference;
1259 	NodeWriteLocker newNodeWriteLocker;
1260 
1261 	// If this is the last package node of the node, remove it completely.
1262 	if (unpackingNode->IsOnlyPackageNode(packageNode)) {
1263 		// Notify before removing the node. Otherwise the indices might not
1264 		// find the node anymore.
1265 		_NotifyNodeRemoved(node);
1266 
1267 		unpackingNode->PrepareForRemoval();
1268 
1269 		_RemoveNodeAndVNode(node);
1270 		nodeRemoved = true;
1271 	} else if (packageNode == headPackageNode) {
1272 		// The node does at least have one more package node, but the one to be
1273 		// removed is the head. Unless it's a directory, we replace the node
1274 		// with a completely new one and let the old one die. This is necessary
1275 		// to avoid surprises for clients that have opened/mapped the node.
1276 		if (S_ISDIR(packageNode->Mode())) {
1277 			unpackingNode->RemovePackageNode(packageNode, ID());
1278 			_NotifyNodeChanged(node, kAllStatFields,
1279 				OldUnpackingNodeAttributes(headPackageNode));
1280 		} else {
1281 			// create a new node and transfer the package nodes to it
1282 			UnpackingNode* newUnpackingNode;
1283 			status_t error = unpackingNode->CloneTransferPackageNodes(
1284 				fNextNodeID++, newUnpackingNode);
1285 			if (error == B_OK) {
1286 				// remove the package node
1287 				newUnpackingNode->RemovePackageNode(packageNode, ID());
1288 
1289 				// remove the old node
1290 				_NotifyNodeRemoved(node);
1291 				_RemoveNodeAndVNode(node);
1292 
1293 				// add the new node
1294 				newNode = newUnpackingNode->GetNode();
1295 				newNodeReference.SetTo(newNode);
1296 				newNodeWriteLocker.SetTo(newNode, false);
1297 
1298 				directory->AddChild(newNode);
1299 				fNodes.Insert(newNode);
1300 				_NotifyNodeAdded(newNode);
1301 			} else {
1302 				// There's nothing we can do. Remove the node completely.
1303 				_NotifyNodeRemoved(node);
1304 
1305 				unpackingNode->PrepareForRemoval();
1306 
1307 				_RemoveNodeAndVNode(node);
1308 				nodeRemoved = true;
1309 			}
1310 		}
1311 	} else {
1312 		// The package node to remove is not the head of the node. This change
1313 		// doesn't have any visible effect.
1314 		unpackingNode->RemovePackageNode(packageNode, ID());
1315 	}
1316 
1317 	if (!notify)
1318 		return;
1319 
1320 	// send notifications
1321 	if (nodeRemoved) {
1322 		notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID());
1323 	} else if (packageNode == headPackageNode) {
1324 		// The removed package node was the one representing the node.
1325 		// Send stat changed notification for directories and entry
1326 		// removed + created notifications for files and symlinks.
1327 		if (S_ISDIR(packageNode->Mode())) {
1328 			notify_stat_changed(ID(), node->ID(), kAllStatFields);
1329 			// TODO: Actually the attributes might change, too!
1330 		} else {
1331 			notify_entry_removed(ID(), directory->ID(), node->Name(),
1332 				node->ID());
1333 			notify_entry_created(ID(), directory->ID(), newNode->Name(),
1334 				newNode->ID());
1335 		}
1336 	}
1337 }
1338 
1339 
1340 status_t
1341 Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const String& name,
1342 	UnpackingNode*& _node)
1343 {
1344 	UnpackingNode* unpackingNode;
1345 	if (S_ISREG(mode) || S_ISLNK(mode))
1346 		unpackingNode = new(std::nothrow) UnpackingLeafNode(fNextNodeID++);
1347 	else if (S_ISDIR(mode))
1348 		unpackingNode = new(std::nothrow) UnpackingDirectory(fNextNodeID++);
1349 	else
1350 		RETURN_ERROR(B_UNSUPPORTED);
1351 
1352 	if (unpackingNode == NULL)
1353 		RETURN_ERROR(B_NO_MEMORY);
1354 
1355 	Node* node = unpackingNode->GetNode();
1356 	BReference<Node> nodeReference(node, true);
1357 
1358 	status_t error = node->Init(parent, name);
1359 	if (error != B_OK)
1360 		RETURN_ERROR(error);
1361 
1362 	parent->AddChild(node);
1363 
1364 	fNodes.Insert(node);
1365 	nodeReference.Detach();
1366 		// we keep the initial node reference for the table
1367 
1368 	_node = unpackingNode;
1369 	return B_OK;
1370 }
1371 
1372 
1373 void
1374 Volume::_RemoveNode(Node* node)
1375 {
1376 	// remove from parent
1377 	Directory* parent = node->Parent();
1378 	parent->RemoveChild(node);
1379 
1380 	// remove from node table
1381 	fNodes.Remove(node);
1382 	node->ReleaseReference();
1383 }
1384 
1385 
1386 void
1387 Volume::_RemoveNodeAndVNode(Node* node)
1388 {
1389 	// If the node is known to the VFS, we get the vnode, remove it, and put it,
1390 	// so that the VFS will discard it as soon as possible (i.e. now, if no one
1391 	// else is using it).
1392 	NodeWriteLocker nodeWriteLocker(node);
1393 
1394 	// Remove the node from its parent and the volume. This makes the node
1395 	// inaccessible via the get_vnode() and lookup() hooks.
1396 	_RemoveNode(node);
1397 
1398 	bool getVNode = node->IsKnownToVFS();
1399 
1400 	nodeWriteLocker.Unlock();
1401 
1402 	// Get a vnode reference, if the node is already known to the VFS.
1403 	Node* dummyNode;
1404 	if (getVNode && GetVNode(node->ID(), dummyNode) == B_OK) {
1405 		// TODO: There still is a race condition here which we can't avoid
1406 		// without more help from the VFS. Right after we drop the write
1407 		// lock a vnode for the node could be discarded by the VFS. At that
1408 		// point another thread trying to get the vnode by ID would create
1409 		// a vnode, mark it busy and call our get_vnode() hook. It would
1410 		// block since we (i.e. the package loader thread executing this
1411 		// method) still have the volume write lock. Our get_vnode() call
1412 		// would block, since it finds the vnode marked busy. It times out
1413 		// eventually, but until then a good deal of FS operations might
1414 		// block as well due to us holding the volume lock and probably
1415 		// several node locks as well. A get_vnode*() variant (e.g.
1416 		// get_vnode_etc() with flags parameter) that wouldn't block and
1417 		// only get the vnode, if already loaded and non-busy, would be
1418 		// perfect here.
1419 		RemoveVNode(node->ID());
1420 		PutVNode(node->ID());
1421 	}
1422 }
1423 
1424 
1425 status_t
1426 Volume::_LoadPackage(const char* name, Package*& _package)
1427 {
1428 	// check whether the entry is a file
1429 	struct stat st;
1430 	if (fstatat(fPackagesDirectory->DirectoryFD(), name, &st, 0) < 0
1431 		|| !S_ISREG(st.st_mode)) {
1432 		return B_BAD_VALUE;
1433 	}
1434 
1435 	// create a package
1436 	Package* package = new(std::nothrow) Package(this, st.st_dev, st.st_ino);
1437 	if (package == NULL)
1438 		RETURN_ERROR(B_NO_MEMORY);
1439 	BReference<Package> packageReference(package, true);
1440 
1441 	status_t error = package->Init(name);
1442 	if (error != B_OK)
1443 		return error;
1444 
1445 	error = package->Load();
1446 	if (error != B_OK)
1447 		return error;
1448 
1449 	_package = packageReference.Detach();
1450 	return B_OK;
1451 }
1452 
1453 
1454 status_t
1455 Volume::_ChangeActivation(ActivationChangeRequest& request)
1456 {
1457 	uint32 itemCount = request.CountItems();
1458 	if (itemCount == 0)
1459 		return B_OK;
1460 
1461 	// first check the request
1462 	int32 newPackageCount = 0;
1463 	int32 oldPackageCount = 0;
1464 	{
1465 		VolumeReadLocker volumeLocker(this);
1466 
1467 		for (uint32 i = 0; i < itemCount; i++) {
1468 			PackageFSActivationChangeItem* item = request.ItemAt(i);
1469 			if (item->parentDeviceID != fPackagesDirectory->DeviceID()
1470 				|| item->parentDirectoryID != fPackagesDirectory->NodeID()) {
1471 				ERROR("Volume::_ChangeActivation(): mismatching packages "
1472 					"directory\n");
1473 				RETURN_ERROR(B_BAD_VALUE);
1474 			}
1475 
1476 			Package* package = _FindPackage(item->name);
1477 // TODO: We should better look up the package by node_ref!
1478 			if (item->type == PACKAGE_FS_ACTIVATE_PACKAGE) {
1479 				if (package != NULL) {
1480 					ERROR("Volume::_ChangeActivation(): package to activate "
1481 						"already activated: \"%s\"\n", item->name);
1482 					RETURN_ERROR(B_BAD_VALUE);
1483 				}
1484 				newPackageCount++;
1485 			} else if (item->type == PACKAGE_FS_DEACTIVATE_PACKAGE) {
1486 				if (package == NULL) {
1487 					ERROR("Volume::_ChangeActivation(): package to deactivate "
1488 						"not found: \"%s\"\n", item->name);
1489 					RETURN_ERROR(B_BAD_VALUE);
1490 				}
1491 				oldPackageCount++;
1492 			} else if (item->type == PACKAGE_FS_REACTIVATE_PACKAGE) {
1493 				if (package == NULL) {
1494 					ERROR("Volume::_ChangeActivation(): package to reactivate "
1495 						"not found: \"%s\"\n", item->name);
1496 					RETURN_ERROR(B_BAD_VALUE);
1497 				}
1498 				oldPackageCount++;
1499 				newPackageCount++;
1500 			} else
1501 				RETURN_ERROR(B_BAD_VALUE);
1502 		}
1503 	}
1504 INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32 " old packages\n", newPackageCount, oldPackageCount);
1505 
1506 	// Things look good so far -- allocate reference arrays for the packages to
1507 	// add and remove.
1508 	BReference<Package>* newPackageReferences
1509 		= new(std::nothrow) BReference<Package>[newPackageCount];
1510 	if (newPackageReferences == NULL)
1511 		RETURN_ERROR(B_NO_MEMORY);
1512 	ArrayDeleter<BReference<Package> > newPackageReferencesDeleter(
1513 			newPackageReferences);
1514 
1515 	BReference<Package>* oldPackageReferences
1516 		= new(std::nothrow) BReference<Package>[oldPackageCount];
1517 	if (oldPackageReferences == NULL)
1518 		RETURN_ERROR(B_NO_MEMORY);
1519 	ArrayDeleter<BReference<Package> > oldPackageReferencesDeleter(
1520 			oldPackageReferences);
1521 
1522 	// load all new packages
1523 	int32 newPackageIndex = 0;
1524 	for (uint32 i = 0; i < itemCount; i++) {
1525 		PackageFSActivationChangeItem* item = request.ItemAt(i);
1526 
1527 		if (item->type != PACKAGE_FS_ACTIVATE_PACKAGE
1528 			&& item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
1529 			continue;
1530 		}
1531 
1532 		Package* package;
1533 		status_t error = _LoadPackage(item->name, package);
1534 		if (error != B_OK) {
1535 			ERROR("Volume::_ChangeActivation(): failed to load package "
1536 				"\"%s\"\n", item->name);
1537 			RETURN_ERROR(error);
1538 		}
1539 
1540 		newPackageReferences[newPackageIndex++].SetTo(package, true);
1541 	}
1542 
1543 	// apply the changes
1544 	VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
1545 	VolumeWriteLocker volumeLocker(this);
1546 // TODO: Add a change counter to Volume, so we can easily check whether
1547 // everything is still the same.
1548 
1549 	// remove the old packages
1550 	int32 oldPackageIndex = 0;
1551 	for (uint32 i = 0; i < itemCount; i++) {
1552 		PackageFSActivationChangeItem* item = request.ItemAt(i);
1553 
1554 		if (item->type != PACKAGE_FS_DEACTIVATE_PACKAGE
1555 			&& item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
1556 			continue;
1557 		}
1558 
1559 		Package* package = _FindPackage(item->name);
1560 // TODO: We should better look up the package by node_ref!
1561 		oldPackageReferences[oldPackageIndex++].SetTo(package);
1562 		_RemovePackageContent(package, NULL, true);
1563 		_RemovePackage(package);
1564 INFORM("package \"%s\" deactivated\n", package->FileName().Data());
1565 	}
1566 // TODO: Since package removal cannot fail, consider adding the new packages
1567 // first. The reactivation case may make that problematic, since two packages
1568 // with the same name would be active after activating the new one. Check!
1569 
1570 	// add the new packages
1571 	status_t error = B_OK;
1572 	for (newPackageIndex = 0; newPackageIndex < newPackageCount;
1573 		newPackageIndex++) {
1574 		Package* package = newPackageReferences[newPackageIndex];
1575 		_AddPackage(package);
1576 
1577 		// add the package to the node tree
1578 		error = _AddPackageContent(package, true);
1579 		if (error != B_OK) {
1580 			_RemovePackage(package);
1581 			break;
1582 		}
1583 INFORM("package \"%s\" activated\n", package->FileName().Data());
1584 	}
1585 
1586 	// Try to roll back the changes, if an error occurred.
1587 	if (error != B_OK) {
1588 		for (int32 i = newPackageIndex - 1; i >= 0; i--) {
1589 			Package* package = newPackageReferences[i];
1590 			_RemovePackageContent(package, NULL, true);
1591 			_RemovePackage(package);
1592 		}
1593 
1594 		for (int32 i = oldPackageCount - 1; i >= 0; i--) {
1595 			Package* package = oldPackageReferences[i];
1596 			_AddPackage(package);
1597 
1598 			if (_AddPackageContent(package, true) != B_OK) {
1599 				// nothing we can do here
1600 				ERROR("Volume::_ChangeActivation(): failed to roll back "
1601 					"deactivation of package \"%s\" after error\n",
1602 		  			package->FileName().Data());
1603 				_RemovePackage(package);
1604 			}
1605 		}
1606 	}
1607 
1608 	return error;
1609 }
1610 
1611 
1612 status_t
1613 Volume::_InitMountType(const char* mountType)
1614 {
1615 	if (mountType == NULL)
1616 		fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
1617 	else if (strcmp(mountType, "system") == 0)
1618 		fMountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM;
1619 	else if (strcmp(mountType, "common") == 0)
1620 		fMountType = PACKAGE_FS_MOUNT_TYPE_COMMON;
1621 	else if (strcmp(mountType, "home") == 0)
1622 		fMountType = PACKAGE_FS_MOUNT_TYPE_HOME;
1623 	else if (strcmp(mountType, "custom") == 0)
1624 		fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
1625 	else
1626 		RETURN_ERROR(B_BAD_VALUE);
1627 
1628 	return B_OK;
1629 }
1630 
1631 
1632 status_t
1633 Volume::_CreateShineThroughDirectory(Directory* parent, const char* name,
1634 	Directory*& _directory)
1635 {
1636 	ShineThroughDirectory* directory = new(std::nothrow) ShineThroughDirectory(
1637 		fNextNodeID++);
1638 	if (directory == NULL)
1639 		RETURN_ERROR(B_NO_MEMORY);
1640 	BReference<ShineThroughDirectory> directoryReference(directory, true);
1641 
1642 	String nameString;
1643 	if (!nameString.SetTo(name))
1644 		RETURN_ERROR(B_NO_MEMORY);
1645 
1646 	status_t error = directory->Init(parent, nameString);
1647 	if (error != B_OK)
1648 		RETURN_ERROR(error);
1649 
1650 	parent->AddChild(directory);
1651 
1652 	fNodes.Insert(directory);
1653 	directoryReference.Detach();
1654 		// we keep the initial node reference for the table
1655 
1656 	_directory = directory;
1657 	return B_OK;
1658 }
1659 
1660 
1661 status_t
1662 Volume::_CreateShineThroughDirectories(const char* shineThroughSetting)
1663 {
1664 	// get the directories to map
1665 	const char* const* directories = NULL;
1666 
1667 	if (shineThroughSetting == NULL) {
1668 		// nothing specified -- derive from mount type
1669 		switch (fMountType) {
1670 			case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
1671 				directories = kSystemShineThroughDirectories;
1672 				break;
1673 			case PACKAGE_FS_MOUNT_TYPE_COMMON:
1674 				directories = kCommonShineThroughDirectories;
1675 				break;
1676 			case PACKAGE_FS_MOUNT_TYPE_HOME:
1677 				directories = kHomeShineThroughDirectories;
1678 				break;
1679 			case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
1680 				return B_OK;
1681 			case PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT:
1682 				return B_BAD_VALUE;
1683 		}
1684 	} else if (strcmp(shineThroughSetting, "system") == 0)
1685 		directories = kSystemShineThroughDirectories;
1686 	else if (strcmp(shineThroughSetting, "common") == 0)
1687 		directories = kCommonShineThroughDirectories;
1688 	else if (strcmp(shineThroughSetting, "home") == 0)
1689 		directories = kHomeShineThroughDirectories;
1690 	else if (strcmp(shineThroughSetting, "none") == 0)
1691 		directories = NULL;
1692 	else
1693 		RETURN_ERROR(B_BAD_VALUE);
1694 
1695 	if (directories == NULL)
1696 		return B_OK;
1697 
1698 	// iterate through the directory list and create the directories
1699 	while (const char* directoryName = *(directories++)) {
1700 		// create the directory
1701 		Directory* directory;
1702 		status_t error = _CreateShineThroughDirectory(fRootDirectory,
1703 			directoryName, directory);
1704 		if (error != B_OK)
1705 			RETURN_ERROR(error);
1706 	}
1707 
1708 	return B_OK;
1709 }
1710 
1711 
1712 status_t
1713 Volume::_PublishShineThroughDirectories()
1714 {
1715 	// Iterate through the root directory children and bind the shine-through
1716 	// directories to the respective mount point subdirectories.
1717 	Node* nextNode;
1718 	for (Node* node = fRootDirectory->FirstChild(); node != NULL;
1719 			node = nextNode) {
1720 		nextNode = fRootDirectory->NextChild(node);
1721 
1722 		// skip anything but shine-through directories
1723 		ShineThroughDirectory* directory
1724 			= dynamic_cast<ShineThroughDirectory*>(node);
1725 		if (directory == NULL)
1726 			continue;
1727 
1728 		const char* directoryName = directory->Name();
1729 
1730 		// look up the mount point subdirectory
1731 		struct vnode* vnode;
1732 		status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID,
1733 			fMountPoint.nodeID, directoryName, &vnode);
1734 		if (error != B_OK) {
1735 			dprintf("packagefs: Failed to get shine-through directory \"%s\": "
1736 				"%s\n", directoryName, strerror(error));
1737 			_RemoveNode(directory);
1738 			continue;
1739 		}
1740 		CObjectDeleter<struct vnode> vnodePutter(vnode, &vfs_put_vnode);
1741 
1742 		// stat it
1743 		struct stat st;
1744 		error = vfs_stat_vnode(vnode, &st);
1745 		if (error != B_OK) {
1746 			dprintf("packagefs: Failed to stat shine-through directory \"%s\": "
1747 				"%s\n", directoryName, strerror(error));
1748 			_RemoveNode(directory);
1749 			continue;
1750 		}
1751 
1752 		if (!S_ISDIR(st.st_mode)) {
1753 			dprintf("packagefs: Shine-through entry \"%s\" is not a "
1754 				"directory\n", directoryName);
1755 			_RemoveNode(directory);
1756 			continue;
1757 		}
1758 
1759 		// publish the vnode, so the VFS will find it without asking us
1760 		directory->AcquireReference();
1761 		error = PublishVNode(directory);
1762 		if (error != B_OK) {
1763 			directory->ReleaseReference();
1764 			_RemoveNode(directory);
1765 			RETURN_ERROR(error);
1766 		}
1767 
1768 		// bind the directory
1769 		error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id,
1770 			directory->ID());
1771 
1772 		PutVNode(directory->ID());
1773 			// release our reference again -- on success
1774 			// vfs_bind_mount_directory() got one
1775 
1776 		if (error != B_OK)
1777 			RETURN_ERROR(error);
1778 	}
1779 
1780 	return B_OK;
1781 }
1782 
1783 
1784 status_t
1785 Volume::_AddPackageLinksDirectory()
1786 {
1787 	// called when mounting, so we don't need to lock the volume
1788 
1789 	PackageLinksDirectory* packageLinksDirectory
1790 		= fPackageFSRoot->GetPackageLinksDirectory();
1791 
1792 	NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1793 	NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
1794 
1795 	packageLinksDirectory->SetParent(fRootDirectory);
1796 	fRootDirectory->AddChild(packageLinksDirectory);
1797 
1798 	_AddPackageLinksNode(packageLinksDirectory);
1799 
1800 	packageLinksDirectory->SetListener(this);
1801 
1802 	return B_OK;
1803 }
1804 
1805 
1806 void
1807 Volume::_RemovePackageLinksDirectory()
1808 {
1809 	PackageLinksDirectory* packageLinksDirectory
1810 		= fPackageFSRoot->GetPackageLinksDirectory();
1811 
1812 	VolumeWriteLocker volumeLocker(this);
1813 	NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1814 	NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
1815 
1816 	if (packageLinksDirectory->Parent() == fRootDirectory) {
1817 		packageLinksDirectory->SetListener(NULL);
1818 		fRootDirectory->RemoveChild(packageLinksDirectory);
1819 		packageLinksDirectory->SetParent(NULL);
1820 	}
1821 }
1822 
1823 
1824 void
1825 Volume::_AddPackageLinksNode(Node* node)
1826 {
1827 	node->SetID(fNextNodeID++);
1828 
1829 	fNodes.Insert(node);
1830 	node->AcquireReference();
1831 
1832 	// If this is a directory, recursively add descendants. The directory tree
1833 	// for the package links isn't deep, so we can do recursion.
1834 	if (Directory* directory = dynamic_cast<Directory*>(node)) {
1835 		for (Node* child = directory->FirstChild(); child != NULL;
1836 				child = directory->NextChild(child)) {
1837 			NodeWriteLocker childWriteLocker(child);
1838 			_AddPackageLinksNode(child);
1839 		}
1840 	}
1841 }
1842 
1843 
1844 void
1845 Volume::_RemovePackageLinksNode(Node* node)
1846 {
1847 	// If this is a directory, recursively remove descendants. The directory
1848 	// tree for the package links isn't deep, so we can do recursion.
1849 	if (Directory* directory = dynamic_cast<Directory*>(node)) {
1850 		for (Node* child = directory->FirstChild(); child != NULL;
1851 				child = directory->NextChild(child)) {
1852 			NodeWriteLocker childWriteLocker(child);
1853 			_RemovePackageLinksNode(child);
1854 		}
1855 	}
1856 
1857 	fNodes.Remove(node);
1858 	node->ReleaseReference();
1859 }
1860 
1861 
1862 inline Volume*
1863 Volume::_SystemVolumeIfNotSelf() const
1864 {
1865 	if (Volume* systemVolume = fPackageFSRoot->SystemVolume())
1866 		return systemVolume == this ? NULL : systemVolume;
1867 	return NULL;
1868 }
1869 
1870 
1871 void
1872 Volume::_NotifyNodeAdded(Node* node)
1873 {
1874 	Node* key = node;
1875 
1876 	for (int i = 0; i < 2; i++) {
1877 		if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1878 			NodeListener* last = listener->PreviousNodeListener();
1879 
1880 			while (true) {
1881 				NodeListener* next = listener->NextNodeListener();
1882 
1883 				listener->NodeAdded(node);
1884 
1885 				if (listener == last)
1886 					break;
1887 
1888 				listener = next;
1889 			}
1890 		}
1891 
1892 		key = NULL;
1893 	}
1894 }
1895 
1896 
1897 void
1898 Volume::_NotifyNodeRemoved(Node* node)
1899 {
1900 	Node* key = node;
1901 
1902 	for (int i = 0; i < 2; i++) {
1903 		if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1904 			NodeListener* last = listener->PreviousNodeListener();
1905 
1906 			while (true) {
1907 				NodeListener* next = listener->NextNodeListener();
1908 
1909 				listener->NodeRemoved(node);
1910 
1911 				if (listener == last)
1912 					break;
1913 
1914 				listener = next;
1915 			}
1916 		}
1917 
1918 		key = NULL;
1919 	}
1920 }
1921 
1922 
1923 void
1924 Volume::_NotifyNodeChanged(Node* node, uint32 statFields,
1925 	const OldNodeAttributes& oldAttributes)
1926 {
1927 	Node* key = node;
1928 
1929 	for (int i = 0; i < 2; i++) {
1930 		if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1931 			NodeListener* last = listener->PreviousNodeListener();
1932 
1933 			while (true) {
1934 				NodeListener* next = listener->NextNodeListener();
1935 
1936 				listener->NodeChanged(node, statFields, oldAttributes);
1937 
1938 				if (listener == last)
1939 					break;
1940 
1941 				listener = next;
1942 			}
1943 		}
1944 
1945 		key = NULL;
1946 	}
1947 }
1948