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