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