xref: /haiku/src/add-ons/kernel/file_systems/packagefs/package_links/PackageLinkDirectory.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
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 
24 PackageLinkDirectory::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 
35 PackageLinkDirectory::~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 
47 status_t
48 PackageLinkDirectory::Init(Package* package)
49 {
50 	// init the directory/node
51 	status_t error = Init(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 
62 status_t
63 PackageLinkDirectory::Init(const String& name)
64 {
65 	return Directory::Init(name);
66 }
67 
68 
69 timespec
70 PackageLinkDirectory::ModifiedTime() const
71 {
72 	return fModifiedTime;
73 }
74 
75 
76 status_t
77 PackageLinkDirectory::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 
93 status_t
94 PackageLinkDirectory::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 
105 void
106 PackageLinkDirectory::AddPackage(Package* package,
107 	PackageLinksListener* listener)
108 {
109 	DirectoryWriteLocker 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 
129 void
130 PackageLinkDirectory::RemovePackage(Package* package,
131 	PackageLinksListener* listener)
132 {
133 	ASSERT(package->LinkDirectory() == this);
134 
135 	DirectoryWriteLocker 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 
147 void
148 PackageLinkDirectory::UpdatePackageDependencies(Package* package,
149 	PackageLinksListener* listener)
150 {
151 	ASSERT(package->LinkDirectory() == this);
152 
153 	DirectoryWriteLocker 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 
163 status_t
164 PackageLinkDirectory::_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 
201 status_t
202 PackageLinkDirectory::_UpdateDependencies(PackageLinksListener* listener)
203 {
204 	ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
205 
206 	Package* package = fPackages.Head();
207 	if (package == NULL)
208 		return B_OK;
209 
210 	// Iterate through the package's dependencies
211 	for (DependencyList::ConstIterator it
212 				= package->Dependencies().GetIterator();
213 			Dependency* dependency = it.Next();) {
214 		Resolvable* resolvable = dependency->Resolvable();
215 		Package* resolvablePackage = resolvable != NULL
216 			? resolvable->Package() : NULL;
217 
218 		Node* node = FindChild(dependency->FileName());
219 		if (node != NULL) {
220 			// link already exists -- update
221 			DependencyLink* link = static_cast<DependencyLink*>(node);
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(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 				listener->PackageLinkNodeAdded(link);
241 		}
242 	}
243 
244 	return B_OK;
245 }
246 
247 
248 void
249 PackageLinkDirectory::_RemoveLink(Link* link, PackageLinksListener* listener)
250 {
251 	ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
252 
253 	if (link != NULL) {
254 		if (listener != NULL)
255 			listener->PackageLinkNodeRemoved(link);
256 
257 		RemoveChild(link);
258 		link->ReleaseReference();
259 	}
260 }
261 
262 
263 status_t
264 PackageLinkDirectory::_CreateOrUpdateLink(Link*& link, Package* package,
265 	Link::Type type, const String& name, PackageLinksListener* listener)
266 {
267 	ASSERT_WRITE_LOCKED_RW_LOCK(&fLock);
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(name);
275 		if (error != B_OK)
276 			RETURN_ERROR(error);
277 
278 		AddChild(link);
279 
280 		if (listener != NULL)
281 			listener->PackageLinkNodeAdded(link);
282 	} else {
283 		link->Update(package, listener);
284 	}
285 
286 	return B_OK;
287 }
288