1/*
2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "PackageFSRoot.h"
8
9#include <AutoDeleterDrivers.h>
10
11#include <vfs.h>
12
13#include "DebugSupport.h"
14#include "PackageLinksDirectory.h"
15#include "StringConstants.h"
16
17
18//#define TRACE_DEPENDENCIES_ENABLED
19#ifdef TRACE_DEPENDENCIES_ENABLED
20#	define TRACE_DEPENDENCIES(x...)	TPRINT(x)
21#else
22#	define TRACE_DEPENDENCIES(x...)	do {} while (false)
23#endif
24
25
26mutex PackageFSRoot::sRootListLock = MUTEX_INITIALIZER("packagefs root list");
27PackageFSRoot::RootList PackageFSRoot::sRootList;
28
29
30PackageFSRoot::PackageFSRoot(dev_t deviceID, ino_t nodeID)
31	:
32	fDeviceID(deviceID),
33	fNodeID(nodeID),
34	fSystemVolume(NULL),
35	fPackageLinksDirectory(NULL)
36{
37	rw_lock_init(&fLock, "packagefs root");
38}
39
40
41PackageFSRoot::~PackageFSRoot()
42{
43	if (fPackageLinksDirectory != NULL)
44		fPackageLinksDirectory->ReleaseReference();
45
46	rw_lock_destroy(&fLock);
47}
48
49
50/*static*/ status_t
51PackageFSRoot::GlobalInit()
52{
53	return B_OK;
54}
55
56
57/*static*/ void
58PackageFSRoot::GlobalUninit()
59{
60}
61
62
63status_t
64PackageFSRoot::Init()
65{
66	// create package links directory
67	fPackageLinksDirectory = new(std::nothrow) PackageLinksDirectory;
68	if (fPackageLinksDirectory == NULL)
69		return B_NO_MEMORY;
70
71	status_t error = fPackageLinksDirectory->Init(NULL,
72		StringConstants::Get().kPackageLinksDirectoryName);
73	if (error != B_OK)
74		RETURN_ERROR(error);
75
76	error = fResolvables.Init();
77	if (error != B_OK)
78		RETURN_ERROR(error);
79
80	error = fDependencies.Init();
81	if (error != B_OK)
82		RETURN_ERROR(error);
83
84	return B_OK;
85}
86
87
88/*static*/ status_t
89PackageFSRoot::RegisterVolume(Volume* volume)
90{
91	// Unless the volume is custom mounted, we stat the supposed root directory.
92	// Get the volume mount point relative path to the root directory depending
93	// on the mount type.
94	const char* relativeRootPath = NULL;
95
96	switch (volume->MountType()) {
97		case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
98			relativeRootPath = "..";
99			break;
100		case PACKAGE_FS_MOUNT_TYPE_HOME:
101			relativeRootPath = "../..";
102			break;
103		case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
104		default:
105			break;
106	}
107
108	if (relativeRootPath != NULL) {
109		struct vnode* vnode;
110		status_t error = vfs_entry_ref_to_vnode(volume->MountPointDeviceID(),
111			volume->MountPointNodeID(), relativeRootPath, &vnode);
112		if (error != B_OK) {
113			dprintf("packagefs: Failed to get root directory \"%s\": %s\n",
114				relativeRootPath, strerror(error));
115			RETURN_ERROR(error);
116		}
117		VnodePutter vnodePutter(vnode);
118
119		// stat it
120		struct stat st;
121		error = vfs_stat_vnode(vnode, &st);
122		if (error != B_OK) {
123			dprintf("packagefs: Failed to stat root directory \"%s\": %s\n",
124				relativeRootPath, strerror(error));
125			RETURN_ERROR(error);
126		}
127
128		// get/create the root
129		PackageFSRoot* root;
130		error = PackageFSRoot::_GetOrCreateRoot(st.st_dev, st.st_ino, root);
131		if (error != B_OK)
132			RETURN_ERROR(error);
133
134		// add the volume
135		error = root->_AddVolume(volume);
136		if (error != B_OK) {
137			_PutRoot(root);
138			RETURN_ERROR(error);
139		}
140
141		return B_OK;
142	}
143
144	// custom mount -- always create a new root
145	PackageFSRoot* root = new(std::nothrow) PackageFSRoot(-1, 0);
146	if (root == NULL)
147		return B_NO_MEMORY;
148	ObjectDeleter<PackageFSRoot> rootDeleter(root);
149
150	status_t error = root->Init();
151	if (error != B_OK)
152		RETURN_ERROR(error);
153
154	// add the volume
155	error = root->_AddVolume(volume);
156	if (error != B_OK) {
157		_PutRoot(root);
158		RETURN_ERROR(error);
159	}
160
161	// We don't add the root to the list.
162	rootDeleter.Detach();
163	return B_OK;
164}
165
166
167void
168PackageFSRoot::UnregisterVolume(Volume* volume)
169{
170	_RemoveVolume(volume);
171	_PutRoot(this);
172}
173
174
175status_t
176PackageFSRoot::AddPackage(Package* package)
177{
178	PackageFSRootWriteLocker writeLocker(this);
179
180	status_t error = _AddPackage(package);
181	if (error != B_OK) {
182		_RemovePackage(package);
183		RETURN_ERROR(error);
184	}
185
186	return B_OK;
187}
188
189
190void
191PackageFSRoot::RemovePackage(Package* package)
192{
193	PackageFSRootWriteLocker writeLocker(this);
194
195	_RemovePackage(package);
196}
197
198
199Volume*
200PackageFSRoot::SystemVolume() const
201{
202	PackageFSRootReadLocker readLocker(this);
203	return fSystemVolume;
204}
205
206
207status_t
208PackageFSRoot::_AddVolume(Volume* volume)
209{
210	PackageFSRootWriteLocker writeLocker(this);
211
212	volume->SetPackageFSRoot(this);
213
214	fVolumes.Add(volume);
215		// TODO: Correct order?
216
217	if (fSystemVolume == NULL && volume->MountType()
218			== PACKAGE_FS_MOUNT_TYPE_SYSTEM) {
219		fSystemVolume = volume;
220	}
221
222	return B_OK;
223}
224
225
226void
227PackageFSRoot::_RemoveVolume(Volume* volume)
228{
229	PackageFSRootWriteLocker writeLocker(this);
230
231	if (volume == fSystemVolume)
232		fSystemVolume = NULL;
233
234	fVolumes.Remove(volume);
235
236	volume->SetPackageFSRoot(NULL);
237}
238
239
240status_t
241PackageFSRoot::_AddPackage(Package* package)
242{
243	TRACE_DEPENDENCIES("adding package \"%s\"\n", package->Name().Data());
244
245	ResolvableDependencyList dependenciesToUpdate;
246
247	// register resolvables
248	for (ResolvableList::ConstIterator it
249				= package->Resolvables().GetIterator();
250			Resolvable* resolvable = it.Next();) {
251		TRACE_DEPENDENCIES("  adding resolvable \"%s\"\n",
252			resolvable->Name().Data());
253
254		if (ResolvableFamily* family
255				= fResolvables.Lookup(resolvable->Name())) {
256			family->AddResolvable(resolvable, dependenciesToUpdate);
257		} else {
258			family = new(std::nothrow) ResolvableFamily;
259			if (family == NULL)
260				return B_NO_MEMORY;
261
262			family->AddResolvable(resolvable, dependenciesToUpdate);
263			fResolvables.Insert(family);
264
265			// add pre-existing dependencies for that resolvable
266			if (DependencyFamily* dependencyFamily
267					= fDependencies.Lookup(resolvable->Name())) {
268				dependencyFamily->AddDependenciesToList(dependenciesToUpdate);
269			}
270		}
271	}
272
273	// register dependencies
274	for (DependencyList::ConstIterator it
275				= package->Dependencies().GetIterator();
276			Dependency* dependency = it.Next();) {
277		TRACE_DEPENDENCIES("  adding dependency \"%s\"\n",
278			dependency->Name().Data());
279
280		if (DependencyFamily* family
281				= fDependencies.Lookup(dependency->Name())) {
282			family->AddDependency(dependency);
283		} else {
284			family = new(std::nothrow) DependencyFamily;
285			if (family == NULL)
286				return B_NO_MEMORY;
287
288			family->AddDependency(dependency);
289			fDependencies.Insert(family);
290		}
291
292		dependenciesToUpdate.Add(dependency);
293	}
294
295	status_t error = fPackageLinksDirectory->AddPackage(package);
296	if (error != B_OK)
297		RETURN_ERROR(error);
298
299	_ResolveDependencies(dependenciesToUpdate);
300
301	return B_OK;
302}
303
304
305void
306PackageFSRoot::_RemovePackage(Package* package)
307{
308	TRACE_DEPENDENCIES("removing package \"%s\"\n", package->Name().Data());
309
310	fPackageLinksDirectory->RemovePackage(package);
311
312	// unregister dependencies
313	for (DependencyList::ConstIterator it
314				= package->Dependencies().GetIterator();
315			Dependency* dependency = it.Next();) {
316		if (DependencyFamily* family = dependency->Family()) {
317			TRACE_DEPENDENCIES("  removing dependency \"%s\"\n",
318				dependency->Name().Data());
319
320			if (family->IsLastDependency(dependency)) {
321				fDependencies.Remove(family);
322				family->RemoveDependency(dependency);
323				delete family;
324			} else
325				family->RemoveDependency(dependency);
326		}
327
328		if (Resolvable* resolvable = dependency->Resolvable())
329			resolvable->RemoveDependency(dependency);
330	}
331
332	// unregister resolvables
333	ResolvableDependencyList dependenciesToUpdate;
334
335	for (ResolvableList::ConstIterator it
336				= package->Resolvables().GetIterator();
337			Resolvable* resolvable = it.Next();) {
338		if (ResolvableFamily* family = resolvable->Family()) {
339			TRACE_DEPENDENCIES("  removing resolvable \"%s\"\n",
340				resolvable->Name().Data());
341
342			if (family->IsLastResolvable(resolvable)) {
343				fResolvables.Remove(family);
344				family->RemoveResolvable(resolvable, dependenciesToUpdate);
345				delete family;
346			} else
347				family->RemoveResolvable(resolvable, dependenciesToUpdate);
348		}
349	}
350
351	_ResolveDependencies(dependenciesToUpdate);
352}
353
354
355void
356PackageFSRoot::_ResolveDependencies(ResolvableDependencyList& dependencies)
357{
358	if (dependencies.IsEmpty())
359		return;
360
361	while (Dependency* dependency = dependencies.RemoveHead()) {
362		Package* package = dependency->Package();
363		_ResolveDependency(dependency);
364
365		// also resolve all other dependencies for that package
366		for (ResolvableDependencyList::Iterator it = dependencies.GetIterator();
367				(dependency = it.Next()) != NULL;) {
368			if (dependency->Package() == package) {
369				it.Remove();
370				_ResolveDependency(dependency);
371			}
372		}
373
374		fPackageLinksDirectory->UpdatePackageDependencies(package);
375	}
376}
377
378
379void
380PackageFSRoot::_ResolveDependency(Dependency* dependency)
381{
382	TRACE_DEPENDENCIES("  resolving dependency \"%s\" (package \"%s\")\n",
383		dependency->Name().Data(), dependency->Package()->Name().Data());
384
385	// get the resolvable family for the dependency
386	ResolvableFamily* resolvableFamily
387		= fResolvables.Lookup(dependency->Name());
388	if (resolvableFamily == NULL) {
389		TRACE_DEPENDENCIES("    -> dependency \"%s\" unresolved\n",
390			dependency->Name().Data());
391		return;
392	}
393
394	// let the family resolve the dependency
395	if (!resolvableFamily->ResolveDependency(dependency)) {
396		TRACE_DEPENDENCIES("    -> dependency \"%s\" unresolved (version "
397			"mismatch)\n", dependency->Name().Data());
398	}
399}
400
401
402/*static*/ status_t
403PackageFSRoot::_GetOrCreateRoot(dev_t deviceID, ino_t nodeID,
404	PackageFSRoot*& _root)
405{
406	// first check the list, if the root already exists
407	MutexLocker rootListLocker(sRootListLock);
408
409	if (PackageFSRoot* root = _FindRootLocked(deviceID, nodeID)) {
410		root->AcquireReference();
411		_root = root;
412		return B_OK;
413	}
414
415	rootListLocker.Unlock();
416
417	// create a new root
418	PackageFSRoot* root = new(std::nothrow) PackageFSRoot(deviceID, nodeID);
419	if (root == NULL)
420		return B_NO_MEMORY;
421	ObjectDeleter<PackageFSRoot> rootDeleter(root);
422
423	status_t error = root->Init();
424	if (error != B_OK)
425		RETURN_ERROR(error);
426
427	// add the root -- first recheck whether someone else added the root in the
428	// meantime
429	rootListLocker.Lock();
430
431	if (PackageFSRoot* otherRoot = _FindRootLocked(deviceID, nodeID)) {
432		// indeed, someone was faster
433		otherRoot->AcquireReference();
434		_root = otherRoot;
435		return B_OK;
436	}
437
438	sRootList.Add(root);
439
440	_root = rootDeleter.Detach();
441	return B_OK;
442}
443
444
445/*static*/ PackageFSRoot*
446PackageFSRoot::_FindRootLocked(dev_t deviceID, ino_t nodeID)
447{
448	for (RootList::Iterator it = sRootList.GetIterator();
449			PackageFSRoot* root = it.Next();) {
450		if (root->DeviceID() == deviceID && root->NodeID() == nodeID)
451			return root;
452	}
453
454	return NULL;
455}
456
457
458/*static*/ void
459PackageFSRoot::_PutRoot(PackageFSRoot* root)
460{
461	// Only non-custom roots are in the global list.
462	if (!root->IsCustom()) {
463		MutexLocker rootListLocker(sRootListLock);
464
465		// When releasing the last reference, remove the root from the list.
466		if (root->CountReferences() == 1)
467			sRootList.Remove(root);
468
469		rootListLocker.Unlock();
470	}
471
472	root->ReleaseReference();
473}
474