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
PackageLinkDirectory()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
~PackageLinkDirectory()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
Init(Package * package)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
Init(const String & name)63 PackageLinkDirectory::Init(const String& name)
64 {
65 return Directory::Init(name);
66 }
67
68
69 timespec
ModifiedTime() const70 PackageLinkDirectory::ModifiedTime() const
71 {
72 return fModifiedTime;
73 }
74
75
76 status_t
OpenAttributeDirectory(AttributeDirectoryCookie * & _cookie)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
OpenAttribute(const StringKey & name,int openMode,AttributeCookie * & _cookie)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
AddPackage(Package * package,PackageLinksListener * listener)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
RemovePackage(Package * package,PackageLinksListener * listener)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
UpdatePackageDependencies(Package * package,PackageLinksListener * listener)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
_Update(PackageLinksListener * listener)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
_UpdateDependencies(PackageLinksListener * listener)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
_RemoveLink(Link * link,PackageLinksListener * listener)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
_CreateOrUpdateLink(Link * & link,Package * package,Link::Type type,const String & name,PackageLinksListener * listener)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