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
49static const ino_t kRootDirectoryID = 1;
50
51static 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
56const char* const kShineThroughDirectories[] = {
57	"cache", "non-packaged", "packages", "settings", "var", NULL
58};
59
60// sanity limit for activation change request
61const size_t kMaxActivationRequestSize = 10 * 1024 * 1024;
62
63// sanity limit for activation file size
64const size_t kMaxActivationFileSize = 10 * 1024 * 1024;
65
66static const char* const kAdministrativeDirectoryName
67	= PACKAGES_DIRECTORY_ADMIN_DIRECTORY;
68static const char* const kActivationFileName
69	= PACKAGES_DIRECTORY_ACTIVATION_FILE;
70static const char* const kActivationFilePath
71	= PACKAGES_DIRECTORY_ADMIN_DIRECTORY "/"
72		PACKAGES_DIRECTORY_ACTIVATION_FILE;
73
74
75// #pragma mark - ShineThroughDirectory
76
77
78struct Volume::ShineThroughDirectory : public Directory {
79	ShineThroughDirectory(ino_t id)
80		:
81		Directory(id)
82	{
83		get_real_time(fModifiedTime);
84	}
85
86	virtual timespec ModifiedTime() const
87	{
88		return fModifiedTime;
89	}
90
91private:
92	timespec	fModifiedTime;
93};
94
95
96// #pragma mark - ActivationChangeRequest
97
98
99struct Volume::ActivationChangeRequest {
100public:
101	ActivationChangeRequest()
102		:
103		fRequest(NULL),
104		fRequestSize(0)
105	{
106	}
107
108	~ActivationChangeRequest()
109	{
110		free(fRequest);
111	}
112
113	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
148	uint32 CountItems() const
149	{
150		return fRequest->itemCount;
151	}
152
153	PackageFSActivationChangeItem* ItemAt(uint32 index) const
154	{
155		return index < CountItems() ? &fRequest->items[index] : NULL;
156	}
157
158private:
159	PackageFSActivationChangeRequest*	fRequest;
160	size_t								fRequestSize;
161};
162
163
164// #pragma mark - Volume
165
166
167Volume::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
182Volume::~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
230status_t
231Volume::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(std::nothrow) ::RootDirectory(kRootDirectoryID, st.st_mtim);
403	if (fRootDirectory == NULL)
404		RETURN_ERROR(B_NO_MEMORY);
405	fRootDirectory->Init(NULL, 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
450void
451Volume::Unmount()
452{
453}
454
455
456status_t
457Volume::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
568void
569Volume::AddNodeListener(NodeListener* listener, Node* node)
570{
571	ASSERT(!listener->IsListening());
572
573	listener->StartedListening(node);
574
575	if (NodeListener* list = fNodeListeners.Lookup(node))
576		list->AddNodeListener(listener);
577	else
578		fNodeListeners.Insert(listener);
579}
580
581
582void
583Volume::RemoveNodeListener(NodeListener* listener)
584{
585	ASSERT(listener->IsListening());
586
587	Node* node = listener->ListenedNode();
588
589	if (NodeListener* next = listener->RemoveNodeListener()) {
590		// list not empty yet -- if we removed the head, add a new head to the
591		// hash table
592		NodeListener* list = fNodeListeners.Lookup(node);
593		if (list == listener) {
594			fNodeListeners.Remove(listener);
595			fNodeListeners.Insert(next);
596		}
597	} else
598		fNodeListeners.Remove(listener);
599
600	listener->StoppedListening();
601}
602
603
604void
605Volume::AddQuery(Query* query)
606{
607	fQueries.Add(query);
608}
609
610
611void
612Volume::RemoveQuery(Query* query)
613{
614	fQueries.Remove(query);
615}
616
617
618void
619Volume::UpdateLiveQueries(Node* node, const char* attribute, int32 type,
620	const void* oldKey, size_t oldLength, const void* newKey,
621	size_t newLength)
622{
623	for (QueryList::Iterator it = fQueries.GetIterator();
624			Query* query = it.Next();) {
625		query->LiveUpdate(node, attribute, type, oldKey, oldLength, newKey,
626			newLength);
627	}
628}
629
630
631status_t
632Volume::GetVNode(ino_t nodeID, Node*& _node)
633{
634	return get_vnode(fFSVolume, nodeID, (void**)&_node);
635}
636
637
638status_t
639Volume::PutVNode(ino_t nodeID)
640{
641	return put_vnode(fFSVolume, nodeID);
642}
643
644
645status_t
646Volume::RemoveVNode(ino_t nodeID)
647{
648	return remove_vnode(fFSVolume, nodeID);
649}
650
651
652status_t
653Volume::PublishVNode(Node* node)
654{
655	return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps,
656		node->Mode() & S_IFMT, 0);
657}
658
659
660void
661Volume::PackageLinkNodeAdded(Node* node)
662{
663	_AddPackageLinksNode(node);
664
665	notify_entry_created(ID(), node->Parent()->ID(), node->Name(), node->ID());
666	_NotifyNodeAdded(node);
667}
668
669
670void
671Volume::PackageLinkNodeRemoved(Node* node)
672{
673	_RemovePackageLinksNode(node);
674
675	notify_entry_removed(ID(), node->Parent()->ID(), node->Name(), node->ID());
676	_NotifyNodeRemoved(node);
677}
678
679
680void
681Volume::PackageLinkNodeChanged(Node* node, uint32 statFields,
682	const OldNodeAttributes& oldAttributes)
683{
684	Directory* parent = node->Parent();
685	notify_stat_changed(ID(), parent != NULL ? parent->ID() : -1, node->ID(),
686		statFields);
687	_NotifyNodeChanged(node, statFields, oldAttributes);
688}
689
690
691status_t
692Volume::_LoadOldPackagesStates(const char* packagesState)
693{
694	// open and stat the admininistrative dir
695	int fd = openat(fPackagesDirectory->DirectoryFD(),
696		kAdministrativeDirectoryName, O_RDONLY);
697	if (fd < 0) {
698		ERROR("Failed to open administrative directory: %s\n", strerror(errno));
699		RETURN_ERROR(errno);
700	}
701
702	struct stat adminDirStat;
703	if (fstat(fd, &adminDirStat) < 0) {
704		ERROR("Failed to fstat() administrative directory: %s\n",
705			strerror(errno));
706		RETURN_ERROR(errno);
707	}
708
709	// iterate through the "administrative" dir
710	DirCloser dir(fdopendir(fd));
711	if (!dir.IsSet()) {
712		ERROR("Failed to open administrative directory: %s\n", strerror(errno));
713		RETURN_ERROR(errno);
714	}
715
716	while (dirent* entry = readdir(dir.Get())) {
717		if (strncmp(entry->d_name, "state_", 6) != 0
718			|| strcmp(entry->d_name, packagesState) < 0) {
719			continue;
720		}
721
722		PackagesDirectory* packagesDirectory
723			= new(std::nothrow) PackagesDirectory;
724		status_t error = packagesDirectory->InitOldState(adminDirStat.st_dev,
725			adminDirStat.st_ino, entry->d_name);
726		if (error != B_OK) {
727			delete packagesDirectory;
728			continue;
729		}
730
731		fPackagesDirectories.Add(packagesDirectory);
732		fPackagesDirectoriesByNodeRef.Insert(packagesDirectory);
733
734		INFORM("added old packages dir state \"%s\"\n",
735			packagesDirectory->StateName().Data());
736	}
737
738	// sort the packages directories by state age
739	fPackagesDirectories.Sort(&PackagesDirectory::IsNewer);
740
741	return B_OK;
742}
743
744
745status_t
746Volume::_AddInitialPackages()
747{
748	PackagesDirectory* packagesDirectory = fPackagesDirectories.Last();
749	INFORM("Adding packages from \"%s\"\n", packagesDirectory->Path());
750
751	// try reading the activation file of the oldest state
752	status_t error = _AddInitialPackagesFromActivationFile(packagesDirectory);
753	if (error != B_OK && packagesDirectory != fPackagesDirectory) {
754		WARN("Loading packages from old state \"%s\" failed. Loading packages "
755			"from latest state.\n", packagesDirectory->StateName().Data());
756
757		// remove all packages already added
758		{
759			VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
760			VolumeWriteLocker volumeLocker(this);
761			_RemoveAllPackages();
762		}
763
764		// remove the old states
765		while (fPackagesDirectories.Last() != fPackagesDirectory)
766			fPackagesDirectories.RemoveTail()->ReleaseReference();
767
768		// try reading the activation file of the latest state
769		packagesDirectory = fPackagesDirectory;
770		error = _AddInitialPackagesFromActivationFile(packagesDirectory);
771	}
772
773	if (error != B_OK) {
774		INFORM("Loading packages from activation file failed. Loading all "
775			"packages in packages directory.\n");
776
777		// remove all packages already added
778		{
779			VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
780			VolumeWriteLocker volumeLocker(this);
781			_RemoveAllPackages();
782		}
783
784		// read the whole directory
785		error = _AddInitialPackagesFromDirectory();
786		if (error != B_OK)
787			RETURN_ERROR(error);
788	}
789
790	// add the packages to the node tree
791	VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
792	VolumeWriteLocker volumeLocker(this);
793	for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator();
794		Package* package = it.Next();) {
795		error = _AddPackageContent(package, false);
796		if (error != B_OK) {
797			for (it.Rewind(); Package* activePackage = it.Next();) {
798				if (activePackage == package)
799					break;
800				_RemovePackageContent(activePackage, NULL, false);
801			}
802			RETURN_ERROR(error);
803		}
804	}
805
806	return B_OK;
807}
808
809
810status_t
811Volume::_AddInitialPackagesFromActivationFile(
812	PackagesDirectory* packagesDirectory)
813{
814	// try reading the activation file
815	FileDescriptorCloser fd(openat(packagesDirectory->DirectoryFD(),
816		packagesDirectory == fPackagesDirectory
817			? kActivationFilePath : kActivationFileName,
818		O_RDONLY));
819	if (!fd.IsSet()) {
820		INFORM("Failed to open packages activation file: %s\n",
821			strerror(errno));
822		RETURN_ERROR(errno);
823	}
824
825	// read the whole file into memory to simplify things
826	struct stat st;
827	if (fstat(fd.Get(), &st) != 0) {
828		ERROR("Failed to stat packages activation file: %s\n",
829			strerror(errno));
830		RETURN_ERROR(errno);
831	}
832
833	if (st.st_size > (off_t)kMaxActivationFileSize) {
834		ERROR("The packages activation file is too big.\n");
835		RETURN_ERROR(B_BAD_DATA);
836	}
837
838	char* fileContent = (char*)malloc(st.st_size + 1);
839	if (fileContent == NULL)
840		RETURN_ERROR(B_NO_MEMORY);
841	MemoryDeleter fileContentDeleter(fileContent);
842
843	ssize_t bytesRead = read(fd.Get(), fileContent, st.st_size);
844	if (bytesRead < 0) {
845		ERROR("Failed to read packages activation file: %s\n", strerror(errno));
846		RETURN_ERROR(errno);
847	}
848
849	if (bytesRead != st.st_size) {
850		ERROR("Failed to read whole packages activation file\n");
851		RETURN_ERROR(B_ERROR);
852	}
853
854	// null-terminate to simplify parsing
855	fileContent[st.st_size] = '\0';
856
857	// parse the file and add the respective packages
858	const char* packageName = fileContent;
859	char* const fileContentEnd = fileContent + st.st_size;
860	while (packageName < fileContentEnd) {
861		char* packageNameEnd = strchr(packageName, '\n');
862		if (packageNameEnd == NULL)
863			packageNameEnd = fileContentEnd;
864
865		// skip empty lines
866		if (packageName == packageNameEnd) {
867			packageName++;
868			continue;
869		}
870		*packageNameEnd = '\0';
871
872		if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) {
873			ERROR("Invalid packages activation file content.\n");
874			RETURN_ERROR(B_BAD_DATA);
875		}
876
877		status_t error = _LoadAndAddInitialPackage(packagesDirectory,
878			packageName);
879		if (error != B_OK)
880			RETURN_ERROR(error);
881
882		packageName = packageNameEnd + 1;
883	}
884
885	return B_OK;
886}
887
888
889status_t
890Volume::_AddInitialPackagesFromDirectory()
891{
892	// iterate through the dir and create packages
893	int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY);
894	if (fd < 0) {
895		ERROR("Failed to open packages directory: %s\n", strerror(errno));
896		RETURN_ERROR(errno);
897	}
898
899	DirCloser dir(fdopendir(fd));
900	if (!dir.IsSet()) {
901		ERROR("Failed to open packages directory \"%s\": %s\n",
902			fPackagesDirectory->Path(), strerror(errno));
903		RETURN_ERROR(errno);
904	}
905
906	while (dirent* entry = readdir(dir.Get())) {
907		// skip "." and ".."
908		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
909			continue;
910
911		// also skip any entry without a ".hpkg" extension
912		size_t nameLength = strlen(entry->d_name);
913		if (nameLength < 5
914			|| memcmp(entry->d_name + nameLength - 5, ".hpkg", 5) != 0) {
915			continue;
916		}
917
918		_LoadAndAddInitialPackage(fPackagesDirectory, entry->d_name);
919	}
920
921	return B_OK;
922}
923
924
925status_t
926Volume::_LoadAndAddInitialPackage(PackagesDirectory* packagesDirectory,
927	const char* name)
928{
929	Package* package;
930	status_t error = _LoadPackage(packagesDirectory, name, package);
931	if (error != B_OK) {
932		ERROR("Failed to load package \"%s\": %s\n", name, strerror(error));
933		RETURN_ERROR(error);
934	}
935	BReference<Package> packageReference(package, true);
936
937	VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
938	VolumeWriteLocker volumeLocker(this);
939	_AddPackage(package);
940
941	return B_OK;
942}
943
944
945inline void
946Volume::_AddPackage(Package* package)
947{
948	fPackages.Insert(package);
949	package->AcquireReference();
950}
951
952
953inline void
954Volume::_RemovePackage(Package* package)
955{
956	fPackages.Remove(package);
957	package->ReleaseReference();
958}
959
960
961void
962Volume::_RemoveAllPackages()
963{
964	Package* package = fPackages.Clear(true);
965	while (package != NULL) {
966		Package* next = package->FileNameHashTableNext();
967		package->ReleaseReference();
968		package = next;
969	}
970}
971
972
973inline Package*
974Volume::_FindPackage(const char* fileName) const
975{
976	return fPackages.Lookup(fileName);
977}
978
979
980status_t
981Volume::_AddPackageContent(Package* package, bool notify)
982{
983	// Open the package. We don't need the FD here, but this is an optimization.
984	// The attribute indices may want to read the package nodes' attributes and
985	// the package file would be opened and closed for each attribute instance.
986	// Since Package keeps and shares the FD as long as at least one party has
987	// the package open, we prevent that.
988	int fd = package->Open();
989	if (fd < 0)
990		RETURN_ERROR(fd);
991	PackageCloser packageCloser(package);
992
993	status_t error = fPackageFSRoot->AddPackage(package);
994	if (error != B_OK)
995		RETURN_ERROR(error);
996
997	for (PackageNodeList::Iterator it = package->Nodes().GetIterator();
998			PackageNode* node = it.Next();) {
999		// skip over ".PackageInfo" file, it isn't part of the package content
1000		if (strcmp(node->Name(),
1001				BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
1002			continue;
1003		}
1004		error = _AddPackageContentRootNode(package, node, notify);
1005		if (error != B_OK) {
1006			_RemovePackageContent(package, node, notify);
1007			RETURN_ERROR(error);
1008		}
1009	}
1010
1011	return B_OK;
1012}
1013
1014
1015void
1016Volume::_RemovePackageContent(Package* package, PackageNode* endNode,
1017	bool notify)
1018{
1019	PackageNode* node = package->Nodes().Head();
1020	while (node != NULL) {
1021		if (node == endNode)
1022			break;
1023
1024		PackageNode* nextNode = package->Nodes().GetNext(node);
1025
1026		// skip over ".PackageInfo" file, it isn't part of the package content
1027		if (strcmp(node->Name(),
1028				BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) != 0) {
1029			_RemovePackageContentRootNode(package, node, NULL, notify);
1030		}
1031
1032		node = nextNode;
1033	}
1034
1035	fPackageFSRoot->RemovePackage(package);
1036}
1037
1038
1039/*!	This method recursively iterates through the descendents of the given
1040	package root node and adds all package nodes to the node tree in
1041	pre-order.
1042	Due to limited kernel stack space we avoid deep recursive function calls
1043	and rather use the package node stack implied by the tree.
1044*/
1045status_t
1046Volume::_AddPackageContentRootNode(Package* package,
1047	PackageNode* rootPackageNode, bool notify)
1048{
1049	ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
1050
1051	PackageNode* packageNode = rootPackageNode;
1052	Directory* directory = fRootDirectory;
1053	directory->WriteLock();
1054
1055	do {
1056		Node* node;
1057		status_t error = _AddPackageNode(directory, packageNode, notify, node);
1058			// returns B_OK with a NULL node, when skipping the node
1059		if (error != B_OK) {
1060			// unlock all directories
1061			while (directory != NULL) {
1062				directory->WriteUnlock();
1063				directory = directory->Parent();
1064			}
1065
1066			// remove the added package nodes
1067			_RemovePackageContentRootNode(package, rootPackageNode, packageNode,
1068				notify);
1069			RETURN_ERROR(error);
1070		}
1071
1072		// recurse into directory, unless we're supposed to skip the node
1073		if (node != NULL) {
1074			if (PackageDirectory* packageDirectory
1075					= dynamic_cast<PackageDirectory*>(packageNode)) {
1076				if (packageDirectory->FirstChild() != NULL) {
1077					directory = dynamic_cast<Directory*>(node);
1078					packageNode = packageDirectory->FirstChild();
1079					directory->WriteLock();
1080					continue;
1081				}
1082			}
1083		}
1084
1085		// continue with the next available (ancestors's) sibling
1086		do {
1087			PackageDirectory* packageDirectory = packageNode->Parent();
1088			PackageNode* sibling = packageDirectory != NULL
1089				? packageDirectory->NextChild(packageNode) : NULL;
1090
1091			if (sibling != NULL) {
1092				packageNode = sibling;
1093				break;
1094			}
1095
1096			// no more siblings -- go back up the tree
1097			packageNode = packageDirectory;
1098			directory->WriteUnlock();
1099			directory = directory->Parent();
1100				// the parent is still locked, so this is safe
1101		} while (packageNode != NULL);
1102	} while (packageNode != NULL);
1103
1104	ASSERT(directory == NULL);
1105
1106	return B_OK;
1107}
1108
1109
1110/*!	Recursively iterates through the descendents of the given package root node
1111	and removes all package nodes from the node tree in post-order, until
1112	encountering \a endPackageNode (if non-null).
1113	Due to limited kernel stack space we avoid deep recursive function calls
1114	and rather use the package node stack implied by the tree.
1115*/
1116void
1117Volume::_RemovePackageContentRootNode(Package* package,
1118	PackageNode* rootPackageNode, PackageNode* endPackageNode, bool notify)
1119{
1120	ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
1121
1122	PackageNode* packageNode = rootPackageNode;
1123	Directory* directory = fRootDirectory;
1124	directory->WriteLock();
1125
1126	do {
1127		if (packageNode == endPackageNode) {
1128			// unlock all directories
1129			while (directory != NULL) {
1130				directory->WriteUnlock();
1131				directory = directory->Parent();
1132			}
1133			break;
1134		}
1135
1136		// recurse into directory
1137		if (PackageDirectory* packageDirectory
1138				= dynamic_cast<PackageDirectory*>(packageNode)) {
1139			if (packageDirectory->FirstChild() != NULL) {
1140				if (Directory* childDirectory = dynamic_cast<Directory*>(
1141						directory->FindChild(packageNode->Name()))) {
1142					directory = childDirectory;
1143					packageNode = packageDirectory->FirstChild();
1144					directory->WriteLock();
1145					continue;
1146				}
1147			}
1148		}
1149
1150		// continue with the next available (ancestors's) sibling
1151		do {
1152			PackageDirectory* packageDirectory = packageNode->Parent();
1153			PackageNode* sibling = packageDirectory != NULL
1154				? packageDirectory->NextChild(packageNode) : NULL;
1155
1156			// we're done with the node -- remove it
1157			_RemovePackageNode(directory, packageNode,
1158				directory->FindChild(packageNode->Name()), notify);
1159
1160			if (sibling != NULL) {
1161				packageNode = sibling;
1162				break;
1163			}
1164
1165			// no more siblings -- go back up the tree
1166			packageNode = packageDirectory;
1167			directory->WriteUnlock();
1168			directory = directory->Parent();
1169				// the parent is still locked, so this is safe
1170		} while (packageNode != NULL/* && packageNode != rootPackageNode*/);
1171	} while (packageNode != NULL/* && packageNode != rootPackageNode*/);
1172
1173	ASSERT(directory == NULL);
1174}
1175
1176
1177status_t
1178Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
1179	bool notify, Node*& _node)
1180{
1181	bool newNode = false;
1182	UnpackingNode* unpackingNode;
1183	Node* node = directory->FindChild(packageNode->Name());
1184	PackageNode* oldPackageNode = NULL;
1185
1186	if (node != NULL) {
1187		unpackingNode = dynamic_cast<UnpackingNode*>(node);
1188		if (unpackingNode == NULL) {
1189			_node = NULL;
1190			return B_OK;
1191		}
1192		oldPackageNode = unpackingNode->GetPackageNode();
1193	} else {
1194		status_t error = _CreateUnpackingNode(packageNode->Mode(), directory,
1195			packageNode->Name(), unpackingNode);
1196		if (error != B_OK)
1197			RETURN_ERROR(error);
1198
1199		node = unpackingNode->GetNode();
1200		newNode = true;
1201	}
1202
1203	BReference<Node> nodeReference(node);
1204	NodeWriteLocker nodeWriteLocker(node);
1205
1206	BReference<Node> newNodeReference;
1207	NodeWriteLocker newNodeWriteLocker;
1208	Node* oldNode = NULL;
1209
1210	if (!newNode && !S_ISDIR(node->Mode()) && oldPackageNode != NULL
1211		&& unpackingNode->WillBeFirstPackageNode(packageNode)) {
1212		// The package node we're going to add will represent the node,
1213		// replacing the current head package node. Since the node isn't a
1214		// directory, we must make sure that clients having opened or mapped the
1215		// node won't be surprised. So we create a new node and remove the
1216		// current one.
1217		// create a new node and transfer the package nodes to it
1218		UnpackingNode* newUnpackingNode;
1219		status_t error = unpackingNode->CloneTransferPackageNodes(
1220			fNextNodeID++, newUnpackingNode);
1221		if (error != B_OK)
1222			RETURN_ERROR(error);
1223
1224		// remove the old node
1225		_NotifyNodeRemoved(node);
1226		_RemoveNodeAndVNode(node);
1227		oldNode = node;
1228
1229		// add the new node
1230		unpackingNode = newUnpackingNode;
1231		node = unpackingNode->GetNode();
1232		newNodeReference.SetTo(node);
1233		newNodeWriteLocker.SetTo(node, false);
1234
1235		directory->AddChild(node);
1236		fNodes.Insert(node);
1237		newNode = true;
1238	}
1239
1240	status_t error = unpackingNode->AddPackageNode(packageNode, ID());
1241	if (error != B_OK) {
1242		// Remove the node, if created before. If the node was created to
1243		// replace the previous node, send out notifications instead.
1244		if (newNode) {
1245			if (oldNode != NULL) {
1246				_NotifyNodeAdded(node);
1247				if (notify) {
1248					notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
1249						oldNode->ID());
1250					notify_entry_created(ID(), directory->ID(), node->Name(),
1251						node->ID());
1252				}
1253			} else
1254				_RemoveNode(node);
1255		}
1256		RETURN_ERROR(error);
1257	}
1258
1259	if (newNode) {
1260		_NotifyNodeAdded(node);
1261	} else if (packageNode == unpackingNode->GetPackageNode()) {
1262		_NotifyNodeChanged(node, kAllStatFields,
1263			OldUnpackingNodeAttributes(oldPackageNode));
1264	}
1265
1266	if (notify) {
1267		if (newNode) {
1268			if (oldNode != NULL) {
1269				notify_entry_removed(ID(), directory->ID(), oldNode->Name(),
1270					oldNode->ID());
1271			}
1272			notify_entry_created(ID(), directory->ID(), node->Name(),
1273				node->ID());
1274		} else if (packageNode == unpackingNode->GetPackageNode()) {
1275			// The new package node has become the one representing the node.
1276			// Send stat changed notification for directories and entry
1277			// removed + created notifications for files and symlinks.
1278			notify_stat_changed(ID(), directory->ID(), node->ID(),
1279				kAllStatFields);
1280			// TODO: Actually the attributes might change, too!
1281		}
1282	}
1283
1284	_node = node;
1285	return B_OK;
1286}
1287
1288
1289void
1290Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
1291	Node* node, bool notify)
1292{
1293	UnpackingNode* unpackingNode = dynamic_cast<UnpackingNode*>(node);
1294	if (unpackingNode == NULL)
1295		return;
1296
1297	BReference<Node> nodeReference(node);
1298	NodeWriteLocker nodeWriteLocker(node);
1299
1300	PackageNode* headPackageNode = unpackingNode->GetPackageNode();
1301	bool nodeRemoved = false;
1302	Node* newNode = NULL;
1303
1304	BReference<Node> newNodeReference;
1305	NodeWriteLocker newNodeWriteLocker;
1306
1307	// If this is the last package node of the node, remove it completely.
1308	if (unpackingNode->IsOnlyPackageNode(packageNode)) {
1309		// Notify before removing the node. Otherwise the indices might not
1310		// find the node anymore.
1311		_NotifyNodeRemoved(node);
1312
1313		unpackingNode->PrepareForRemoval();
1314
1315		_RemoveNodeAndVNode(node);
1316		nodeRemoved = true;
1317	} else if (packageNode == headPackageNode) {
1318		// The node does at least have one more package node, but the one to be
1319		// removed is the head. Unless it's a directory, we replace the node
1320		// with a completely new one and let the old one die. This is necessary
1321		// to avoid surprises for clients that have opened/mapped the node.
1322		if (S_ISDIR(packageNode->Mode())) {
1323			unpackingNode->RemovePackageNode(packageNode, ID());
1324			_NotifyNodeChanged(node, kAllStatFields,
1325				OldUnpackingNodeAttributes(headPackageNode));
1326		} else {
1327			// create a new node and transfer the package nodes to it
1328			UnpackingNode* newUnpackingNode;
1329			status_t error = unpackingNode->CloneTransferPackageNodes(
1330				fNextNodeID++, newUnpackingNode);
1331			if (error == B_OK) {
1332				// remove the package node
1333				newUnpackingNode->RemovePackageNode(packageNode, ID());
1334
1335				// remove the old node
1336				_NotifyNodeRemoved(node);
1337				_RemoveNodeAndVNode(node);
1338
1339				// add the new node
1340				newNode = newUnpackingNode->GetNode();
1341				newNodeReference.SetTo(newNode);
1342				newNodeWriteLocker.SetTo(newNode, false);
1343
1344				directory->AddChild(newNode);
1345				fNodes.Insert(newNode);
1346				_NotifyNodeAdded(newNode);
1347			} else {
1348				// There's nothing we can do. Remove the node completely.
1349				_NotifyNodeRemoved(node);
1350
1351				unpackingNode->PrepareForRemoval();
1352
1353				_RemoveNodeAndVNode(node);
1354				nodeRemoved = true;
1355			}
1356		}
1357	} else {
1358		// The package node to remove is not the head of the node. This change
1359		// doesn't have any visible effect.
1360		unpackingNode->RemovePackageNode(packageNode, ID());
1361	}
1362
1363	if (!notify)
1364		return;
1365
1366	// send notifications
1367	if (nodeRemoved) {
1368		notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID());
1369	} else if (packageNode == headPackageNode) {
1370		// The removed package node was the one representing the node.
1371		// Send stat changed notification for directories and entry
1372		// removed + created notifications for files and symlinks.
1373		if (S_ISDIR(packageNode->Mode())) {
1374			notify_stat_changed(ID(), directory->ID(), node->ID(),
1375				kAllStatFields);
1376			// TODO: Actually the attributes might change, too!
1377		} else {
1378			notify_entry_removed(ID(), directory->ID(), node->Name(),
1379				node->ID());
1380			notify_entry_created(ID(), directory->ID(), newNode->Name(),
1381				newNode->ID());
1382		}
1383	}
1384}
1385
1386
1387status_t
1388Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const String& name,
1389	UnpackingNode*& _node)
1390{
1391	UnpackingNode* unpackingNode;
1392	if (S_ISREG(mode) || S_ISLNK(mode))
1393		unpackingNode = new(std::nothrow) UnpackingLeafNode(fNextNodeID++);
1394	else if (S_ISDIR(mode))
1395		unpackingNode = new(std::nothrow) UnpackingDirectory(fNextNodeID++);
1396	else
1397		RETURN_ERROR(B_UNSUPPORTED);
1398
1399	if (unpackingNode == NULL)
1400		RETURN_ERROR(B_NO_MEMORY);
1401
1402	Node* node = unpackingNode->GetNode();
1403	BReference<Node> nodeReference(node, true);
1404
1405	status_t error = node->Init(parent, name);
1406	if (error != B_OK)
1407		RETURN_ERROR(error);
1408
1409	parent->AddChild(node);
1410
1411	fNodes.Insert(node);
1412	nodeReference.Detach();
1413		// we keep the initial node reference for the table
1414
1415	_node = unpackingNode;
1416	return B_OK;
1417}
1418
1419
1420void
1421Volume::_RemoveNode(Node* node)
1422{
1423	// remove from parent
1424	Directory* parent = node->Parent();
1425	parent->RemoveChild(node);
1426
1427	// remove from node table
1428	fNodes.Remove(node);
1429	node->ReleaseReference();
1430}
1431
1432
1433void
1434Volume::_RemoveNodeAndVNode(Node* node)
1435{
1436	// If the node is known to the VFS, we get the vnode, remove it, and put it,
1437	// so that the VFS will discard it as soon as possible (i.e. now, if no one
1438	// else is using it).
1439	NodeWriteLocker nodeWriteLocker(node);
1440
1441	// Remove the node from its parent and the volume. This makes the node
1442	// inaccessible via the get_vnode() and lookup() hooks.
1443	_RemoveNode(node);
1444
1445	bool getVNode = node->IsKnownToVFS();
1446
1447	nodeWriteLocker.Unlock();
1448
1449	// Get a vnode reference, if the node is already known to the VFS.
1450	Node* dummyNode;
1451	if (getVNode && GetVNode(node->ID(), dummyNode) == B_OK) {
1452		// TODO: There still is a race condition here which we can't avoid
1453		// without more help from the VFS. Right after we drop the write
1454		// lock a vnode for the node could be discarded by the VFS. At that
1455		// point another thread trying to get the vnode by ID would create
1456		// a vnode, mark it busy and call our get_vnode() hook. It would
1457		// block since we (i.e. the package loader thread executing this
1458		// method) still have the volume write lock. Our get_vnode() call
1459		// would block, since it finds the vnode marked busy. It times out
1460		// eventually, but until then a good deal of FS operations might
1461		// block as well due to us holding the volume lock and probably
1462		// several node locks as well. A get_vnode*() variant (e.g.
1463		// get_vnode_etc() with flags parameter) that wouldn't block and
1464		// only get the vnode, if already loaded and non-busy, would be
1465		// perfect here.
1466		RemoveVNode(node->ID());
1467		PutVNode(node->ID());
1468	}
1469}
1470
1471
1472status_t
1473Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name,
1474	Package*& _package)
1475{
1476	// Find the package -- check the specified packages directory and iterate
1477	// toward the newer states.
1478	struct stat st;
1479	for (;;) {
1480		if (packagesDirectory == NULL)
1481			return B_ENTRY_NOT_FOUND;
1482
1483		if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) {
1484			// check whether the entry is a file
1485			if (!S_ISREG(st.st_mode))
1486				return B_BAD_VALUE;
1487			break;
1488		}
1489
1490		packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory);
1491	}
1492
1493	// create a package
1494	Package* package = new(std::nothrow) Package(this, packagesDirectory,
1495		st.st_dev, st.st_ino);
1496	if (package == NULL)
1497		RETURN_ERROR(B_NO_MEMORY);
1498	BReference<Package> packageReference(package, true);
1499
1500	status_t error = package->Init(name);
1501	if (error != B_OK)
1502		return error;
1503
1504	error = package->Load(fPackageSettings);
1505	if (error != B_OK)
1506		return error;
1507
1508	_package = packageReference.Detach();
1509	return B_OK;
1510}
1511
1512
1513status_t
1514Volume::_ChangeActivation(ActivationChangeRequest& request)
1515{
1516	uint32 itemCount = request.CountItems();
1517	if (itemCount == 0)
1518		return B_OK;
1519
1520	// first check the request
1521	int32 newPackageCount = 0;
1522	int32 oldPackageCount = 0;
1523	{
1524		VolumeReadLocker volumeLocker(this);
1525
1526		for (uint32 i = 0; i < itemCount; i++) {
1527			PackageFSActivationChangeItem* item = request.ItemAt(i);
1528			if (item->parentDeviceID != fPackagesDirectory->DeviceID()
1529				|| item->parentDirectoryID != fPackagesDirectory->NodeID()) {
1530				ERROR("Volume::_ChangeActivation(): mismatching packages "
1531					"directory\n");
1532				RETURN_ERROR(B_MISMATCHED_VALUES);
1533			}
1534
1535			Package* package = _FindPackage(item->name);
1536// TODO: We should better look up the package by node_ref!
1537			if (item->type == PACKAGE_FS_ACTIVATE_PACKAGE) {
1538				if (package != NULL) {
1539					ERROR("Volume::_ChangeActivation(): package to activate "
1540						"already activated: \"%s\"\n", item->name);
1541					RETURN_ERROR(B_NAME_IN_USE);
1542				}
1543				newPackageCount++;
1544			} else if (item->type == PACKAGE_FS_DEACTIVATE_PACKAGE) {
1545				if (package == NULL) {
1546					ERROR("Volume::_ChangeActivation(): package to deactivate "
1547						"not found: \"%s\"\n", item->name);
1548					RETURN_ERROR(B_NAME_NOT_FOUND);
1549				}
1550				oldPackageCount++;
1551			} else if (item->type == PACKAGE_FS_REACTIVATE_PACKAGE) {
1552				if (package == NULL) {
1553					ERROR("Volume::_ChangeActivation(): package to reactivate "
1554						"not found: \"%s\"\n", item->name);
1555					RETURN_ERROR(B_NAME_NOT_FOUND);
1556				}
1557				oldPackageCount++;
1558				newPackageCount++;
1559			} else
1560				RETURN_ERROR(B_BAD_VALUE);
1561		}
1562	}
1563
1564	INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32
1565		" old packages\n", newPackageCount, oldPackageCount);
1566
1567	// Things look good so far -- allocate reference arrays for the packages to
1568	// add and remove.
1569	BReference<Package>* newPackageReferences
1570		= new(std::nothrow) BReference<Package>[newPackageCount];
1571	if (newPackageReferences == NULL)
1572		RETURN_ERROR(B_NO_MEMORY);
1573	ArrayDeleter<BReference<Package> > newPackageReferencesDeleter(
1574			newPackageReferences);
1575
1576	BReference<Package>* oldPackageReferences
1577		= new(std::nothrow) BReference<Package>[oldPackageCount];
1578	if (oldPackageReferences == NULL)
1579		RETURN_ERROR(B_NO_MEMORY);
1580	ArrayDeleter<BReference<Package> > oldPackageReferencesDeleter(
1581			oldPackageReferences);
1582
1583	// load all new packages
1584	int32 newPackageIndex = 0;
1585	for (uint32 i = 0; i < itemCount; i++) {
1586		PackageFSActivationChangeItem* item = request.ItemAt(i);
1587
1588		if (item->type != PACKAGE_FS_ACTIVATE_PACKAGE
1589			&& item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
1590			continue;
1591		}
1592
1593		Package* package;
1594		status_t error = _LoadPackage(fPackagesDirectory, item->name, package);
1595		if (error != B_OK) {
1596			ERROR("Volume::_ChangeActivation(): failed to load package "
1597				"\"%s\"\n", item->name);
1598			RETURN_ERROR(error);
1599		}
1600
1601		newPackageReferences[newPackageIndex++].SetTo(package, true);
1602	}
1603
1604	// apply the changes
1605	VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf());
1606	VolumeWriteLocker volumeLocker(this);
1607// TODO: Add a change counter to Volume, so we can easily check whether
1608// everything is still the same.
1609
1610	// remove the old packages
1611	int32 oldPackageIndex = 0;
1612	for (uint32 i = 0; i < itemCount; i++) {
1613		PackageFSActivationChangeItem* item = request.ItemAt(i);
1614
1615		if (item->type != PACKAGE_FS_DEACTIVATE_PACKAGE
1616			&& item->type != PACKAGE_FS_REACTIVATE_PACKAGE) {
1617			continue;
1618		}
1619
1620		Package* package = _FindPackage(item->name);
1621// TODO: We should better look up the package by node_ref!
1622		oldPackageReferences[oldPackageIndex++].SetTo(package);
1623		_RemovePackageContent(package, NULL, true);
1624		_RemovePackage(package);
1625
1626		INFORM("package \"%s\" deactivated\n", package->FileName().Data());
1627	}
1628// TODO: Since package removal cannot fail, consider adding the new packages
1629// first. The reactivation case may make that problematic, since two packages
1630// with the same name would be active after activating the new one. Check!
1631
1632	// add the new packages
1633	status_t error = B_OK;
1634	for (newPackageIndex = 0; newPackageIndex < newPackageCount;
1635		newPackageIndex++) {
1636		Package* package = newPackageReferences[newPackageIndex];
1637		_AddPackage(package);
1638
1639		// add the package to the node tree
1640		error = _AddPackageContent(package, true);
1641		if (error != B_OK) {
1642			_RemovePackage(package);
1643			break;
1644		}
1645		INFORM("package \"%s\" activated\n", package->FileName().Data());
1646	}
1647
1648	// Try to roll back the changes, if an error occurred.
1649	if (error != B_OK) {
1650		for (int32 i = newPackageIndex - 1; i >= 0; i--) {
1651			Package* package = newPackageReferences[i];
1652			_RemovePackageContent(package, NULL, true);
1653			_RemovePackage(package);
1654		}
1655
1656		for (int32 i = oldPackageCount - 1; i >= 0; i--) {
1657			Package* package = oldPackageReferences[i];
1658			_AddPackage(package);
1659
1660			if (_AddPackageContent(package, true) != B_OK) {
1661				// nothing we can do here
1662				ERROR("Volume::_ChangeActivation(): failed to roll back "
1663					"deactivation of package \"%s\" after error\n",
1664		  			package->FileName().Data());
1665				_RemovePackage(package);
1666			}
1667		}
1668	}
1669
1670	return error;
1671}
1672
1673
1674status_t
1675Volume::_InitMountType(const char* mountType)
1676{
1677	if (mountType == NULL)
1678		fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
1679	else if (strcmp(mountType, "system") == 0)
1680		fMountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM;
1681	else if (strcmp(mountType, "home") == 0)
1682		fMountType = PACKAGE_FS_MOUNT_TYPE_HOME;
1683	else if (strcmp(mountType, "custom") == 0)
1684		fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM;
1685	else
1686		RETURN_ERROR(B_BAD_VALUE);
1687
1688	return B_OK;
1689}
1690
1691
1692status_t
1693Volume::_CreateShineThroughDirectory(Directory* parent, const char* name,
1694	Directory*& _directory)
1695{
1696	ShineThroughDirectory* directory = new(std::nothrow) ShineThroughDirectory(
1697		fNextNodeID++);
1698	if (directory == NULL)
1699		RETURN_ERROR(B_NO_MEMORY);
1700	BReference<ShineThroughDirectory> directoryReference(directory, true);
1701
1702	String nameString;
1703	if (!nameString.SetTo(name))
1704		RETURN_ERROR(B_NO_MEMORY);
1705
1706	status_t error = directory->Init(parent, nameString);
1707	if (error != B_OK)
1708		RETURN_ERROR(error);
1709
1710	parent->AddChild(directory);
1711
1712	fNodes.Insert(directory);
1713	directoryReference.Detach();
1714		// we keep the initial node reference for the table
1715
1716	_directory = directory;
1717	return B_OK;
1718}
1719
1720
1721status_t
1722Volume::_CreateShineThroughDirectories(const char* shineThroughSetting)
1723{
1724	// get the directories to map
1725	const char* const* directories = NULL;
1726
1727	if (shineThroughSetting == NULL) {
1728		// nothing specified -- derive from mount type
1729		switch (fMountType) {
1730			case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
1731			case PACKAGE_FS_MOUNT_TYPE_HOME:
1732				directories = kShineThroughDirectories;
1733				break;
1734			case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
1735				return B_OK;
1736			case PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT:
1737				return B_BAD_VALUE;
1738		}
1739	} else if (strcmp(shineThroughSetting, "system") == 0)
1740		directories = kShineThroughDirectories;
1741	else if (strcmp(shineThroughSetting, "home") == 0)
1742		directories = kShineThroughDirectories;
1743	else if (strcmp(shineThroughSetting, "none") == 0)
1744		directories = NULL;
1745	else
1746		RETURN_ERROR(B_BAD_VALUE);
1747
1748	if (directories == NULL)
1749		return B_OK;
1750
1751	// iterate through the directory list and create the directories
1752	while (const char* directoryName = *(directories++)) {
1753		// create the directory
1754		Directory* directory;
1755		status_t error = _CreateShineThroughDirectory(fRootDirectory,
1756			directoryName, directory);
1757		if (error != B_OK)
1758			RETURN_ERROR(error);
1759	}
1760
1761	return B_OK;
1762}
1763
1764
1765status_t
1766Volume::_PublishShineThroughDirectories()
1767{
1768	// Iterate through the root directory children and bind the shine-through
1769	// directories to the respective mount point subdirectories.
1770	Node* nextNode;
1771	for (Node* node = fRootDirectory->FirstChild(); node != NULL;
1772			node = nextNode) {
1773		nextNode = fRootDirectory->NextChild(node);
1774
1775		// skip anything but shine-through directories
1776		ShineThroughDirectory* directory
1777			= dynamic_cast<ShineThroughDirectory*>(node);
1778		if (directory == NULL)
1779			continue;
1780
1781		const char* directoryName = directory->Name();
1782
1783		// look up the mount point subdirectory
1784		struct vnode* vnode;
1785		status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID,
1786			fMountPoint.nodeID, directoryName, &vnode);
1787		if (error != B_OK) {
1788			dprintf("packagefs: Failed to get shine-through directory \"%s\": "
1789				"%s\n", directoryName, strerror(error));
1790			_RemoveNode(directory);
1791			continue;
1792		}
1793		VnodePutter vnodePutter(vnode);
1794
1795		// stat it
1796		struct stat st;
1797		error = vfs_stat_vnode(vnode, &st);
1798		if (error != B_OK) {
1799			dprintf("packagefs: Failed to stat shine-through directory \"%s\": "
1800				"%s\n", directoryName, strerror(error));
1801			_RemoveNode(directory);
1802			continue;
1803		}
1804
1805		if (!S_ISDIR(st.st_mode)) {
1806			dprintf("packagefs: Shine-through entry \"%s\" is not a "
1807				"directory\n", directoryName);
1808			_RemoveNode(directory);
1809			continue;
1810		}
1811
1812		// publish the vnode, so the VFS will find it without asking us
1813		directory->AcquireReference();
1814		error = PublishVNode(directory);
1815		if (error != B_OK) {
1816			directory->ReleaseReference();
1817			_RemoveNode(directory);
1818			RETURN_ERROR(error);
1819		}
1820
1821		// bind the directory
1822		error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id,
1823			directory->ID());
1824
1825		PutVNode(directory->ID());
1826			// release our reference again -- on success
1827			// vfs_bind_mount_directory() got one
1828
1829		if (error != B_OK)
1830			RETURN_ERROR(error);
1831	}
1832
1833	return B_OK;
1834}
1835
1836
1837status_t
1838Volume::_AddPackageLinksDirectory()
1839{
1840	// called when mounting, so we don't need to lock the volume
1841
1842	PackageLinksDirectory* packageLinksDirectory
1843		= fPackageFSRoot->GetPackageLinksDirectory();
1844
1845	NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1846	NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
1847
1848	packageLinksDirectory->SetParent(fRootDirectory);
1849	fRootDirectory->AddChild(packageLinksDirectory);
1850
1851	_AddPackageLinksNode(packageLinksDirectory);
1852
1853	packageLinksDirectory->SetListener(this);
1854
1855	return B_OK;
1856}
1857
1858
1859void
1860Volume::_RemovePackageLinksDirectory()
1861{
1862	PackageLinksDirectory* packageLinksDirectory
1863		= fPackageFSRoot->GetPackageLinksDirectory();
1864
1865	VolumeWriteLocker volumeLocker(this);
1866	NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory);
1867	NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory);
1868
1869	if (packageLinksDirectory->Parent() == fRootDirectory) {
1870		packageLinksDirectory->SetListener(NULL);
1871		fRootDirectory->RemoveChild(packageLinksDirectory);
1872		packageLinksDirectory->SetParent(NULL);
1873	}
1874}
1875
1876
1877void
1878Volume::_AddPackageLinksNode(Node* node)
1879{
1880	node->SetID(fNextNodeID++);
1881
1882	fNodes.Insert(node);
1883	node->AcquireReference();
1884
1885	// If this is a directory, recursively add descendants. The directory tree
1886	// for the package links isn't deep, so we can do recursion.
1887	if (Directory* directory = dynamic_cast<Directory*>(node)) {
1888		for (Node* child = directory->FirstChild(); child != NULL;
1889				child = directory->NextChild(child)) {
1890			NodeWriteLocker childWriteLocker(child);
1891			_AddPackageLinksNode(child);
1892		}
1893	}
1894}
1895
1896
1897void
1898Volume::_RemovePackageLinksNode(Node* node)
1899{
1900	// If this is a directory, recursively remove descendants. The directory
1901	// tree for the package links isn't deep, so we can do recursion.
1902	if (Directory* directory = dynamic_cast<Directory*>(node)) {
1903		for (Node* child = directory->FirstChild(); child != NULL;
1904				child = directory->NextChild(child)) {
1905			NodeWriteLocker childWriteLocker(child);
1906			_RemovePackageLinksNode(child);
1907		}
1908	}
1909
1910	fNodes.Remove(node);
1911	node->ReleaseReference();
1912}
1913
1914
1915inline Volume*
1916Volume::_SystemVolumeIfNotSelf() const
1917{
1918	if (Volume* systemVolume = fPackageFSRoot->SystemVolume())
1919		return systemVolume == this ? NULL : systemVolume;
1920	return NULL;
1921}
1922
1923
1924void
1925Volume::_NotifyNodeAdded(Node* node)
1926{
1927	Node* key = node;
1928
1929	for (int i = 0; i < 2; i++) {
1930		if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1931			NodeListener* last = listener->PreviousNodeListener();
1932
1933			while (true) {
1934				NodeListener* next = listener->NextNodeListener();
1935
1936				listener->NodeAdded(node);
1937
1938				if (listener == last)
1939					break;
1940
1941				listener = next;
1942			}
1943		}
1944
1945		key = NULL;
1946	}
1947}
1948
1949
1950void
1951Volume::_NotifyNodeRemoved(Node* node)
1952{
1953	Node* key = node;
1954
1955	for (int i = 0; i < 2; i++) {
1956		if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1957			NodeListener* last = listener->PreviousNodeListener();
1958
1959			while (true) {
1960				NodeListener* next = listener->NextNodeListener();
1961
1962				listener->NodeRemoved(node);
1963
1964				if (listener == last)
1965					break;
1966
1967				listener = next;
1968			}
1969		}
1970
1971		key = NULL;
1972	}
1973}
1974
1975
1976void
1977Volume::_NotifyNodeChanged(Node* node, uint32 statFields,
1978	const OldNodeAttributes& oldAttributes)
1979{
1980	Node* key = node;
1981
1982	for (int i = 0; i < 2; i++) {
1983		if (NodeListener* listener = fNodeListeners.Lookup(key)) {
1984			NodeListener* last = listener->PreviousNodeListener();
1985
1986			while (true) {
1987				NodeListener* next = listener->NextNodeListener();
1988
1989				listener->NodeChanged(node, statFields, oldAttributes);
1990
1991				if (listener == last)
1992					break;
1993
1994				listener = next;
1995			}
1996		}
1997
1998		key = NULL;
1999	}
2000}
2001