CFBundle.c
1 /* CFBundle.c 2 Copyright (c) 1999-2019, Apple Inc. and the Swift project authors 3 4 Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors 5 Licensed under Apache License v2.0 with Runtime Library Exception 6 See http://swift.org/LICENSE.txt for license information 7 See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 8 Responsibility: Tony Parker 9 */ 10 11 #include <CoreFoundation/CoreFoundation.h> 12 #include "CFBundle_Internal.h" 13 #include <CoreFoundation/CFPropertyList.h> 14 #include <CoreFoundation/CFNumber.h> 15 #include <CoreFoundation/CFSet.h> 16 #include <CoreFoundation/CFURLAccess.h> 17 #include <CoreFoundation/CFError.h> 18 #include <CoreFoundation/CFError_Private.h> 19 #include <string.h> 20 #include <CoreFoundation/CFPriv.h> 21 #include "CFInternal.h" 22 #include "CFRuntime_Internal.h" 23 #include <CoreFoundation/CFByteOrder.h> 24 #include "CFBundle_BinaryTypes.h" 25 #include <ctype.h> 26 #include <sys/stat.h> 27 #include <stdlib.h> 28 #include <assert.h> 29 30 31 #if defined(BINARY_SUPPORT_DYLD) 32 #include <unistd.h> 33 #include <fcntl.h> 34 #include <sys/mman.h> 35 #include <crt_externs.h> 36 #endif /* BINARY_SUPPORT_DYLD */ 37 38 #if defined(BINARY_SUPPORT_DLFCN) 39 #include <dlfcn.h> 40 #ifndef RTLD_FIRST 41 #define RTLD_FIRST 0 42 #endif 43 #endif /* BINARY_SUPPORT_DLFCN */ 44 45 #if TARGET_OS_MAC 46 #include <fcntl.h> 47 #elif TARGET_OS_WIN32 48 #include <fcntl.h> 49 #include <io.h> 50 #endif 51 52 53 #define LOG_BUNDLE_LOAD 0 54 55 // Public CFBundle Info plist keys 56 CONST_STRING_DECL(kCFBundleInfoDictionaryVersionKey, "CFBundleInfoDictionaryVersion") 57 CONST_STRING_DECL(kCFBundleExecutableKey, "CFBundleExecutable") 58 CONST_STRING_DECL(kCFBundleIdentifierKey, "CFBundleIdentifier") 59 CONST_STRING_DECL(kCFBundleVersionKey, "CFBundleVersion") 60 CONST_STRING_DECL(kCFBundleDevelopmentRegionKey, "CFBundleDevelopmentRegion") 61 CONST_STRING_DECL(kCFBundleLocalizationsKey, "CFBundleLocalizations") 62 63 // Private CFBundle key, used to mean 'I support the same localizations as CoreFoundation' 64 CONST_STRING_DECL(_kCFBundleUseAppleLocalizationsKey, "_CFBundleUseAppleLocalizations") 65 66 // Private CFBundle Info plist keys, possible candidates for public constants 67 CONST_STRING_DECL(_kCFBundleAllowMixedLocalizationsKey, "CFBundleAllowMixedLocalizations") 68 CONST_STRING_DECL(_kCFBundleSupportedPlatformsKey, "CFBundleSupportedPlatforms") 69 CONST_STRING_DECL(_kCFBundleResourceSpecificationKey, "CFBundleResourceSpecification") 70 71 // Finder stuff 72 CONST_STRING_DECL(_kCFBundlePackageTypeKey, "CFBundlePackageType") 73 CONST_STRING_DECL(_kCFBundleSignatureKey, "CFBundleSignature") 74 CONST_STRING_DECL(_kCFBundleIconFileKey, "CFBundleIconFile") 75 CONST_STRING_DECL(_kCFBundleDocumentTypesKey, "CFBundleDocumentTypes") 76 CONST_STRING_DECL(_kCFBundleURLTypesKey, "CFBundleURLTypes") 77 78 // Keys that are usually localized in InfoPlist.strings 79 CONST_STRING_DECL(kCFBundleNameKey, "CFBundleName") 80 CONST_STRING_DECL(_kCFBundleDisplayNameKey, "CFBundleDisplayName") 81 CONST_STRING_DECL(_kCFBundleShortVersionStringKey, "CFBundleShortVersionString") 82 CONST_STRING_DECL(_kCFBundleGetInfoStringKey, "CFBundleGetInfoString") 83 CONST_STRING_DECL(_kCFBundleGetInfoHTMLKey, "CFBundleGetInfoHTML") 84 85 // Sub-keys for CFBundleDocumentTypes dictionaries 86 CONST_STRING_DECL(_kCFBundleTypeNameKey, "CFBundleTypeName") 87 CONST_STRING_DECL(_kCFBundleTypeRoleKey, "CFBundleTypeRole") 88 CONST_STRING_DECL(_kCFBundleTypeIconFileKey, "CFBundleTypeIconFile") 89 CONST_STRING_DECL(_kCFBundleTypeOSTypesKey, "CFBundleTypeOSTypes") 90 CONST_STRING_DECL(_kCFBundleTypeExtensionsKey, "CFBundleTypeExtensions") 91 CONST_STRING_DECL(_kCFBundleTypeMIMETypesKey, "CFBundleTypeMIMETypes") 92 93 // Sub-keys for CFBundleURLTypes dictionaries 94 CONST_STRING_DECL(_kCFBundleURLNameKey, "CFBundleURLName") 95 CONST_STRING_DECL(_kCFBundleURLIconFileKey, "CFBundleURLIconFile") 96 CONST_STRING_DECL(_kCFBundleURLSchemesKey, "CFBundleURLSchemes") 97 98 // Compatibility key names 99 CONST_STRING_DECL(_kCFBundleOldExecutableKey, "NSExecutable") 100 CONST_STRING_DECL(_kCFBundleOldInfoDictionaryVersionKey, "NSInfoPlistVersion") 101 CONST_STRING_DECL(_kCFBundleOldNameKey, "NSHumanReadableName") 102 CONST_STRING_DECL(_kCFBundleOldIconFileKey, "NSIcon") 103 CONST_STRING_DECL(_kCFBundleOldDocumentTypesKey, "NSTypes") 104 CONST_STRING_DECL(_kCFBundleOldShortVersionStringKey, "NSAppVersion") 105 106 // Compatibility CFBundleDocumentTypes key names 107 CONST_STRING_DECL(_kCFBundleOldTypeNameKey, "NSName") 108 CONST_STRING_DECL(_kCFBundleOldTypeRoleKey, "NSRole") 109 CONST_STRING_DECL(_kCFBundleOldTypeIconFileKey, "NSIcon") 110 CONST_STRING_DECL(_kCFBundleOldTypeExtensions1Key, "NSUnixExtensions") 111 CONST_STRING_DECL(_kCFBundleOldTypeExtensions2Key, "NSDOSExtensions") 112 CONST_STRING_DECL(_kCFBundleOldTypeOSTypesKey, "NSMacOSType") 113 114 // Internally used keys for loaded Info plists. 115 CONST_STRING_DECL(_kCFBundleInfoPlistURLKey, "CFBundleInfoPlistURL") 116 CONST_STRING_DECL(_kCFBundleRawInfoPlistURLKey, "CFBundleRawInfoPlistURL") 117 CONST_STRING_DECL(_kCFBundleNumericVersionKey, "CFBundleNumericVersion") 118 CONST_STRING_DECL(_kCFBundleExecutablePathKey, "CFBundleExecutablePath") 119 CONST_STRING_DECL(_kCFBundleResourcesFileMappedKey, "CSResourcesFileMapped") 120 CONST_STRING_DECL(_kCFBundleCFMLoadAsBundleKey, "CFBundleCFMLoadAsBundle") 121 122 // Keys used by NSBundle for loaded Info plists. 123 CONST_STRING_DECL(_kCFBundlePrincipalClassKey, "NSPrincipalClass") 124 125 static _CFMutex CFBundleGlobalDataLock = _CF_MUTEX_STATIC_INITIALIZER; 126 127 static CFMutableDictionaryRef _bundlesByIdentifier = NULL; 128 static CFMutableDictionaryRef _bundlesByURL = NULL; 129 static CFMutableArrayRef _allBundles = NULL; 130 131 static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean doFinalProcessing, Boolean unique, Boolean addToTables); 132 static void _CFBundleEnsureBundlesUpToDateWithHint(CFStringRef hint); 133 static void _CFBundleEnsureAllBundlesUpToDate(void); 134 static void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath, Boolean permissive); 135 static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths); 136 137 #pragma mark - 138 139 #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 140 141 // Functions and constants for FHS bundles: 142 #define _CFBundleFHSDirectory_share CFSTR("share") 143 144 static Boolean _CFBundleURLIsForFHSInstalledBundle(CFURLRef bundleURL) { 145 // Paths of this form are FHS installed bundles: 146 // <anywhere>/share/<name>.resources 147 148 CFStringRef extension = CFURLCopyPathExtension(bundleURL); 149 CFURLRef parentURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, bundleURL); 150 CFStringRef containingDirectoryName = parentURL ? CFURLCopyLastPathComponent(parentURL) : NULL; 151 152 Boolean isFHSBundle = 153 extension && 154 containingDirectoryName && 155 CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension) && 156 CFEqual(containingDirectoryName, _CFBundleFHSDirectory_share); 157 158 if (extension) CFRelease(extension); 159 if (parentURL) CFRelease(parentURL); 160 if (containingDirectoryName) CFRelease(containingDirectoryName); 161 162 return isFHSBundle; 163 } 164 #endif // !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 165 166 CF_CROSS_PLATFORM_EXPORT Boolean _CFBundleSupportsFHSBundles() { 167 #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 168 return true; 169 #else 170 return false; 171 #endif 172 } 173 174 CF_CROSS_PLATFORM_EXPORT Boolean _CFBundleSupportsFreestandingBundles() { 175 #if !DEPLOYMENT_RUNTIME_OBJC 176 return true; 177 #else 178 return false; 179 #endif 180 } 181 182 #pragma mark - 183 184 CF_PRIVATE os_log_t _CFBundleResourceLogger(void) { 185 static os_log_t _log; 186 static dispatch_once_t onceToken; 187 dispatch_once(&onceToken, ^{ 188 _log = os_log_create("com.apple.CFBundle", "resources"); 189 }); 190 return _log; 191 } 192 193 CF_PRIVATE os_log_t _CFBundleLocalizedStringLogger(void) { 194 static os_log_t _log; 195 static dispatch_once_t onceToken; 196 dispatch_once(&onceToken, ^{ 197 _log = os_log_create("com.apple.CFBundle", "strings"); 198 }); 199 return _log; 200 } 201 202 CF_PRIVATE os_log_t _CFBundleLoadingLogger(void) { 203 static os_log_t _log; 204 static dispatch_once_t onceToken; 205 dispatch_once(&onceToken, ^{ 206 _log = os_log_create("com.apple.CFBundle", "loading"); 207 }); 208 return _log; 209 } 210 211 #pragma mark - 212 213 #if TARGET_OS_OSX 214 // Some apps may rely on the fact that CFBundle used to allow bundle objects to be deallocated (despite handing out unretained pointers via CFBundleGetBundleWithIdentifier or CFBundleGetAllBundles). To remain compatible even in the face of unsafe behavior, we can optionally use unsafe-unretained memory management for holding on to bundles. 215 static Boolean _useUnsafeUnretainedTables(void) { 216 return false; 217 } 218 #endif 219 220 #pragma mark - 221 #pragma mark Utilities 222 223 // Create an absolute URL after creating a URL with the supplied URLString, baseURL. 224 // This is used to resolve symbolic links in the path (For example, for wrapped bundles) 225 CF_PRIVATE CFURLRef _CFURLCreateResolvedDirectoryWithString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) { 226 CFURLRef relativeURL = CFURLCreateWithString(allocator, URLString, baseURL); 227 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(relativeURL); 228 CFRelease(relativeURL); 229 CFStringRef absolutePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); 230 CFRelease(absoluteURL); 231 232 char absolutePathCString[PATH_MAX]; 233 Boolean success = CFStringGetFileSystemRepresentation(absolutePath, absolutePathCString, PATH_MAX); 234 CFRelease(absolutePath); 235 if (success) { 236 int fd = open(absolutePathCString, O_RDONLY); 237 if (fd > 0) { 238 char resolvedPathCString[PATH_MAX]; 239 if (_CFGetPathFromFileDescriptor(fd, resolvedPathCString)) { 240 os_log_error(_CFBundleResourceLogger(), "Unable to resolve directory (%d)", errno); 241 close(fd); 242 } else { 243 close(fd); 244 CFStringRef resolvedPath = CFStringCreateWithFileSystemRepresentation(allocator, resolvedPathCString); 245 CFURLRef result = CFURLCreateWithFileSystemPath(allocator, resolvedPath, PLATFORM_PATH_STYLE, true); 246 CFRelease(resolvedPath); 247 return result; 248 } 249 } 250 } 251 252 return NULL; 253 } 254 255 #pragma mark - 256 #pragma mark Bundle Tables 257 258 static void _CFBundleAddToTablesLocked(CFBundleRef bundle, CFStringRef bundleID) { 259 if (bundle->_isUnique) return; 260 261 // Add to the _allBundles list 262 if (!_allBundles) { 263 CFArrayCallBacks callbacks = kCFTypeArrayCallBacks; 264 #if TARGET_OS_OSX 265 if (_useUnsafeUnretainedTables()) { 266 callbacks.retain = NULL; 267 callbacks.release = NULL; 268 } 269 #endif 270 // The _allBundles array holds a strong reference on the bundle. 271 // It does this to prevent a race on bundle deallocation / creation. See: <rdar://problem/6606482> CFBundle isn't thread-safe in RR mode 272 // Also, the existence of the CFBundleGetBundleWithIdentifier / CFBundleGetAllBundles API means that any bundle we hand out from there must be permanently retained, or callers will potentially have an object that can be deallocated out from underneath them. 273 _allBundles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &callbacks); 274 } 275 CFArrayAppendValue(_allBundles, bundle); 276 277 // Add to the table that maps urls to bundles 278 if (!_bundlesByURL) { 279 CFDictionaryValueCallBacks nonRetainingDictionaryValueCallbacks = kCFTypeDictionaryValueCallBacks; 280 nonRetainingDictionaryValueCallbacks.retain = NULL; 281 nonRetainingDictionaryValueCallbacks.release = NULL; 282 _bundlesByURL = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &nonRetainingDictionaryValueCallbacks); 283 } 284 CFDictionarySetValue(_bundlesByURL, bundle->_url, bundle); 285 286 // Add to the table that maps identifiers to bundles 287 if (bundleID) { 288 CFMutableArrayRef bundlesWithThisID = NULL; 289 CFBundleRef existingBundle = NULL; 290 if (!_bundlesByIdentifier) { 291 _bundlesByIdentifier = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 292 } 293 bundlesWithThisID = (CFMutableArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 294 if (bundlesWithThisID) { 295 CFIndex i, count = CFArrayGetCount(bundlesWithThisID); 296 UInt32 existingVersion, newVersion = CFBundleGetVersionNumber(bundle); 297 for (i = 0; i < count; i++) { 298 existingBundle = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, i); 299 existingVersion = CFBundleGetVersionNumber(existingBundle); 300 // If you load two bundles with the same identifier and the same version, the last one wins. 301 if (newVersion >= existingVersion) break; 302 } 303 CFArrayInsertValueAtIndex(bundlesWithThisID, i, bundle); 304 305 // We've encountered a bundle with this ID already. 306 // Output some additional info here. It may not be an error (adding a newer version of a bundle is supported), so use os_log_debug. 307 if (os_log_debug_enabled(_CFBundleResourceLogger())) { 308 CFMutableArrayRef bundleList = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); 309 for (CFIndex i = 0; i < CFArrayGetCount(bundlesWithThisID); i++) { 310 CFBundleRef bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, i); 311 CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Bundle %p at %@"), bundle, bundle->_url); 312 CFArrayAppendValue(bundleList, desc); 313 CFRelease(desc); 314 } 315 os_log_debug(_CFBundleResourceLogger(), "More than one bundle with the same identifier has been added: %{public}@", bundleList); 316 CFRelease(bundleList); 317 } 318 } else { 319 CFArrayCallBacks nonRetainingArrayCallbacks = kCFTypeArrayCallBacks; 320 nonRetainingArrayCallbacks.retain = NULL; 321 nonRetainingArrayCallbacks.release = NULL; 322 bundlesWithThisID = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingArrayCallbacks); 323 CFArrayAppendValue(bundlesWithThisID, bundle); 324 CFDictionarySetValue(_bundlesByIdentifier, bundleID, bundlesWithThisID); 325 CFRelease(bundlesWithThisID); 326 } 327 } 328 } 329 330 static void _CFBundleRemoveFromTables(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef bundleID) { 331 // Since we no longer allow bundles to be removed from tables, this method does nothing. Modifying the tables during deallocation is risky because if the caller has over-released the bundle object then we will deadlock on the global lock. 332 #if TARGET_OS_OSX 333 if (_useUnsafeUnretainedTables()) { 334 // Except for special cases of unsafe-unretained, where we must clean up the table or risk handing out a zombie object. There may still be outstanding pointers to these bundes (e.g. the result of CFBundleGetBundleWithIdentifier) but there is nothing we can do about that after this point. 335 336 // Unique bundles aren't in the tables anyway 337 if (bundle->_isUnique) return; 338 339 _CFMutexLock(&CFBundleGlobalDataLock); 340 // Remove from the table of all bundles 341 if (_allBundles) { 342 CFIndex i = CFArrayGetFirstIndexOfValue(_allBundles, CFRangeMake(0, CFArrayGetCount(_allBundles)), bundle); 343 if (i >= 0) CFArrayRemoveValueAtIndex(_allBundles, i); 344 } 345 346 // Remove from the table that maps urls to bundles 347 if (bundleURL && _bundlesByURL) { 348 CFBundleRef bundleForURL = (CFBundleRef)CFDictionaryGetValue(_bundlesByURL, bundleURL); 349 if (bundleForURL == bundle) CFDictionaryRemoveValue(_bundlesByURL, bundleURL); 350 } 351 352 // Remove from the table that maps identifiers to bundles 353 if (bundleID && _bundlesByIdentifier) { 354 CFMutableArrayRef bundlesWithThisID = (CFMutableArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 355 if (bundlesWithThisID) { 356 CFIndex count = CFArrayGetCount(bundlesWithThisID); 357 while (count-- > 0) if (bundle == (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, count)) CFArrayRemoveValueAtIndex(bundlesWithThisID, count); 358 if (0 == CFArrayGetCount(bundlesWithThisID)) CFDictionaryRemoveValue(_bundlesByIdentifier, bundleID); 359 } 360 } 361 _CFMutexUnlock(&CFBundleGlobalDataLock); 362 } 363 #endif 364 } 365 366 static CFBundleRef _CFBundleGetFromTablesLocked(CFStringRef bundleID) { 367 CFBundleRef result = NULL, bundle; 368 if (_bundlesByIdentifier && bundleID) { 369 // Note that this array is maintained in descending order by version number 370 CFArrayRef bundlesWithThisID = (CFArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 371 if (bundlesWithThisID) { 372 CFIndex i, count = CFArrayGetCount(bundlesWithThisID); 373 if (count > 0) { 374 // First check for loaded bundles so we will always prefer a loaded to an unloaded bundle 375 for (i = 0; !result && i < count; i++) { 376 bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, i); 377 if (CFBundleIsExecutableLoaded(bundle)) result = bundle; 378 } 379 // If no loaded bundle, simply take the first item in the array, i.e. the one with the latest version number 380 if (!result) result = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, 0); 381 } 382 } 383 } 384 return result; 385 } 386 387 static CFBundleRef _CFBundleGetFromTables(CFStringRef bundleID) { 388 _CFMutexLock(&CFBundleGlobalDataLock); 389 CFBundleRef result = _CFBundleGetFromTablesLocked(bundleID); 390 _CFMutexUnlock(&CFBundleGlobalDataLock); 391 return result; 392 } 393 394 static CFBundleRef _CFBundleCopyFromTablesForURLLocked(CFURLRef url) { 395 /* 396 If you're curious why this doesn't consult the main bundle URL, consider the case where you have a directory structure like this: 397 398 /S/L/F/Foo.framework/Foo 399 /S/L/F/Foo.framework/food (a daemon for the Foo framework) 400 401 And the main executable is 'food'. 402 403 This flat structure can happen on iOS, with its more common version 3 bundles. In this scenario, there are theoretically two different bundles that could be returned: one for the framework, one for the daemon. They have the same URL but different bundle identifiers. 404 405 Since the main bundle is not part of the bundle tables, we can support this scenario by having the _bundlesByURL data structure hold the bundle for URL "/S/L/F/Foo.framework/Foo" and _mainBundle (in CFBundle_Main.c) hold the bundle for URL "/S/L/F/Foo.framework/food". 406 */ 407 CFBundleRef result = NULL; 408 if (_bundlesByURL) result = (CFBundleRef)CFDictionaryGetValue(_bundlesByURL, url); 409 if (result && !result->_url) { 410 result = NULL; 411 CFDictionaryRemoveValue(_bundlesByURL, url); 412 } 413 if (result) CFRetain(result); 414 return result; 415 } 416 417 static CFBundleRef _CFBundleCopyFromTablesForURL(CFURLRef url) { 418 _CFMutexLock(&CFBundleGlobalDataLock); 419 CFBundleRef result = _CFBundleCopyFromTablesForURLLocked(url); 420 _CFMutexUnlock(&CFBundleGlobalDataLock); 421 return result; 422 } 423 424 #pragma mark - 425 426 CF_PRIVATE _CFBundleVersion _CFBundleEffectiveLayoutVersion(CFBundleRef bundle) { 427 _CFBundleVersion localVersion = bundle->_version; 428 // exclude type 0 bundles with no binary (or CFM binary) and no Info.plist, since they give too many false positives 429 if (_CFBundleVersionOldStyleResources == localVersion) { 430 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 431 if (!infoDict || 0 == CFDictionaryGetCount(infoDict)) { 432 #if defined(BINARY_SUPPORT_DYLD) 433 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 434 if (executableURL) { 435 if (bundle->_binaryType == __CFBundleUnknownBinary) bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 436 if (bundle->_binaryType == __CFBundleCFMBinary || bundle->_binaryType == __CFBundleUnreadableBinary) { 437 localVersion = _CFBundleVersionNotABundle; 438 } else { 439 bundle->_resourceData._executableLacksResourceFork = true; 440 } 441 CFRelease(executableURL); 442 } else { 443 localVersion = _CFBundleVersionNotABundle; 444 } 445 #else 446 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 447 if (executableURL) { 448 CFRelease(executableURL); 449 } else { 450 localVersion = _CFBundleVersionNotABundle; 451 } 452 #endif /* BINARY_SUPPORT_DYLD */ 453 } 454 } 455 return localVersion; 456 } 457 458 CFBundleRef _CFBundleCreateIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url) { 459 // It is assumed that users of this SPI do not want this bundle to persist forever. 460 CFBundleRef bundle = _CFBundleCreateUnique(allocator, url); 461 if (bundle) { 462 _CFBundleVersion localVersion = _CFBundleEffectiveLayoutVersion(bundle); 463 if (_CFBundleVersionFlat == localVersion || _CFBundleVersionNotABundle == localVersion) { 464 CFRelease(bundle); 465 bundle = NULL; 466 } 467 } 468 return bundle; 469 } 470 471 CF_EXPORT Boolean _CFBundleURLLooksLikeBundle(CFURLRef url) { 472 Boolean result = false; 473 CFBundleRef bundle = _CFBundleCreateIfLooksLikeBundle(kCFAllocatorSystemDefault, url); 474 if (bundle) { 475 result = true; 476 CFRelease(bundle); 477 } 478 return result; 479 } 480 481 CFBundleRef _CFBundleGetMainBundleIfLooksLikeBundle(void) { 482 CFBundleRef mainBundle = CFBundleGetMainBundle(); 483 if (mainBundle && (_CFBundleVersionFlat == mainBundle->_version || _CFBundleVersionNotABundle == mainBundle->_version)) mainBundle = NULL; 484 return mainBundle; 485 } 486 487 Boolean _CFBundleMainBundleInfoDictionaryComesFromResourceFork(void) { 488 CFBundleRef mainBundle = CFBundleGetMainBundle(); 489 return (mainBundle && mainBundle->_resourceData._infoDictionaryFromResourceFork); 490 } 491 492 CF_EXPORT CFBundleRef _CFBundleCreateIfMightBeBundle(CFAllocatorRef allocator, CFURLRef url) { 493 // This function is obsolete 494 CFBundleRef bundle = CFBundleCreate(allocator, url); 495 return bundle; 496 } 497 498 CF_EXPORT void _CFBundleFlushBundleCaches(CFBundleRef bundle) { 499 __CFLock(&bundle->_lock); 500 CFDictionaryRef oldInfoDict = bundle->_infoDict; 501 CFTypeRef val; 502 503 bundle->_infoDict = NULL; 504 if (bundle->_localInfoDict) { 505 CFRelease(bundle->_localInfoDict); 506 bundle->_localInfoDict = NULL; 507 } 508 if (bundle->_infoPlistUrl) { 509 CFRelease(bundle->_infoPlistUrl); 510 bundle->_infoPlistUrl = NULL; 511 } 512 if (bundle->_developmentRegion) { 513 CFRelease(bundle->_developmentRegion); 514 bundle->_developmentRegion = NULL; 515 } 516 if (bundle->_executablePath) { 517 CFRelease(bundle->_executablePath); 518 bundle->_executablePath = NULL; 519 } 520 if (bundle->_searchLanguages) { 521 CFRelease(bundle->_searchLanguages); 522 bundle->_searchLanguages = NULL; 523 } 524 if (bundle->_stringTable) { 525 CFRelease(bundle->_stringTable); 526 bundle->_stringTable = NULL; 527 } 528 _CFBundleRefreshInfoDictionaryAlreadyLocked(bundle); 529 if (oldInfoDict) { 530 if (!bundle->_infoDict) bundle->_infoDict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 531 val = CFDictionaryGetValue(oldInfoDict, _kCFBundlePrincipalClassKey); 532 if (val) CFDictionarySetValue((CFMutableDictionaryRef)bundle->_infoDict, _kCFBundlePrincipalClassKey, val); 533 CFRelease(oldInfoDict); 534 } 535 _CFBundleFlushQueryTableCache(bundle); 536 __CFUnlock(&bundle->_lock); 537 } 538 539 static CFBundleRef _CFBundleGetBundleWithIdentifier(CFStringRef bundleID, void *frameworkHint) { 540 CFBundleRef result = NULL; 541 if (bundleID) { 542 CFBundleRef main = CFBundleGetMainBundle(); 543 if (main) { 544 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(main); 545 if (infoDict) { 546 CFStringRef mainBundleID = CFDictionaryGetValue(infoDict, kCFBundleIdentifierKey); 547 if (mainBundleID && CFGetTypeID(mainBundleID) == CFStringGetTypeID() && CFEqual(mainBundleID, bundleID)) { 548 return main; 549 } 550 } 551 } 552 553 result = _CFBundleGetFromTables(bundleID); 554 #if TARGET_OS_MAC 555 if (!result) { 556 // Try to create the bundle for the caller and try again 557 if (frameworkHint) { 558 CFStringRef imagePath = _CFBundleCopyLoadedImagePathForPointer(frameworkHint); 559 if (imagePath) { 560 // As this is a fast-path check, we don't want to be aggressive about assuming that the executable URL that we may have received from DYLD via _CFBundleCopyLoadedImagePathForPointer should be turned into a framework URL. If we do, then it is possible that an executable located inside a framework bundle which does not normally link that framework will cause us to load it unintentionally (31165928). 561 // For example: 562 // Foo.framework/ 563 // Resources/ 564 // HelperTool 565 // 566 // With permissive set to 'true', this would make the 'Foo.framework' bundle exist, but there is no reason why HelperTool is required to have loaded Foo.framework. 567 568 _CFBundleEnsureBundleExistsForImagePath(imagePath, false); 569 CFRelease(imagePath); 570 } 571 572 // Now try again 573 result = _CFBundleGetFromTables(bundleID); 574 } 575 } 576 #endif 577 if (!result) { 578 // Try to guess the bundle from the identifier and try again 579 _CFBundleEnsureBundlesUpToDateWithHint(bundleID); 580 581 // Now try again 582 result = _CFBundleGetFromTables(bundleID); 583 } 584 } 585 586 if (!result) { 587 // Make sure all bundles have been created and try again. 588 _CFBundleEnsureAllBundlesUpToDate(); 589 590 // Now try again 591 result = _CFBundleGetFromTables(bundleID); 592 } 593 594 return result; 595 } 596 597 CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID) { 598 // Use the frame that called this as a hint 599 return _CFBundleGetBundleWithIdentifier(bundleID, __builtin_return_address(0)); 600 } 601 602 CFBundleRef _CFBundleGetBundleWithIdentifierWithHint(CFStringRef bundleID, void *pointer) { 603 return _CFBundleGetBundleWithIdentifier(bundleID, pointer); 604 } 605 606 static CFStringRef __CFBundleCopyDescription(CFTypeRef cf) { 607 char buff[CFMaxPathSize]; 608 CFStringRef path = NULL, binaryType = NULL, retval = NULL; 609 if (((CFBundleRef)cf)->_url && CFURLGetFileSystemRepresentation(((CFBundleRef)cf)->_url, true, (uint8_t *)buff, CFMaxPathSize)) path = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, buff); 610 switch (((CFBundleRef)cf)->_binaryType) { 611 case __CFBundleCFMBinary: 612 binaryType = CFSTR(""); 613 break; 614 case __CFBundleDYLDExecutableBinary: 615 binaryType = CFSTR("executable, "); 616 break; 617 case __CFBundleDYLDBundleBinary: 618 binaryType = CFSTR("bundle, "); 619 break; 620 case __CFBundleDYLDFrameworkBinary: 621 binaryType = CFSTR("framework, "); 622 break; 623 case __CFBundleDLLBinary: 624 binaryType = CFSTR("DLL, "); 625 break; 626 case __CFBundleUnreadableBinary: 627 binaryType = CFSTR(""); 628 break; 629 default: 630 binaryType = CFSTR(""); 631 break; 632 } 633 if (((CFBundleRef)cf)->_plugInData._isPlugIn) { 634 retval = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFBundle/CFPlugIn %p <%@> (%@%@loaded)"), cf, path, binaryType, ((CFBundleRef)cf)->_isLoaded ? CFSTR("") : CFSTR("not ")); 635 } else { 636 retval = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFBundle %p <%@> (%@%@loaded)"), cf, path, binaryType, ((CFBundleRef)cf)->_isLoaded ? CFSTR("") : CFSTR("not ")); 637 } 638 if (path) CFRelease(path); 639 return retval; 640 } 641 642 static void __CFBundleDeallocate(CFTypeRef cf) { 643 CFBundleRef bundle = (CFBundleRef)cf; 644 CFURLRef bundleURL; 645 CFStringRef bundleID = NULL; 646 647 __CFGenericValidateType(cf, CFBundleGetTypeID()); 648 bundleURL = bundle->_url; 649 bundle->_url = NULL; 650 if (bundle->_infoDict) bundleID = (CFStringRef)CFDictionaryGetValue(bundle->_infoDict, kCFBundleIdentifierKey); 651 _CFBundleRemoveFromTables(bundle, bundleURL, bundleID); 652 CFBundleUnloadExecutable(bundle); 653 _CFBundleDeallocatePlugIn(bundle); 654 if (bundleURL) { 655 CFRelease(bundleURL); 656 } 657 if (bundle->_infoDict) CFRelease(bundle->_infoDict); 658 if (bundle->_localInfoDict) CFRelease(bundle->_localInfoDict); 659 if (bundle->_searchLanguages) CFRelease(bundle->_searchLanguages); 660 if (bundle->_executablePath) CFRelease(bundle->_executablePath); 661 if (bundle->_developmentRegion) CFRelease(bundle->_developmentRegion); 662 if (bundle->_infoPlistUrl) CFRelease(bundle->_infoPlistUrl); 663 if (bundle->_stringTable) CFRelease(bundle->_stringTable); 664 if (bundle->_bundleBasePath) CFRelease(bundle->_bundleBasePath); 665 if (bundle->_queryTable) CFRelease(bundle->_queryTable); 666 667 if (bundle->_localizations) CFRelease(bundle->_localizations); 668 if (bundle->_resourceDirectoryContents) CFRelease(bundle->_resourceDirectoryContents); 669 670 if (bundle->_additionalResourceBundles) CFRelease(bundle->_additionalResourceBundles); 671 672 _CFMutexDestroy(&(bundle->_bundleLoadingLock)); 673 } 674 675 const CFRuntimeClass __CFBundleClass = { 676 _kCFRuntimeScannedObject, 677 "CFBundle", 678 NULL, // init 679 NULL, // copy 680 __CFBundleDeallocate, 681 NULL, // equal 682 NULL, // hash 683 NULL, // 684 __CFBundleCopyDescription 685 }; 686 687 CFTypeID CFBundleGetTypeID(void) { 688 return _kCFRuntimeIDCFBundle; 689 } 690 691 // TODO: Remove this SPI, it appears no one is using it 692 // <rdar://problem/30925651> Remove _CFBundleGetExistingBundleWithBundleURL 693 CFBundleRef _CFBundleGetExistingBundleWithBundleURL(CFURLRef bundleURL) { 694 CFBundleRef bundle = NULL; 695 char buff[CFMaxPathSize]; 696 CFURLRef newURL = NULL; 697 698 if (!CFURLGetFileSystemRepresentation(bundleURL, true, (uint8_t *)buff, CFMaxPathSize)) return NULL; 699 700 newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)buff, strlen(buff), true); 701 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); 702 703 // First check the main bundle; otherwise fallback to the other tables 704 CFBundleRef main = CFBundleGetMainBundle(); 705 if (main->_url && newURL && CFEqual(main->_url, newURL)) { 706 CFRelease(newURL); 707 return main; 708 } 709 710 bundle = _CFBundleCopyFromTablesForURL(newURL); 711 if (bundle) CFRelease(bundle); 712 CFRelease(newURL); 713 return bundle; 714 } 715 716 static Boolean _CFBundlesHaveEquivalentURL(CFBundleRef b1, CFBundleRef b2) { 717 CFStringRef path1 = b1->_bundleBasePath; 718 CFStringRef path2 = b2->_bundleBasePath; 719 char path1CString[PATH_MAX]; 720 char path2CString[PATH_MAX]; 721 722 Boolean result = false; 723 if (CFStringGetFileSystemRepresentation(path1, path1CString, PATH_MAX) && 724 CFStringGetFileSystemRepresentation(path2, path2CString, PATH_MAX)) { 725 int fd1 = open(path1CString, O_RDONLY); 726 int fd2 = open(path2CString, O_RDONLY); 727 728 if (fd1 > 0 && fd2 > 0) { 729 struct stat statbuf1; 730 struct stat statbuf2; 731 732 if (fstat(fd1, &statbuf1) == 0 && 733 fstat(fd2, &statbuf2) == 0 && 734 statbuf1.st_dev == statbuf2.st_dev && 735 statbuf1.st_ino == statbuf2.st_ino) 736 { 737 result = true; 738 } 739 } 740 741 if (fd1 > 0) close(fd1); 742 if (fd2 > 0) close(fd2); 743 } 744 745 return result; 746 } 747 748 /* 749 The combinations of boolean arguments to this function are as follows: 750 751 doFinal | unique | addToTables 752 ------- | ------ | ----------- 753 true false true // CFBundleCreate 754 true true false // _CFBundleCreateUnique 755 false false false // _CFBundleCreateMain 756 false false true // _CFBundleEnsureBundleExistsForImagePath 757 758 n.b. 759 (unique && addToTables) is an invalid configuration and this function will assert. 760 (unique && doFinalProcessing) does nothing but there is no assert; doFinalProcessing is ignored 761 This is because doFinalProcessing is primarily about initializing plugins, but unique bundles cannot be plugins. 762 763 */ 764 static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean doFinalProcessing, Boolean unique, Boolean addToTables) { 765 char buff[CFMaxPathSize]; 766 CFURLRef newURL = NULL; 767 if (!CFURLGetFileSystemRepresentation(bundleURL, true, (uint8_t *)buff, CFMaxPathSize)) return NULL; 768 newURL = CFURLCreateFromFileSystemRepresentation(allocator, (uint8_t *)buff, strlen(buff), true); 769 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); 770 771 // Don't go searching for the URL in the tables if the bundle is unique or the main bundle (addToTables == false) 772 if (!unique && addToTables) { 773 CFBundleRef existingBundle = _CFBundleCopyFromTablesForURL(newURL); 774 if (existingBundle) { 775 CFRelease(newURL); 776 return existingBundle; 777 } 778 } 779 780 781 // This section of _CFBundleCreate touches the disk, so it should be done outside the lock. This avoids blocking threads which are just attempting to get an existing bundle on file system access, potentially from a lower-piority thread. 782 _CFBundleVersion localVersion = _CFBundleGetBundleVersionForURL(newURL); 783 if (_CFBundleVersionFlat == localVersion) { 784 Boolean exists = false; 785 SInt32 mode = 0; 786 SInt32 res = _CFGetPathProperties(allocator, (char *)buff, &exists, &mode, NULL, NULL, NULL, NULL); 787 #if DEPLOYMENT_RUNTIME_OBJC && TARGET_OS_WIN32 788 if (!(res == 0 && exists && ((mode & S_IFMT) == S_IFDIR))) { 789 // 2nd chance at finding a bundle path - remove the last path component (e.g., mybundle.resources) and try again 790 CFURLRef shorterPath = CFURLCreateCopyDeletingLastPathComponent(allocator, newURL); 791 CFRelease(newURL); 792 newURL = shorterPath; 793 res = _CFGetFileProperties(allocator, newURL, &exists, &mode, NULL, NULL, NULL, NULL); 794 } 795 #endif 796 if (res == 0) { 797 if (!exists || ((mode & S_IFMT) != S_IFDIR)) { 798 CFRelease(newURL); 799 return NULL; 800 } 801 } else { 802 CFRelease(newURL); 803 return NULL; 804 } 805 } 806 807 CFBundleRef bundle = (CFBundleRef)_CFRuntimeCreateInstance(allocator, CFBundleGetTypeID(), sizeof(struct __CFBundle) - sizeof(CFRuntimeBase), NULL); 808 if (!bundle) { 809 CFRelease(newURL); 810 return NULL; 811 } 812 813 bundle->_url = newURL; 814 815 #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 816 bundle->_isFHSInstalledBundle = _CFBundleURLIsForFHSInstalledBundle(newURL); 817 #endif 818 819 bundle->_version = localVersion; 820 821 #if defined(BINARY_SUPPORT_DYLD) 822 /* We'll have to figure it out later */ 823 bundle->_binaryType = __CFBundleUnknownBinary; 824 #elif defined(BINARY_SUPPORT_DLL) 825 /* We support DLL only */ 826 bundle->_binaryType = __CFBundleDLLBinary; 827 #else 828 /* We'll have to figure it out later */ 829 bundle->_binaryType = __CFBundleUnknownBinary; 830 #endif /* BINARY_SUPPORT_DYLD */ 831 832 bundle->_isUnique = unique; 833 834 #if TARGET_OS_OSX 835 if (!__CFgetenv("CFBundleDisableStringsSharing") && 836 (strncmp(buff, "/System/Library/Frameworks", 26) == 0) && 837 (strncmp(buff + strlen(buff) - 10, ".framework", 10) == 0)) bundle->_sharesStringsFiles = true; 838 #endif 839 840 _CFMutexCreate(&(bundle->_bundleLoadingLock)); 841 842 bundle->_lock = CFLockInit; 843 bundle->_queryLock = CFLockInit; 844 845 CFURLRef absoURL = CFURLCopyAbsoluteURL(bundle->_url); 846 bundle->_bundleBasePath = CFURLCopyFileSystemPath(absoURL, PLATFORM_PATH_STYLE); 847 CFRelease(absoURL); 848 849 bundle->_additionalResourceLock = CFLockInit; 850 851 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 852 CFStringRef bundleID = CFBundleGetIdentifier(bundle); 853 854 // Do this so that we can use the dispatch_once on the ivar of this bundle safely 855 #pragma GCC diagnostic push 856 #pragma GCC diagnostic ignored "-Wdeprecated" 857 OSMemoryBarrier(); 858 #pragma GCC diagnostic pop 859 860 // We cannot add to tables for unique bundles. Return unique bundle results here without heading into the section below where we take a lock. 861 if (unique) { 862 assert(!addToTables); 863 return bundle; 864 } 865 866 // Locked after here for the global tables 867 _CFMutexLock(&CFBundleGlobalDataLock); 868 869 // Check to see if we already have a bundle with the same identifier or URL. Someone may have raced us to this point. 870 CFBundleRef existingBundleWithSameURL = _CFBundleCopyFromTablesForURLLocked(bundle->_url); 871 if (existingBundleWithSameURL) { 872 // Deallocating a bundle takes the lock when _useUnsafeUnretainedTables returns true, so release the lock first. 873 _CFMutexUnlock(&CFBundleGlobalDataLock); 874 CFRelease(bundle); 875 876 return existingBundleWithSameURL; 877 } 878 879 // It's unfortunate, but valid, for two different bundles to have the same identifier. However, if they also have the same URL (resolving symlinks), then we can return the original one because they are really the same bundle after all. 880 CFBundleRef existingBundleWithSameIdentifier = _CFBundleGetFromTablesLocked(bundleID); 881 if (existingBundleWithSameIdentifier && _CFBundlesHaveEquivalentURL(bundle, existingBundleWithSameIdentifier)) { 882 // Deallocating a bundle takes the lock when _useUnsafeUnretainedTables returns true, so release the lock first. 883 _CFMutexUnlock(&CFBundleGlobalDataLock); 884 CFRelease(bundle); 885 return (CFBundleRef)CFRetain(existingBundleWithSameIdentifier); 886 } 887 888 if (doFinalProcessing) { 889 // Initializing the tables for the list of plugins and initializing the tables for the list of bundles are inherently tied together, because plugins have references back to bundles. 890 // In order to preserve safety, we take both locks. Always in the order of CFBundleGlobalDataLock -> CFPlugInGlobalDataLock. 891 892 // We have to do this before adding the bundle to the tables, otherwise another thread coming along and fetching the bundle will get the bundle ref but the factories for PlugIn have not yet been registered. 893 CFBundleRef duplicateFactoryBundle = NULL; 894 if (!_CFBundleInitPlugIn(bundle, infoDict, &duplicateFactoryBundle)) { 895 // At this point the most likely explanation is that we have the same factory ID but two different bundles. There is no way to really proceed safely here because the plugin system can only deal with one factory ID -> bundle pairing. 896 // Get some info for error logging 897 CFDictionaryRef factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey); 898 if (factoryDict && CFGetTypeID(factoryDict) != CFDictionaryGetTypeID()) factoryDict = NULL; 899 os_log_error(_CFBundleLoadingLogger(), "More than one bundle with the same factory UUID detected: %{public}@ in %{public}@ and %{public}@", factoryDict, bundle, duplicateFactoryBundle); 900 901 // Deallocating a bundle takes the lock when _useUnsafeUnretainedTables returns true, so release the lock first. 902 _CFMutexUnlock(&CFBundleGlobalDataLock); 903 904 // Release the allocated instance 905 CFRelease(bundle); 906 907 // Release the duplicate factory bundle 908 if (duplicateFactoryBundle) CFRelease(duplicateFactoryBundle); 909 return NULL; 910 } 911 } 912 913 if (addToTables) { 914 _CFBundleAddToTablesLocked(bundle, bundleID); 915 } 916 917 _CFMutexUnlock(&CFBundleGlobalDataLock); 918 919 if (doFinalProcessing) { 920 // Outside of the lock, handle dynamic registration 921 _CFPlugInHandleDynamicRegistration(bundle); 922 923 } 924 925 return bundle; 926 } 927 928 CFBundleRef CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL) { 929 if (NULL == bundleURL) return NULL; 930 931 // _CFBundleCreate doesn't know about the main bundle, so we have to check that first. If the URL passed in is the same as the main bundle, then we'll need to return that bundle first. 932 // Result will be nil if the bundleURL passed in happened to have been the main bundle. 933 // As a fallback, check now to see if the main bundle URL is equal to bundleURL. If so, return that bundle instead of nil (32988858). 934 CFBundleRef main = CFBundleGetMainBundle(); 935 if (main && main->_url && CFEqual(main->_url, bundleURL)) { 936 CFRetain(main); 937 return main; 938 } 939 940 return _CFBundleCreate(allocator, bundleURL, true, false, true); 941 } 942 943 CFBundleRef _CFBundleCreateUnique(CFAllocatorRef allocator, CFURLRef bundleURL) { 944 // This function can never return an existing CFBundleRef object. 945 return _CFBundleCreate(allocator, bundleURL, true, true, false); 946 } 947 948 CF_PRIVATE CFBundleRef _CFBundleCreateMain(CFAllocatorRef allocator, CFURLRef mainBundleURL) { 949 // Do not add the main bundle to tables 950 return _CFBundleCreate(allocator, mainBundleURL, false, false, false); 951 } 952 953 CFArrayRef CFBundleCreateBundlesFromDirectory(CFAllocatorRef alloc, CFURLRef directoryURL, CFStringRef bundleType) { 954 CFMutableArrayRef bundles = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); 955 CFArrayRef URLs = _CFCreateContentsOfDirectory(alloc, NULL, NULL, directoryURL, bundleType); 956 if (URLs) { 957 CFIndex c = CFArrayGetCount(URLs); 958 CFURLRef curURL; 959 CFBundleRef curBundle; 960 961 for (CFIndex i = 0; i < c; i++) { 962 curURL = (CFURLRef)CFArrayGetValueAtIndex(URLs, i); 963 curBundle = CFBundleCreate(alloc, curURL); 964 if (curBundle) { 965 CFArrayAppendValue(bundles, curBundle); 966 #ifdef __clang_analyzer__ 967 // The contract of this function is that the bundles created inside here are 'not released'. 968 CFRelease(curBundle); 969 #endif 970 } 971 } 972 CFRelease(URLs); 973 } 974 975 return bundles; 976 } 977 978 CFURLRef CFBundleCopyBundleURL(CFBundleRef bundle) { 979 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 980 if (bundle->_url) CFRetain(bundle->_url); 981 return bundle->_url; 982 } 983 984 UInt32 CFBundleGetVersionNumber(CFBundleRef bundle) { 985 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 986 CFNumberRef versionValue = (CFNumberRef)CFDictionaryGetValue(infoDict, _kCFBundleNumericVersionKey); 987 if (!versionValue || CFGetTypeID(versionValue) != CFNumberGetTypeID()) return 0; 988 989 UInt32 vers = 0; 990 CFNumberGetValue(versionValue, kCFNumberSInt32Type, &vers); 991 return vers; 992 } 993 994 CFStringRef CFBundleGetDevelopmentRegion(CFBundleRef bundle) { 995 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 996 dispatch_once(&bundle->_developmentRegionCalculated, ^{ 997 CFStringRef devRegion = NULL; 998 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 999 if (infoDict) { 1000 devRegion = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); 1001 if (devRegion && (CFGetTypeID(devRegion) != CFStringGetTypeID() || CFStringGetLength(devRegion) == 0)) { 1002 devRegion = NULL; 1003 } 1004 } 1005 1006 if (devRegion) bundle->_developmentRegion = (CFStringRef)CFRetain(devRegion); 1007 }); 1008 return bundle->_developmentRegion; 1009 } 1010 1011 Boolean _CFBundleGetHasChanged(CFBundleRef bundle) { 1012 // This SPI isn't very useful, so now we just return true (30211007) 1013 return true; 1014 } 1015 1016 void _CFBundleSetStringsFilesShared(CFBundleRef bundle, Boolean flag) { 1017 bundle->_sharesStringsFiles = flag; 1018 } 1019 1020 Boolean _CFBundleGetStringsFilesShared(CFBundleRef bundle) { 1021 return bundle->_sharesStringsFiles; 1022 } 1023 1024 CF_EXPORT CFURLRef CFBundleCopySupportFilesDirectoryURL(CFBundleRef bundle) { 1025 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1026 CFURLRef bundleURL = bundle->_url; 1027 _CFBundleVersion version = bundle->_version; 1028 CFURLRef result = NULL; 1029 if (bundleURL) { 1030 if (_CFBundleVersionOldStyleSupportFiles == version) { 1031 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase1, bundleURL); 1032 } else if (_CFBundleVersionContentsResources == version) { 1033 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase2, bundleURL); 1034 } else if (_CFBundleVersionWrappedContentsResources == version) { 1035 result = _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrappedSupportFilesURLFromBase2, bundleURL); 1036 } else if (_CFBundleVersionWrappedFlat == version) { 1037 result = _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrappedSupportFilesURLFromBase3, bundleURL); 1038 } else { 1039 result = (CFURLRef)CFRetain(bundleURL); 1040 } 1041 } 1042 return result; 1043 } 1044 1045 CF_PRIVATE CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFURLRef bundleURL, _CFBundleVersion version) { 1046 CFURLRef result = NULL; 1047 if (bundleURL) { 1048 if (_CFBundleVersionOldStyleResources == version) { 1049 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase0, bundleURL); 1050 } else if (_CFBundleVersionOldStyleSupportFiles == version) { 1051 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase1, bundleURL); 1052 } else if (_CFBundleVersionContentsResources == version) { 1053 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase2, bundleURL); 1054 } else if (_CFBundleVersionWrappedContentsResources == version) { 1055 result = _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrappedResourcesURLFromBase2, bundleURL); 1056 } else if (_CFBundleVersionWrappedFlat == version) { 1057 result = _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrappedResourcesURLFromBase3, bundleURL); 1058 } else { 1059 result = (CFURLRef)CFRetain(bundleURL); 1060 } 1061 } 1062 return result; 1063 } 1064 1065 CF_EXPORT CFURLRef CFBundleCopyResourcesDirectoryURL(CFBundleRef bundle) { 1066 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1067 return _CFBundleCopyResourcesDirectoryURLInDirectory(bundle->_url, bundle->_version); 1068 } 1069 1070 CF_PRIVATE CFURLRef _CFBundleCopyAppStoreReceiptURLInDirectory(CFURLRef bundleURL, _CFBundleVersion version) { 1071 CFURLRef result = NULL; 1072 if (bundleURL) { 1073 if (_CFBundleVersionOldStyleResources == version) { 1074 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase0, bundleURL); 1075 } else if (_CFBundleVersionOldStyleSupportFiles == version) { 1076 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase1, bundleURL); 1077 } else if (_CFBundleVersionContentsResources == version) { 1078 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase2, bundleURL); 1079 } else if (_CFBundleVersionFlat == version) { 1080 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase0, bundleURL); 1081 } else if (_CFBundleVersionWrappedContentsResources == version) { 1082 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrappedAppStoreReceiptURLFromBase2, bundleURL); 1083 } else if (_CFBundleVersionWrappedFlat == version) { 1084 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrappedAppStoreReceiptURLFromBase3, bundleURL); 1085 } 1086 } 1087 return result; 1088 } 1089 1090 CFURLRef _CFBundleCopyAppStoreReceiptURL(CFBundleRef bundle) { 1091 return _CFBundleCopyAppStoreReceiptURLInDirectory(bundle->_url, bundle->_version); 1092 } 1093 1094 CF_EXPORT CFURLRef _CFBundleCopyWrappedBundleURL(CFBundleRef bundle) { 1095 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1096 _CFBundleVersion version = bundle->_version; 1097 if (version == _CFBundleVersionWrappedContentsResources || version == _CFBundleVersionWrappedFlat) { 1098 return _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrapperLinkName, bundle->_url); 1099 } else { 1100 return NULL; 1101 } 1102 } 1103 1104 CF_EXPORT CFURLRef _CFBundleCopyWrapperContainerURL(CFBundleRef bundle) { 1105 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1106 _CFBundleVersion version = bundle->_version; 1107 if (version == _CFBundleVersionWrappedContentsResources || version == _CFBundleVersionWrappedFlat) { 1108 // This is a directory, not a symlink 1109 return CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrapperDirectoryName, bundle->_url); 1110 } else { 1111 return NULL; 1112 } 1113 } 1114 1115 CF_CROSS_PLATFORM_EXPORT CFStringRef _CFBundleCopyExecutablePath(CFBundleRef bundle) { 1116 return _CFBundleCopyExecutableName(bundle, NULL, NULL); 1117 } 1118 1119 CF_PRIVATE CFStringRef _CFBundleCopyExecutableName(CFBundleRef bundle, CFURLRef url, CFDictionaryRef infoDict) { 1120 CFStringRef executableName = NULL; 1121 1122 if (!infoDict && bundle) infoDict = CFBundleGetInfoDictionary(bundle); 1123 if (!url && bundle) url = bundle->_url; 1124 1125 if (infoDict) { 1126 // Figure out the name of the executable. 1127 // First try for the new key in the plist. 1128 executableName = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleExecutableKey); 1129 // Second try for the old key in the plist. 1130 if (!executableName) executableName = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleOldExecutableKey); 1131 if (executableName && CFGetTypeID(executableName) == CFStringGetTypeID() && CFStringGetLength(executableName) > 0) { 1132 CFRetain(executableName); 1133 } else { 1134 executableName = NULL; 1135 } 1136 } 1137 if (!executableName && url) { 1138 // Third, take the name of the bundle itself (with path extension stripped) 1139 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url); 1140 CFStringRef bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); 1141 CFRelease(absoluteURL); 1142 if (bundlePath) { 1143 CFIndex len = CFStringGetLength(bundlePath); 1144 CFIndex startOfBundleName = _CFStartOfLastPathComponent2(bundlePath); 1145 CFIndex endOfBundleName = _CFLengthAfterDeletingPathExtension2(bundlePath); 1146 1147 if (startOfBundleName <= len && endOfBundleName <= len && startOfBundleName < endOfBundleName) { 1148 executableName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, bundlePath, CFRangeMake(startOfBundleName, endOfBundleName - startOfBundleName)); 1149 } 1150 CFRelease(bundlePath); 1151 } 1152 } 1153 1154 return executableName; 1155 } 1156 1157 Boolean CFBundleIsExecutableLoaded(CFBundleRef bundle) { 1158 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1159 return bundle->_isLoaded; 1160 } 1161 1162 CFBundleExecutableType CFBundleGetExecutableType(CFBundleRef bundle) { 1163 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1164 CFBundleExecutableType result = kCFBundleOtherExecutableType; 1165 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 1166 1167 if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; 1168 #if defined(BINARY_SUPPORT_DYLD) 1169 if (bundle->_binaryType == __CFBundleUnknownBinary) { 1170 bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 1171 if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; 1172 } 1173 #endif /* BINARY_SUPPORT_DYLD */ 1174 if (executableURL) CFRelease(executableURL); 1175 1176 if (bundle->_binaryType == __CFBundleCFMBinary) { 1177 result = kCFBundlePEFExecutableType; 1178 } else if (bundle->_binaryType == __CFBundleDYLDExecutableBinary || bundle->_binaryType == __CFBundleDYLDBundleBinary || bundle->_binaryType == __CFBundleDYLDFrameworkBinary) { 1179 result = kCFBundleMachOExecutableType; 1180 } else if (bundle->_binaryType == __CFBundleDLLBinary) { 1181 result = kCFBundleDLLExecutableType; 1182 } else if (bundle->_binaryType == __CFBundleELFBinary) { 1183 result = kCFBundleELFExecutableType; 1184 } 1185 return result; 1186 } 1187 1188 void _CFBundleSetCFMConnectionID(CFBundleRef bundle, void *connectionID) { 1189 bundle->_connectionCookie = connectionID; 1190 bundle->_isLoaded = true; 1191 } 1192 1193 static CFStringRef _CFBundleCopyLastPathComponent(CFBundleRef bundle) { 1194 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle); 1195 if (!bundleURL) { 1196 return CFSTR("<unknown>"); 1197 } 1198 CFStringRef str = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle); 1199 UniChar buff[CFMaxPathSize]; 1200 CFIndex buffLen = CFStringGetLength(str), startOfLastDir = 0; 1201 1202 CFRelease(bundleURL); 1203 if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; 1204 CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); 1205 CFRelease(str); 1206 if (buffLen > 0) startOfLastDir = _CFStartOfLastPathComponent(buff, buffLen); 1207 return CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfLastDir]), buffLen - startOfLastDir); 1208 } 1209 1210 #pragma mark - 1211 1212 CF_PRIVATE CFErrorRef _CFBundleCreateErrorDebug(CFAllocatorRef allocator, CFBundleRef bundle, CFIndex code, CFStringRef debugString) { 1213 const void *userInfoKeys[6], *userInfoValues[6]; 1214 CFIndex numKeys = 0; 1215 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle), absoluteURL = CFURLCopyAbsoluteURL(bundleURL), executableURL = CFBundleCopyExecutableURL(bundle); 1216 CFBundleRef bdl = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreFoundation")); 1217 CFStringRef bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE), executablePath = executableURL ? CFURLCopyFileSystemPath(executableURL, PLATFORM_PATH_STYLE) : NULL, descFormat = NULL, desc = NULL, reason = NULL, suggestion = NULL; 1218 CFErrorRef error; 1219 if (bdl) { 1220 CFStringRef name = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey); 1221 name = name ? (CFStringRef)CFRetain(name) : _CFBundleCopyLastPathComponent(bundle); 1222 if (CFBundleExecutableNotFoundError == code) { 1223 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because its executable couldn\\U2019t be located."), "NSFileNoSuchFileError"); 1224 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4-C"), CFSTR("Error"), bdl, CFSTR("The bundle\\U2019s executable couldn\\U2019t be located."), "NSFileNoSuchFileError"); 1225 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSFileNoSuchFileError"); 1226 } else if (CFBundleExecutableNotLoadableError == code) { 1227 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because its executable isn\\U2019t loadable."), "NSExecutableNotLoadableError"); 1228 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584-C"), CFSTR("Error"), bdl, CFSTR("The bundle\\U2019s executable isn\\U2019t loadable."), "NSExecutableNotLoadableError"); 1229 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableNotLoadableError"); 1230 } else if (CFBundleExecutableArchitectureMismatchError == code) { 1231 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because it doesn\\U2019t contain a version for the current architecture."), "NSExecutableArchitectureMismatchError"); 1232 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585-C"), CFSTR("Error"), bdl, CFSTR("The bundle doesn\\U2019t contain a version for the current architecture."), "NSExecutableArchitectureMismatchError"); 1233 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585-R"), CFSTR("Error"), bdl, CFSTR("Try installing a universal version of the bundle."), "NSExecutableArchitectureMismatchError"); 1234 } else if (CFBundleExecutableRuntimeMismatchError == code) { 1235 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because it isn\\U2019t compatible with the current application."), "NSExecutableRuntimeMismatchError"); 1236 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586-C"), CFSTR("Error"), bdl, CFSTR("The bundle isn\\U2019t compatible with this application."), "NSExecutableRuntimeMismatchError"); 1237 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586-R"), CFSTR("Error"), bdl, CFSTR("Try installing a newer version of the bundle."), "NSExecutableRuntimeMismatchError"); 1238 } else if (CFBundleExecutableLoadError == code) { 1239 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because it is damaged or missing necessary resources."), "NSExecutableLoadError"); 1240 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587-C"), CFSTR("Error"), bdl, CFSTR("The bundle is damaged or missing necessary resources."), "NSExecutableLoadError"); 1241 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableLoadError"); 1242 } else if (CFBundleExecutableLinkError == code) { 1243 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded."), "NSExecutableLinkError"); 1244 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588-C"), CFSTR("Error"), bdl, CFSTR("The bundle couldn\\U2019t be loaded."), "NSExecutableLinkError"); 1245 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableLinkError"); 1246 } 1247 if (descFormat) { 1248 desc = CFStringCreateWithFormat(allocator, NULL, descFormat, name); 1249 CFRelease(descFormat); 1250 } 1251 CFRelease(name); 1252 } 1253 if (bundlePath) { 1254 userInfoKeys[numKeys] = CFSTR("NSBundlePath"); 1255 userInfoValues[numKeys] = bundlePath; 1256 numKeys++; 1257 } 1258 if (executablePath) { 1259 userInfoKeys[numKeys] = CFSTR("NSFilePath"); 1260 userInfoValues[numKeys] = executablePath; 1261 numKeys++; 1262 } 1263 if (desc) { 1264 userInfoKeys[numKeys] = kCFErrorLocalizedDescriptionKey; 1265 userInfoValues[numKeys] = desc; 1266 numKeys++; 1267 } 1268 if (reason) { 1269 userInfoKeys[numKeys] = kCFErrorLocalizedFailureReasonKey; 1270 userInfoValues[numKeys] = reason; 1271 numKeys++; 1272 } 1273 if (suggestion) { 1274 userInfoKeys[numKeys] = kCFErrorLocalizedRecoverySuggestionKey; 1275 userInfoValues[numKeys] = suggestion; 1276 numKeys++; 1277 } 1278 if (debugString) { 1279 userInfoKeys[numKeys] = kCFErrorDebugDescriptionKey; 1280 userInfoValues[numKeys] = debugString; 1281 numKeys++; 1282 } 1283 error = CFErrorCreateWithUserInfoKeysAndValues(allocator, kCFErrorDomainCocoa, code, userInfoKeys, userInfoValues, numKeys); 1284 if (bundleURL) CFRelease(bundleURL); 1285 if (absoluteURL) CFRelease(absoluteURL); 1286 if (executableURL) CFRelease(executableURL); 1287 if (bundlePath) CFRelease(bundlePath); 1288 if (executablePath) CFRelease(executablePath); 1289 if (desc) CFRelease(desc); 1290 if (reason) CFRelease(reason); 1291 if (suggestion) CFRelease(suggestion); 1292 return error; 1293 } 1294 1295 CFErrorRef _CFBundleCreateError(CFAllocatorRef allocator, CFBundleRef bundle, CFIndex code) { 1296 return _CFBundleCreateErrorDebug(allocator, bundle, code, NULL); 1297 } 1298 1299 #pragma mark - 1300 1301 Boolean _CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, Boolean forceGlobal, CFErrorRef *error) { 1302 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1303 Boolean result = false; 1304 CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); 1305 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 1306 1307 1308 _CFMutexLock(&(bundle->_bundleLoadingLock)); 1309 if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; 1310 // make sure we know whether bundle is already loaded or not 1311 #if defined(BINARY_SUPPORT_DLFCN) 1312 if (!bundle->_isLoaded) _CFBundleDlfcnCheckLoaded(bundle); 1313 #elif defined(BINARY_SUPPORT_DYLD) 1314 if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); 1315 #endif /* BINARY_SUPPORT_DLFCN */ 1316 #if defined(BINARY_SUPPORT_DYLD) 1317 // We might need to figure out what it is 1318 if (bundle->_binaryType == __CFBundleUnknownBinary) { 1319 bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 1320 if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; 1321 } 1322 #endif /* BINARY_SUPPORT_DYLD */ 1323 if (executableURL) CFRelease(executableURL); 1324 1325 if (bundle->_isLoaded) { 1326 _CFMutexUnlock(&(bundle->_bundleLoadingLock)); 1327 _CFPlugInUnscheduleForUnloading(bundle); 1328 return true; 1329 } 1330 1331 // n.b. some applications may call API like CFBundleGetBundleWithIdentifier during library initialization. This could wind up recursively taking this lock. Therefore, even though it results in the potential for a race on the internals of bundle's data structures, we have to unlock before calling out to dlopen. rdar://67319441 1332 _CFMutexUnlock(&(bundle->_bundleLoadingLock)); 1333 1334 switch (bundle->_binaryType) { 1335 #if defined(BINARY_SUPPORT_DLFCN) 1336 case __CFBundleUnreadableBinary: 1337 result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); 1338 break; 1339 #endif /* BINARY_SUPPORT_DLFCN */ 1340 #if defined(BINARY_SUPPORT_DYLD) 1341 case __CFBundleDYLDBundleBinary: 1342 #if defined(BINARY_SUPPORT_DLFCN) 1343 result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); 1344 #else /* BINARY_SUPPORT_DLFCN */ 1345 result = _CFBundleDYLDLoadBundle(bundle, forceGlobal, subError); 1346 #endif /* BINARY_SUPPORT_DLFCN */ 1347 break; 1348 case __CFBundleDYLDFrameworkBinary: 1349 #if defined(BINARY_SUPPORT_DLFCN) 1350 result = _CFBundleDlfcnLoadFramework(bundle, subError); 1351 #else /* BINARY_SUPPORT_DLFCN */ 1352 result = _CFBundleDYLDLoadFramework(bundle, subError); 1353 #endif /* BINARY_SUPPORT_DLFCN */ 1354 break; 1355 case __CFBundleDYLDExecutableBinary: 1356 if (error) { 1357 localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); 1358 } else { 1359 CFLog(__kCFLogBundle, CFSTR("Attempt to load executable of a type that cannot be dynamically loaded for %@"), bundle); 1360 } 1361 break; 1362 #endif /* BINARY_SUPPORT_DYLD */ 1363 #if defined(BINARY_SUPPORT_DLFCN) 1364 case __CFBundleUnknownBinary: 1365 case __CFBundleELFBinary: 1366 result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); 1367 break; 1368 #endif /* BINARY_SUPPORT_DLFCN */ 1369 #if defined(BINARY_SUPPORT_DLL) 1370 case __CFBundleDLLBinary: 1371 result = _CFBundleDLLLoad(bundle, subError); 1372 break; 1373 #endif /* BINARY_SUPPORT_DLL */ 1374 case __CFBundleNoBinary: 1375 if (error) { 1376 localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); 1377 } else { 1378 CFLog(__kCFLogBundle, CFSTR("Cannot find executable for %@"), bundle); 1379 } 1380 break; 1381 default: 1382 if (error) { 1383 localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); 1384 } else { 1385 CFLog(__kCFLogBundle, CFSTR("Cannot recognize type of executable for %@"), bundle); 1386 } 1387 break; 1388 } 1389 1390 if (result && bundle->_plugInData._isPlugIn) { 1391 _CFPlugInHandleDynamicRegistration(bundle); 1392 } 1393 if (!result && error) *error = localError; 1394 return result; 1395 } 1396 1397 Boolean CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, CFErrorRef *error) { 1398 return _CFBundleLoadExecutableAndReturnError(bundle, false, error); 1399 } 1400 1401 Boolean CFBundleLoadExecutable(CFBundleRef bundle) { 1402 return _CFBundleLoadExecutableAndReturnError(bundle, false, NULL); 1403 } 1404 1405 Boolean CFBundlePreflightExecutable(CFBundleRef bundle, CFErrorRef *error) { 1406 Boolean result = false; 1407 CFErrorRef localError = NULL; 1408 #if defined(BINARY_SUPPORT_DLFCN) 1409 CFErrorRef *subError = (error ? &localError : NULL); 1410 #endif 1411 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 1412 1413 _CFMutexLock(&(bundle->_bundleLoadingLock)); 1414 if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; 1415 // make sure we know whether bundle is already loaded or not 1416 #if defined(BINARY_SUPPORT_DLFCN) 1417 if (!bundle->_isLoaded) _CFBundleDlfcnCheckLoaded(bundle); 1418 #elif defined(BINARY_SUPPORT_DYLD) 1419 if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); 1420 #endif /* BINARY_SUPPORT_DLFCN */ 1421 #if defined(BINARY_SUPPORT_DYLD) 1422 // We might need to figure out what it is 1423 if (bundle->_binaryType == __CFBundleUnknownBinary) { 1424 bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 1425 if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; 1426 } 1427 #endif /* BINARY_SUPPORT_DYLD */ 1428 if (executableURL) CFRelease(executableURL); 1429 1430 if (bundle->_isLoaded) { 1431 _CFMutexUnlock(&(bundle->_bundleLoadingLock)); 1432 return true; 1433 } 1434 _CFMutexUnlock(&(bundle->_bundleLoadingLock)); 1435 1436 switch (bundle->_binaryType) { 1437 #if defined(BINARY_SUPPORT_DLFCN) 1438 case __CFBundleUnreadableBinary: 1439 result = _CFBundleDlfcnPreflight(bundle, subError); 1440 break; 1441 #endif /* BINARY_SUPPORT_DLFCN */ 1442 #if defined(BINARY_SUPPORT_DYLD) 1443 case __CFBundleDYLDBundleBinary: 1444 result = true; 1445 #if defined(BINARY_SUPPORT_DLFCN) 1446 result = _CFBundleDlfcnPreflight(bundle, subError); 1447 #endif /* BINARY_SUPPORT_DLFCN */ 1448 break; 1449 case __CFBundleDYLDFrameworkBinary: 1450 result = true; 1451 #if defined(BINARY_SUPPORT_DLFCN) 1452 result = _CFBundleDlfcnPreflight(bundle, subError); 1453 #endif /* BINARY_SUPPORT_DLFCN */ 1454 break; 1455 case __CFBundleDYLDExecutableBinary: 1456 if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); 1457 break; 1458 #endif /* BINARY_SUPPORT_DYLD */ 1459 #if defined(BINARY_SUPPORT_DLFCN) 1460 case __CFBundleUnknownBinary: 1461 case __CFBundleELFBinary: 1462 result = _CFBundleDlfcnPreflight(bundle, subError); 1463 break; 1464 #endif /* BINARY_SUPPORT_DLFCN */ 1465 #if defined(BINARY_SUPPORT_DLL) 1466 case __CFBundleDLLBinary: 1467 result = true; 1468 break; 1469 #endif /* BINARY_SUPPORT_DLL */ 1470 case __CFBundleNoBinary: 1471 if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); 1472 break; 1473 default: 1474 if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); 1475 break; 1476 } 1477 if (!result && error) *error = localError; 1478 return result; 1479 } 1480 1481 CFArrayRef CFBundleCopyExecutableArchitectures(CFBundleRef bundle) { 1482 CFArrayRef result = NULL; 1483 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 1484 if (executableURL) { 1485 result = _CFBundleCopyArchitecturesForExecutable(executableURL); 1486 CFRelease(executableURL); 1487 } 1488 return result; 1489 } 1490 1491 void CFBundleUnloadExecutable(CFBundleRef bundle) { 1492 _CFBundleUnloadExecutable(bundle, false); 1493 } 1494 1495 CF_PRIVATE void _CFBundleUnloadExecutable(CFBundleRef bundle, Boolean unloadingPlugins) { 1496 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1497 // First unload bundles scheduled for unloading (if that's not what we are already doing.) 1498 if (!unloadingPlugins) _CFPlugInUnloadScheduledPlugIns(); 1499 1500 if (!bundle->_isLoaded) return; 1501 1502 // Remove from the scheduled unload set if we are there. 1503 _CFPlugInUnscheduleForUnloading(bundle); 1504 1505 // Give the plugIn code a chance to realize this... 1506 _CFPlugInWillUnload(bundle); 1507 1508 _CFMutexLock(&(bundle->_bundleLoadingLock)); 1509 if (!bundle->_isLoaded) { 1510 _CFMutexUnlock(&(bundle->_bundleLoadingLock)); 1511 return; 1512 } 1513 1514 switch (bundle->_binaryType) { 1515 #if defined(BINARY_SUPPORT_DYLD) 1516 case __CFBundleDYLDBundleBinary: 1517 #if defined(BINARY_SUPPORT_DLFCN) 1518 if (bundle->_handleCookie) _CFBundleDlfcnUnload(bundle); 1519 #else /* BINARY_SUPPORT_DLFCN */ 1520 _CFBundleDYLDUnloadBundle(bundle); 1521 #endif /* BINARY_SUPPORT_DLFCN */ 1522 break; 1523 case __CFBundleDYLDFrameworkBinary: 1524 #if defined(BINARY_SUPPORT_DLFCN) 1525 if (bundle->_handleCookie && _CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) _CFBundleDlfcnUnload(bundle); 1526 #endif /* BINARY_SUPPORT_DLFCN */ 1527 break; 1528 #endif /* BINARY_SUPPORT_DYLD */ 1529 #if defined(BINARY_SUPPORT_DLL) 1530 case __CFBundleDLLBinary: 1531 _CFBundleDLLUnload(bundle); 1532 break; 1533 #endif /* BINARY_SUPPORT_DLL */ 1534 default: 1535 #if defined(BINARY_SUPPORT_DLFCN) 1536 if (bundle->_handleCookie) _CFBundleDlfcnUnload(bundle); 1537 #endif /* BINARY_SUPPORT_DLFCN */ 1538 break; 1539 } 1540 1541 _CFMutexUnlock(&(bundle->_bundleLoadingLock)); 1542 } 1543 1544 #pragma mark - 1545 1546 CF_PRIVATE _CFResourceData *__CFBundleGetResourceData(CFBundleRef bundle) { 1547 return &(bundle->_resourceData); 1548 } 1549 1550 CFPlugInRef CFBundleGetPlugIn(CFBundleRef bundle) { 1551 return (bundle->_plugInData._isPlugIn) ? (CFPlugInRef)bundle : NULL; 1552 } 1553 1554 CF_PRIVATE _CFPlugInData *__CFBundleGetPlugInData(CFBundleRef bundle) { 1555 return &(bundle->_plugInData); 1556 } 1557 1558 CF_PRIVATE Boolean _CFBundleCouldBeBundle(CFURLRef url) { 1559 Boolean result = false; 1560 Boolean exists; 1561 SInt32 mode; 1562 if (_CFGetFileProperties(kCFAllocatorSystemDefault, url, &exists, &mode, NULL, NULL, NULL, NULL) == 0) result = (exists && (mode & S_IFMT) == S_IFDIR && (mode & 0444) != 0); 1563 return result; 1564 } 1565 1566 #define LENGTH_OF(A) (sizeof(A) / sizeof(A[0])) 1567 1568 //If 'permissive' is set, we will maintain the historical behavior of returning frameworks with names that don't match, and frameworks for executables in Resources/ 1569 static CFURLRef __CFBundleCopyFrameworkURLForExecutablePath(CFStringRef executablePath, Boolean permissive) { 1570 // MF:!!! Implement me. We need to be able to find the bundle from the exe, dealing with old vs. new as well as the Executables dir business on Windows. 1571 #if TARGET_OS_WIN32 1572 UniChar executablesToFrameworksPathBuff[] = {'.', '.', '\\', 'F', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k', 's'}; 1573 UniChar executablesToPrivateFrameworksPathBuff[] = {'.', '.', '\\', 'P', 'r', 'i', 'v', 'a', 't', 'e', 'F', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k', 's'}; 1574 UniChar frameworksExtension[] = {'f', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k'}; 1575 #endif 1576 UniChar pathBuff[CFMaxPathSize] = {0}; 1577 UniChar nameBuff[CFMaxPathSize] = {0}; 1578 CFIndex length, nameStart, nameLength, savedLength; 1579 CFMutableStringRef cheapStr = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, NULL); 1580 CFURLRef bundleURL = NULL; 1581 1582 length = CFStringGetLength(executablePath); 1583 if (length > CFMaxPathSize) length = CFMaxPathSize; 1584 CFStringGetCharacters(executablePath, CFRangeMake(0, length), pathBuff); 1585 1586 // Save the name in nameBuff 1587 length = _CFLengthAfterDeletingPathExtension(pathBuff, length); 1588 nameStart = _CFStartOfLastPathComponent(pathBuff, length); 1589 nameLength = length - nameStart; 1590 memmove(nameBuff, &(pathBuff[nameStart]), nameLength * sizeof(UniChar)); 1591 1592 // Strip the name from pathBuff 1593 length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); 1594 savedLength = length; 1595 1596 #if TARGET_OS_WIN32 1597 // * (Windows-only) First check the "Executables" directory parallel to the "Frameworks" directory case. 1598 if (_CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, executablesToFrameworksPathBuff, LENGTH_OF(executablesToFrameworksPathBuff)) && _CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, nameBuff, nameLength) && _CFAppendPathExtension(pathBuff, &length, CFMaxPathSize, frameworksExtension, LENGTH_OF(frameworksExtension))) { 1599 CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); 1600 bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cheapStr, PLATFORM_PATH_STYLE, true); 1601 if (!_CFBundleCouldBeBundle(bundleURL)) { 1602 CFRelease(bundleURL); 1603 bundleURL = NULL; 1604 } 1605 } 1606 // * (Windows-only) Next check the "Executables" directory parallel to the "PrivateFrameworks" directory case. 1607 if (!bundleURL) { 1608 length = savedLength; 1609 if (_CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, executablesToPrivateFrameworksPathBuff, LENGTH_OF(executablesToPrivateFrameworksPathBuff)) && _CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, nameBuff, nameLength) && _CFAppendPathExtension(pathBuff, &length, CFMaxPathSize, frameworksExtension, LENGTH_OF(frameworksExtension))) { 1610 CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); 1611 bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cheapStr, PLATFORM_PATH_STYLE, true); 1612 if (!_CFBundleCouldBeBundle(bundleURL)) { 1613 CFRelease(bundleURL); 1614 bundleURL = NULL; 1615 } 1616 } 1617 } 1618 #endif 1619 // * Finally check the executable inside the framework case. 1620 if (!bundleURL) { 1621 length = savedLength; 1622 // To catch all the cases, we just peel off level looking for one ending in .framework or one called "Supporting Files". 1623 1624 CFStringRef name = permissive ? CFSTR("") : CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, (const char *)nameBuff); 1625 1626 while (length > 0) { 1627 CFIndex curStart = _CFStartOfLastPathComponent(pathBuff, length); 1628 if (curStart >= length) break; 1629 CFStringSetExternalCharactersNoCopy(cheapStr, &(pathBuff[curStart]), length - curStart, CFMaxPathSize - curStart); 1630 if (!permissive && CFEqual(cheapStr, _CFBundleResourcesDirectoryName)) break; 1631 if (CFEqual(cheapStr, _CFBundleSupportFilesDirectoryName1) || CFEqual(cheapStr, _CFBundleSupportFilesDirectoryName2)) { 1632 if (!permissive) { 1633 CFIndex fmwkStart = _CFStartOfLastPathComponent(pathBuff, length); 1634 CFStringSetExternalCharactersNoCopy(cheapStr, &(pathBuff[fmwkStart]), length - fmwkStart, CFMaxPathSize - fmwkStart); 1635 } 1636 if (permissive || CFStringHasPrefix(cheapStr, name)) { 1637 length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); 1638 CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); 1639 1640 bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cheapStr, PLATFORM_PATH_STYLE, true); 1641 if (!_CFBundleCouldBeBundle(bundleURL)) { 1642 CFRelease(bundleURL); 1643 bundleURL = NULL; 1644 } 1645 break; 1646 } 1647 } else if (CFStringHasSuffix(cheapStr, CFSTR(".framework")) && (permissive || CFStringHasPrefix(cheapStr, name))) { 1648 CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); 1649 bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cheapStr, PLATFORM_PATH_STYLE, true); 1650 if (!_CFBundleCouldBeBundle(bundleURL)) { 1651 CFRelease(bundleURL); 1652 bundleURL = NULL; 1653 } 1654 break; 1655 } 1656 length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); 1657 } 1658 if (!permissive) CFRelease(name); 1659 } 1660 CFStringSetExternalCharactersNoCopy(cheapStr, NULL, 0, 0); 1661 CFRelease(cheapStr); 1662 1663 return bundleURL; 1664 } 1665 1666 //SPI version; separated out to minimize linkage changes 1667 CFURLRef _CFBundleCopyFrameworkURLForExecutablePath(CFStringRef executablePath) { 1668 return __CFBundleCopyFrameworkURLForExecutablePath(executablePath, false); 1669 } 1670 1671 static void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath, Boolean permissive) { 1672 // This finds the bundle for the given path. 1673 // If an image path corresponds to a bundle, we see if there is already a bundle instance. If there is and it is NOT in the _dynamicBundles array, it is added to the staticBundles. Do not add the main bundle to the list here. 1674 CFBundleRef bundle; 1675 CFURLRef curURL = __CFBundleCopyFrameworkURLForExecutablePath(imagePath, permissive); 1676 1677 if (curURL) { 1678 // Ensure bundle exists by creating it if necessary. This will check the tables as a first step. 1679 // NB doFinalProcessing must be false here, see below 1680 bundle = _CFBundleCreate(kCFAllocatorSystemDefault, curURL, false, false, true); 1681 if (bundle) { 1682 _CFMutexLock(&(bundle->_bundleLoadingLock)); 1683 if (!bundle->_isLoaded) { 1684 // make sure that these bundles listed as loaded, and mark them frameworks (we probably can't see anything else here, and we cannot unload them) 1685 #if defined(BINARY_SUPPORT_DLFCN) 1686 if (!bundle->_isLoaded) _CFBundleDlfcnCheckLoaded(bundle); 1687 #elif defined(BINARY_SUPPORT_DYLD) 1688 if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); 1689 #endif /* BINARY_SUPPORT_DLFCN */ 1690 #if defined(BINARY_SUPPORT_DYLD) 1691 if (bundle->_binaryType == __CFBundleUnknownBinary) bundle->_binaryType = __CFBundleDYLDFrameworkBinary; 1692 if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; 1693 #endif /* BINARY_SUPPORT_DYLD */ 1694 #if LOG_BUNDLE_LOAD 1695 if (!bundle->_isLoaded) printf("ensure bundle %p set loaded fallback, handle %p image %p conn %p\n", bundle, bundle->_handleCookie, bundle->_imageCookie, bundle->_connectionCookie); 1696 #endif /* LOG_BUNDLE_LOAD */ 1697 bundle->_isLoaded = true; 1698 } 1699 _CFMutexUnlock(&(bundle->_bundleLoadingLock)); 1700 // Perform delayed final processing steps. 1701 // This must be done after _isLoaded has been set, for security reasons (3624341). 1702 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 1703 _CFBundleInitPlugIn(bundle, infoDict, NULL); 1704 _CFPlugInHandleDynamicRegistration(bundle); 1705 } 1706 CFRelease(curURL); 1707 } 1708 } 1709 1710 static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths) { 1711 // This finds the bundles for the given paths. 1712 // If an image path corresponds to a bundle, we see if there is already a bundle instance. If there is and it is NOT in the _dynamicBundles array, it is added to the staticBundles. Do not add the main bundle to the list here (even if it appears in imagePaths). 1713 CFIndex i, imagePathCount = CFArrayGetCount(imagePaths); 1714 for (i = 0; i < imagePathCount; i++) _CFBundleEnsureBundleExistsForImagePath((CFStringRef)CFArrayGetValueAtIndex(imagePaths, i), true); 1715 } 1716 1717 static void _CFBundleEnsureBundlesUpToDateWithHint(CFStringRef hint) { 1718 CFArrayRef imagePaths = NULL; 1719 // Tickle the main bundle into existence 1720 (void)CFBundleGetMainBundle(); 1721 #if defined(BINARY_SUPPORT_DYLD) 1722 imagePaths = _CFBundleDYLDCopyLoadedImagePathsForHint(hint); 1723 #endif /* BINARY_SUPPORT_DYLD */ 1724 if (imagePaths) { 1725 _CFBundleEnsureBundlesExistForImagePaths(imagePaths); 1726 CFRelease(imagePaths); 1727 } 1728 } 1729 1730 CFBundleRef _CFBundleGetBundleWithIdentifierAndLibraryName(CFStringRef bundleID, CFStringRef libraryName) { 1731 if (libraryName) { 1732 _CFBundleEnsureBundlesUpToDateWithHint(libraryName); 1733 } 1734 return _CFBundleGetFromTables(bundleID); 1735 } 1736 1737 static void _CFBundleEnsureAllBundlesUpToDate(void) { 1738 // This method returns all the statically linked bundles. This includes the main bundle as well as any frameworks that the process was linked against at launch time. It does not include frameworks or opther bundles that were loaded dynamically. 1739 CFArrayRef imagePaths = NULL; 1740 // Tickle the main bundle into existence 1741 (void)CFBundleGetMainBundle(); 1742 1743 #if defined(BINARY_SUPPORT_DLL) 1744 // Don't know how to find static bundles for DLLs 1745 #endif /* BINARY_SUPPORT_DLL */ 1746 1747 #if defined(BINARY_SUPPORT_DYLD) 1748 imagePaths = _CFBundleDYLDCopyLoadedImagePathsIfChanged(); 1749 #endif /* BINARY_SUPPORT_DYLD */ 1750 if (imagePaths) { 1751 _CFBundleEnsureBundlesExistForImagePaths(imagePaths); 1752 CFRelease(imagePaths); 1753 } 1754 } 1755 1756 CFArrayRef CFBundleGetAllBundles(void) { 1757 // This API is fundamentally broken from a thread safety point of view. To mitigate the issues, we keep around the last list we handed out. If the list of allBundles changed, we leak the last one and return a new copy. If no bundle loading is done this list would be static. 1758 // Fortunately this method is rarely used. 1759 CFArrayRef result = NULL; 1760 _CFMutexLock(&CFBundleGlobalDataLock); 1761 static CFArrayRef _lastBundleList = NULL; 1762 if (!_lastBundleList) { 1763 // This is the first time we've been asked for a list of all bundles 1764 // Unlock the global lock. CopyAllBundles will use it. 1765 _CFMutexUnlock(&CFBundleGlobalDataLock); 1766 result = _CFBundleCopyAllBundles(); 1767 _CFMutexLock(&CFBundleGlobalDataLock); 1768 if (_lastBundleList) { 1769 // Another thread beat us here 1770 CFRelease(result); 1771 } else { 1772 _lastBundleList = result; 1773 } 1774 } else if (!CFEqual(_lastBundleList, _allBundles)) { 1775 // Check if the list of bundles has changed 1776 _CFMutexUnlock(&CFBundleGlobalDataLock); 1777 result = _CFBundleCopyAllBundles(); 1778 _CFMutexLock(&CFBundleGlobalDataLock); 1779 // note: intentionally leak the last value in _lastBundleList, due to API contract of 'get' 1780 _lastBundleList = result; 1781 } 1782 result = _lastBundleList; 1783 _CFMutexUnlock(&CFBundleGlobalDataLock); 1784 return result; 1785 } 1786 1787 CF_EXPORT CFArrayRef _CFBundleCopyAllBundles(void) { 1788 // To answer this properly, we have to have created the static bundles! 1789 _CFBundleEnsureAllBundlesUpToDate(); 1790 CFBundleRef main = CFBundleGetMainBundle(); 1791 _CFMutexLock(&CFBundleGlobalDataLock); 1792 // _allBundles does not include the main bundle, so insert it here. 1793 CFMutableArrayRef bundles = CFArrayCreateMutableCopy(kCFAllocatorSystemDefault, CFArrayGetCount(_allBundles) + 1, _allBundles); 1794 _CFMutexUnlock(&CFBundleGlobalDataLock); 1795 CFArrayInsertValueAtIndex(bundles, 0, main); 1796 return bundles; 1797 } 1798 1799 CF_PRIVATE _CFBundleVersion _CFBundleLayoutVersion(CFBundleRef bundle) { 1800 return bundle->_version; 1801 } 1802 1803 CF_EXPORT CFURLRef _CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle) { 1804 return CFBundleCopyPrivateFrameworksURL(bundle); 1805 } 1806 1807 CF_EXPORT CFURLRef CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle) { 1808 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1809 CFURLRef result = NULL; 1810 1811 if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { 1812 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase1, bundle->_url); 1813 } else if (_CFBundleVersionContentsResources == bundle->_version) { 1814 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase2, bundle->_url); 1815 } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { 1816 result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedPrivateFrameworksURLFromBase2, bundle->_url); 1817 } else if (_CFBundleVersionWrappedFlat == bundle->_version) { 1818 result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedPrivateFrameworksURLFromBase3, bundle->_url); 1819 } else { 1820 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase0, bundle->_url); 1821 } 1822 return result; 1823 } 1824 1825 CF_EXPORT CFURLRef _CFBundleCopySharedFrameworksURL(CFBundleRef bundle) { 1826 return CFBundleCopySharedFrameworksURL(bundle); 1827 } 1828 1829 CF_EXPORT CFURLRef CFBundleCopySharedFrameworksURL(CFBundleRef bundle) { 1830 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1831 CFURLRef result = NULL; 1832 1833 if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { 1834 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase1, bundle->_url); 1835 } else if (_CFBundleVersionContentsResources == bundle->_version) { 1836 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase2, bundle->_url); 1837 } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { 1838 result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedSharedFrameworksURLFromBase2, bundle->_url); 1839 } else if (_CFBundleVersionWrappedFlat == bundle->_version) { 1840 result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedSharedFrameworksURLFromBase3, bundle->_url); 1841 } else { 1842 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase0, bundle->_url); 1843 } 1844 return result; 1845 } 1846 1847 CF_EXPORT CFURLRef _CFBundleCopySharedSupportURL(CFBundleRef bundle) { 1848 return CFBundleCopySharedSupportURL(bundle); 1849 } 1850 1851 CF_EXPORT CFURLRef CFBundleCopySharedSupportURL(CFBundleRef bundle) { 1852 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1853 CFURLRef result = NULL; 1854 1855 if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { 1856 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase1, bundle->_url); 1857 } else if (_CFBundleVersionContentsResources == bundle->_version) { 1858 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase2, bundle->_url); 1859 } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { 1860 result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedSharedSupportURLFromBase2, bundle->_url); 1861 } else if (_CFBundleVersionWrappedFlat == bundle->_version) { 1862 result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedSharedSupportURLFromBase3, bundle->_url); 1863 } else { 1864 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase0, bundle->_url); 1865 } 1866 return result; 1867 } 1868 1869 CF_PRIVATE CFURLRef _CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { 1870 return CFBundleCopyBuiltInPlugInsURL(bundle); 1871 } 1872 1873 CF_EXPORT CFURLRef CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { 1874 CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); 1875 CFURLRef result = NULL, alternateResult = NULL; 1876 1877 CFAllocatorRef alloc = CFGetAllocator(bundle); 1878 if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { 1879 result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase1, bundle->_url); 1880 } else if (_CFBundleVersionContentsResources == bundle->_version) { 1881 result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase2, bundle->_url); 1882 } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { 1883 result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedBuiltInPlugInsURLFromBase2, bundle->_url); 1884 } else if (_CFBundleVersionWrappedFlat == bundle->_version) { 1885 result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedBuiltInPlugInsURLFromBase3, bundle->_url); 1886 } else { 1887 result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase0, bundle->_url); 1888 } 1889 if (!result || !_CFURLExists(result)) { 1890 if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { 1891 alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase1, bundle->_url); 1892 } else if (_CFBundleVersionContentsResources == bundle->_version) { 1893 alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase2, bundle->_url); 1894 } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { 1895 alternateResult = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedAlternateBuiltInPlugInsURLFromBase2, bundle->_url); 1896 } else if (_CFBundleVersionWrappedFlat == bundle->_version) { 1897 alternateResult = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedAlternateBuiltInPlugInsURLFromBase3, bundle->_url); 1898 } else { 1899 alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase0, bundle->_url); 1900 } 1901 if (alternateResult && _CFURLExists(alternateResult)) { 1902 if (result) CFRelease(result); 1903 result = alternateResult; 1904 } else { 1905 if (alternateResult) CFRelease(alternateResult); 1906 } 1907 } 1908 return result; 1909 } 1910