1/*
2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "PackageLinkDirectory.h"
8
9#include <new>
10
11#include <NodeMonitor.h>
12
13#include <AutoDeleter.h>
14
15#include "AutoPackageAttributeDirectoryCookie.h"
16#include "DebugSupport.h"
17#include "PackageLinksListener.h"
18#include "StringConstants.h"
19#include "Utils.h"
20#include "Version.h"
21#include "Volume.h"
22
23
24PackageLinkDirectory::PackageLinkDirectory()
25	:
26	Directory(0),
27		// the ID needs to be assigned later, when added to a volume
28	fSelfLink(NULL),
29	fSettingsLink(NULL)
30{
31	get_real_time(fModifiedTime);
32}
33
34
35PackageLinkDirectory::~PackageLinkDirectory()
36{
37	if (fSelfLink != NULL)
38		fSelfLink->ReleaseReference();
39	if (fSettingsLink != NULL)
40		fSettingsLink->ReleaseReference();
41
42	while (DependencyLink* link = fDependencyLinks.RemoveHead())
43		link->ReleaseReference();
44}
45
46
47status_t
48PackageLinkDirectory::Init(Directory* parent, Package* package)
49{
50	// init the directory/node
51	status_t error = Init(parent, package->VersionedName());
52	if (error != B_OK)
53		RETURN_ERROR(error);
54
55	// add the package
56	AddPackage(package, NULL);
57
58	return B_OK;
59}
60
61
62status_t
63PackageLinkDirectory::Init(Directory* parent, const String& name)
64{
65	return Directory::Init(parent, name);
66}
67
68
69timespec
70PackageLinkDirectory::ModifiedTime() const
71{
72	return fModifiedTime;
73}
74
75
76status_t
77PackageLinkDirectory::OpenAttributeDirectory(AttributeDirectoryCookie*& _cookie)
78{
79	Package* package = fPackages.Head();
80	if (package == NULL)
81		return B_ENTRY_NOT_FOUND;
82
83	AutoPackageAttributeDirectoryCookie* cookie = new(std::nothrow)
84		AutoPackageAttributeDirectoryCookie();
85	if (cookie == NULL)
86		return B_NO_MEMORY;
87
88	_cookie = cookie;
89	return B_OK;
90}
91
92
93status_t
94PackageLinkDirectory::OpenAttribute(const StringKey& name, int openMode,
95	AttributeCookie*& _cookie)
96{
97	Package* package = fPackages.Head();
98	if (package == NULL)
99		return B_ENTRY_NOT_FOUND;
100
101	return AutoPackageAttributes::OpenCookie(package, name, openMode, _cookie);
102}
103
104
105void
106PackageLinkDirectory::AddPackage(Package* package,
107	PackageLinksListener* listener)
108{
109	NodeWriteLocker writeLocker(this);
110
111	// Find the insertion point in the list. We sort by mount type -- the more
112	// specific the higher the priority.
113	MountType mountType = package->Volume()->MountType();
114	Package* otherPackage = NULL;
115	for (PackageList::Iterator it = fPackages.GetIterator();
116			(otherPackage = it.Next()) != NULL;) {
117		if (otherPackage->Volume()->MountType() <= mountType)
118			break;
119	}
120
121	fPackages.InsertBefore(otherPackage, package);
122	package->SetLinkDirectory(this);
123
124	if (package == fPackages.Head())
125		_Update(listener);
126}
127
128
129void
130PackageLinkDirectory::RemovePackage(Package* package,
131	PackageLinksListener* listener)
132{
133	ASSERT(package->LinkDirectory() == this);
134
135	NodeWriteLocker writeLocker(this);
136
137	bool firstPackage = package == fPackages.Head();
138
139	package->SetLinkDirectory(NULL);
140	fPackages.Remove(package);
141
142	if (firstPackage)
143		_Update(listener);
144}
145
146
147void
148PackageLinkDirectory::UpdatePackageDependencies(Package* package,
149	PackageLinksListener* listener)
150{
151	ASSERT(package->LinkDirectory() == this);
152
153	NodeWriteLocker writeLocker(this);
154
155	// We only need to update, if that head package is affected.
156	if (package != fPackages.Head())
157		return;
158
159	_UpdateDependencies(listener);
160}
161
162
163status_t
164PackageLinkDirectory::_Update(PackageLinksListener* listener)
165{
166	// Always remove all dependency links -- if there's still a package, they
167	// will be re-created below.
168	while (DependencyLink* link = fDependencyLinks.RemoveHead())
169		_RemoveLink(link, listener);
170
171	// check, if empty
172	Package* package = fPackages.Head();
173	if (package == NULL) {
174		// remove self and settings link
175		_RemoveLink(fSelfLink, listener);
176		fSelfLink = NULL;
177
178		_RemoveLink(fSettingsLink, listener);
179		fSettingsLink = NULL;
180
181		return B_OK;
182	}
183
184	// create/update self and settings link
185	status_t error = _CreateOrUpdateLink(fSelfLink, package,
186		Link::TYPE_INSTALLATION_LOCATION, StringConstants::Get().kSelfLinkName,
187		listener);
188	if (error != B_OK)
189		RETURN_ERROR(error);
190
191	error = _CreateOrUpdateLink(fSettingsLink, package, Link::TYPE_SETTINGS,
192		StringConstants::Get().kSettingsLinkName, listener);
193	if (error != B_OK)
194		RETURN_ERROR(error);
195
196	// update the dependency links
197	return _UpdateDependencies(listener);
198}
199
200
201status_t
202PackageLinkDirectory::_UpdateDependencies(PackageLinksListener* listener)
203{
204	Package* package = fPackages.Head();
205	if (package == NULL)
206		return B_OK;
207
208	// Iterate through the package's dependencies
209	for (DependencyList::ConstIterator it
210				= package->Dependencies().GetIterator();
211			Dependency* dependency = it.Next();) {
212		Resolvable* resolvable = dependency->Resolvable();
213		Package* resolvablePackage = resolvable != NULL
214			? resolvable->Package() : NULL;
215
216		Node* node = FindChild(dependency->FileName());
217		if (node != NULL) {
218			// link already exists -- update
219			DependencyLink* link = static_cast<DependencyLink*>(node);
220
221			NodeWriteLocker linkLocker(link);
222			link->Update(resolvablePackage, listener);
223		} else {
224			// no link for the dependency yet -- create one
225			DependencyLink* link = new(std::nothrow) DependencyLink(
226				resolvablePackage);
227			if (link == NULL)
228				return B_NO_MEMORY;
229
230			status_t error = link->Init(this, dependency->FileName());
231			if (error != B_OK) {
232				delete link;
233				RETURN_ERROR(error);
234			}
235
236			AddChild(link);
237			fDependencyLinks.Add(link);
238
239			if (listener != NULL) {
240				NodeWriteLocker linkLocker(link);
241				listener->PackageLinkNodeAdded(link);
242			}
243		}
244	}
245
246	return B_OK;
247}
248
249
250void
251PackageLinkDirectory::_RemoveLink(Link* link, PackageLinksListener* listener)
252{
253	if (link != NULL) {
254		NodeWriteLocker linkLocker(link);
255		if (listener != NULL)
256			listener->PackageLinkNodeRemoved(link);
257
258		RemoveChild(link);
259		linkLocker.Unlock();
260		link->ReleaseReference();
261	}
262}
263
264
265status_t
266PackageLinkDirectory::_CreateOrUpdateLink(Link*& link, Package* package,
267	Link::Type type, const String& name, PackageLinksListener* listener)
268{
269	if (link == NULL) {
270		link = new(std::nothrow) Link(package, type);
271		if (link == NULL)
272			return B_NO_MEMORY;
273
274		status_t error = link->Init(this, name);
275		if (error != B_OK)
276			RETURN_ERROR(error);
277
278		AddChild(link);
279
280		if (listener != NULL) {
281			NodeWriteLocker lLinkLocker(link);
282			listener->PackageLinkNodeAdded(link);
283		}
284	} else {
285		NodeWriteLocker lLinkLocker(link);
286		link->Update(package, listener);
287	}
288
289
290	return B_OK;
291}
292