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