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