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