CFBundle_Executable.c
1 /* CFBundle_Executable.c 2 Copyright (c) 1999-2017, Apple Inc. and the Swift project authors 3 4 Portions Copyright (c) 2014-2017, 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/CFBase.h> 12 #include <CoreFoundation/CFBundle.h> 13 #include "CFBundle_Internal.h" 14 15 #if TARGET_OS_IPHONE 16 #include <dlfcn.h> 17 #endif 18 19 #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 20 21 #if TARGET_OS_LINUX 22 #if TARGET_RT_64_BIT 23 #define _CFBundleFHSArchDirectorySuffix "64" 24 #else // !TARGET_RT_64_BIT 25 #define _CFBundleFHSArchDirectorySuffix "32" 26 #endif // TARGET_RT_64_BIT 27 #endif // TARGET_OS_LINUX 28 29 CONST_STRING_DECL(_kCFBundleFHSDirectory_bin, "bin"); 30 CONST_STRING_DECL(_kCFBundleFHSDirectory_sbin, "sbin"); 31 CONST_STRING_DECL(_kCFBundleFHSDirectory_lib, "lib"); 32 #if TARGET_OS_LINUX 33 CONST_STRING_DECL(_kCFBundleFHSDirectory_libWithArchSuffix, "lib" _CFBundleFHSArchDirectorySuffix); 34 #endif 35 36 #define _CFBundleFHSExecutablesDirectorySuffix CFSTR(".executables") 37 #define _CFBundleFHSDirectoryCLiteral_libexec "libexec" 38 39 #if TARGET_OS_LINUX 40 #define _CFBundleFHSDirectoriesInExecutableSearchOrder \ 41 _kCFBundleFHSDirectory_bin, \ 42 _kCFBundleFHSDirectory_sbin, \ 43 _kCFBundleFHSDirectory_libWithArchSuffix, \ 44 _kCFBundleFHSDirectory_lib 45 #else 46 #define _CFBundleFHSDirectoriesInExecutableSearchOrder \ 47 _kCFBundleFHSDirectory_bin, \ 48 _kCFBundleFHSDirectory_sbin, \ 49 _kCFBundleFHSDirectory_lib 50 #endif // TARGET_OS_LINUX 51 52 #endif // !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 53 54 // This is here because on iPhoneOS with the dyld shared cache, we remove binaries from their 55 // original locations on disk, so checking whether a binary's path exists is no longer sufficient. 56 // For performance reasons, we only call dlopen_preflight() after we've verified that the binary 57 // does not exist at its original path with _CFURLExists(). 58 // See <rdar://problem/6956670> 59 static Boolean _binaryLoadable(CFURLRef url) { 60 Boolean loadable = _CFURLExists(url); 61 #if TARGET_OS_IPHONE 62 if (!loadable) { 63 uint8_t path[PATH_MAX]; 64 if (url && CFURLGetFileSystemRepresentation(url, true, path, sizeof(path))) { 65 loadable = dlopen_preflight((char *)path); 66 } 67 } 68 #endif 69 return loadable; 70 } 71 72 static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeName) { 73 // Given an url to a folder and a name, this returns the url to the executable in that folder with that name, if it exists, and NULL otherwise. This function deals with appending the ".exe" or ".dll" on Windows. 74 CFURLRef executableURL = NULL; 75 if (!urlPath || !exeName) return NULL; 76 77 #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 78 if (!executableURL) { 79 executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath); 80 if (!_binaryLoadable(executableURL)) { 81 CFRelease(executableURL); 82 83 CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleFHSSharedLibraryFilenamePrefix, exeName, _CFBundleFHSSharedLibraryFilenameSuffix); 84 85 executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, kCFURLPOSIXPathStyle, false, urlPath); 86 if (!_binaryLoadable(executableURL)) { 87 CFRelease(executableURL); 88 executableURL = NULL; 89 } 90 if (sharedLibraryName) CFRelease(sharedLibraryName); 91 } 92 } 93 #elif TARGET_OS_MAC 94 const uint8_t *image_suffix = (uint8_t *)__CFgetenvIfNotRestricted("DYLD_IMAGE_SUFFIX"); 95 96 if (image_suffix) { 97 CFStringRef newExeName, imageSuffix; 98 imageSuffix = CFStringCreateWithCString(kCFAllocatorSystemDefault, (char *)image_suffix, kCFStringEncodingUTF8); 99 if (CFStringHasSuffix(exeName, CFSTR(".dylib"))) { 100 CFStringRef bareExeName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, exeName, CFRangeMake(0, CFStringGetLength(exeName)-6)); 101 newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@.dylib"), exeName, imageSuffix); 102 CFRelease(bareExeName); 103 } else { 104 newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, imageSuffix); 105 } 106 executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, newExeName, kCFURLPOSIXPathStyle, false, urlPath); 107 if (executableURL && !_binaryLoadable(executableURL)) { 108 CFRelease(executableURL); 109 executableURL = NULL; 110 } 111 CFRelease(newExeName); 112 CFRelease(imageSuffix); 113 } 114 if (!executableURL) { 115 executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath); 116 if (executableURL && !_binaryLoadable(executableURL)) { 117 CFRelease(executableURL); 118 executableURL = NULL; 119 } 120 } 121 #elif TARGET_OS_WIN32 122 if (!executableURL) { 123 executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLWindowsPathStyle, false, urlPath); 124 if (executableURL && !_binaryLoadable(executableURL)) { 125 CFRelease(executableURL); 126 executableURL = NULL; 127 } 128 } 129 if (!executableURL) { 130 if (!CFStringFindWithOptions(exeName, CFSTR(".dll"), CFRangeMake(0, CFStringGetLength(exeName)), kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) { 131 #if defined(DEBUG) 132 CFStringRef extension = CFSTR("_debug.dll"); 133 #else 134 CFStringRef extension = CFSTR(".dll"); 135 #endif 136 CFStringRef newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, extension); 137 executableURL = CFURLCreateWithString(kCFAllocatorSystemDefault, newExeName, urlPath); 138 if (executableURL && !_binaryLoadable(executableURL)) { 139 CFRelease(executableURL); 140 executableURL = NULL; 141 } 142 CFRelease(newExeName); 143 } 144 } 145 if (!executableURL) { 146 if (!CFStringFindWithOptions(exeName, CFSTR(".exe"), CFRangeMake(0, CFStringGetLength(exeName)), kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) { 147 #if defined(DEBUG) 148 CFStringRef extension = CFSTR("_debug.exe"); 149 #else 150 CFStringRef extension = CFSTR(".exe"); 151 #endif 152 CFStringRef newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, extension); 153 executableURL = CFURLCreateWithString(kCFAllocatorSystemDefault, newExeName, urlPath); 154 if (executableURL && !_binaryLoadable(executableURL)) { 155 CFRelease(executableURL); 156 executableURL = NULL; 157 } 158 CFRelease(newExeName); 159 } 160 } 161 #endif 162 return executableURL; 163 } 164 165 static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURLRef url, CFStringRef executableName, Boolean ignoreCache) { 166 uint8_t version = 0; 167 CFDictionaryRef infoDict = NULL; 168 CFStringRef executablePath = NULL; 169 CFURLRef executableURL = NULL; 170 Boolean foundIt = false; 171 Boolean lookupMainExe = (executableName ? false : true); 172 173 if (bundle) { 174 infoDict = CFBundleGetInfoDictionary(bundle); 175 version = bundle->_version; 176 } else { 177 infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, &version); 178 } 179 180 // If we have a bundle instance and an info dict, see if we have already cached the path 181 if (lookupMainExe && !ignoreCache && bundle && bundle->_executablePath) { 182 __CFLock(&bundle->_lock); 183 executablePath = bundle->_executablePath; 184 if (executablePath) CFRetain(executablePath); 185 __CFUnlock(&bundle->_lock); 186 if (executablePath) { 187 executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, executablePath, PLATFORM_PATH_STYLE, false); 188 if (executableURL) { 189 foundIt = true; 190 } 191 CFRelease(executablePath); 192 } 193 } 194 195 if (!foundIt) { 196 if (lookupMainExe) executableName = _CFBundleCopyExecutableName(bundle, url, infoDict); 197 if (executableName) { 198 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR) 199 Boolean doExecSearch = false; 200 #else 201 Boolean doExecSearch = true; 202 #endif 203 204 #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 205 if (lookupMainExe && bundle && bundle->_isFHSInstalledBundle) { 206 // For a FHS installed bundle, the URL points to share/Bundle.resources, and the binary is in: 207 208 CFURLRef sharePath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); 209 CFURLRef prefixPath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, sharePath); 210 CFRelease(sharePath); 211 212 CFStringRef directories[] = { _CFBundleFHSDirectoriesInExecutableSearchOrder }; 213 size_t directoriesCount = sizeof(directories) / sizeof(directories[0]); 214 215 for (size_t i = 0; i < directoriesCount; i++) { 216 CFURLRef where = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, prefixPath, directories[i], true); 217 executableURL = _CFBundleCopyExecutableURLRaw(where, executableName); 218 CFRelease(where); 219 220 if (executableURL) { 221 foundIt = true; 222 break; 223 } 224 } 225 226 CFRelease(prefixPath); 227 } 228 #endif // !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 229 230 // Now, look for the executable inside the bundle. 231 if (!foundIt && doExecSearch && 0 != version) { 232 CFURLRef exeDirURL = NULL; 233 234 #if !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 235 if (bundle && bundle->_isFHSInstalledBundle) { 236 CFURLRef withoutExtension = CFURLCreateCopyDeletingPathExtension(kCFAllocatorSystemDefault, url); 237 CFStringRef lastPathComponent = CFURLCopyLastPathComponent(withoutExtension); 238 239 CFURLRef libexec = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../" _CFBundleFHSDirectoryCLiteral_libexec), url); 240 241 CFStringRef exeDirName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), lastPathComponent, _CFBundleFHSExecutablesDirectorySuffix); 242 exeDirURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, libexec, exeDirName, true); 243 244 CFRelease(withoutExtension); 245 CFRelease(lastPathComponent); 246 CFRelease(libexec); 247 CFRelease(exeDirName); 248 } else 249 #endif // !DEPLOYMENT_RUNTIME_OBJC && !TARGET_OS_WIN32 && !TARGET_OS_ANDROID 250 if (1 == version) { 251 exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url); 252 } else if (2 == version) { 253 exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url); 254 } else { 255 #if TARGET_OS_WIN32 || !DEPLOYMENT_RUNTIME_OBJC 256 // On Windows and on targets that support FHS bundles, if the bundle URL is foo.resources, then the executable is at the same level as the .resources directory 257 CFStringRef extension = CFURLCopyPathExtension(url); 258 if (extension && CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension)) { 259 exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); 260 } else { 261 exeDirURL = (CFURLRef)CFRetain(url); 262 } 263 if (extension) CFRelease(extension); 264 #else 265 exeDirURL = (CFURLRef)CFRetain(url); 266 #endif 267 } 268 269 // Historical note: This used to search the directories "Mac OS X", "MacOSClassic", then "MacOS8". As of 10.13 we only look in "MacOS". 270 CFURLRef exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, _CFBundleGetPlatformExecutablesSubdirectoryName(), kCFURLPOSIXPathStyle, true, exeDirURL); 271 executableURL = _CFBundleCopyExecutableURLRaw(exeSubdirURL, executableName); 272 CFRelease(exeSubdirURL); 273 274 if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); 275 CFRelease(exeDirURL); 276 } 277 278 // If this was an old bundle, or we did not find the executable in the Executables subdirectory, look directly in the bundle wrapper. 279 if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(url, executableName); 280 281 #if TARGET_OS_WIN32 282 // Windows only: If we still haven't found the exe, look in the Executables folder. 283 // But only for the main bundle exe 284 if (lookupMainExe && !executableURL) { 285 CFURLRef exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../Executables"), url); 286 executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); 287 CFRelease(exeDirURL); 288 } 289 #endif 290 291 if (lookupMainExe && !ignoreCache && bundle && executableURL) { 292 // We found it. Cache the path. 293 CFURLRef absURL = CFURLCopyAbsoluteURL(executableURL); 294 executablePath = CFURLCopyFileSystemPath(absURL, PLATFORM_PATH_STYLE); 295 CFRelease(absURL); 296 __CFLock(&bundle->_lock); 297 bundle->_executablePath = (CFStringRef)CFRetain(executablePath); 298 __CFUnlock(&bundle->_lock); 299 CFRelease(executablePath); 300 } 301 if (lookupMainExe && bundle && !executableURL) bundle->_binaryType = __CFBundleNoBinary; 302 if (lookupMainExe) CFRelease(executableName); 303 } 304 } 305 if (!bundle && infoDict) CFRelease(infoDict); 306 return executableURL; 307 } 308 309 static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { 310 //!!! need to handle frameworks, NT; need to integrate with NSBundle - drd 311 UniChar buff[CFMaxPathSize]; 312 CFIndex buffLen; 313 CFURLRef url = NULL; 314 CFStringRef outstr; 315 316 #if TARGET_OS_ANDROID 317 const char *fixedUserHome = __CFgetenv("CFFIXED_USER_HOME"); 318 if (fixedUserHome) { 319 outstr = CFStringCreateWithCString(kCFAllocatorSystemDefault, fixedUserHome, kCFStringEncodingUTF8); 320 if (outstr) { 321 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true); 322 CFRelease(outstr); 323 if (url) { 324 return url; 325 } 326 } 327 } 328 #endif 329 330 buffLen = CFStringGetLength(str); 331 if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; 332 CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); 333 334 #if TARGET_OS_WIN32 335 // Is this a .dll or .exe? 336 if (buffLen >= 5 && (_wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".dll", 4) == 0 || _wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".exe", 4) == 0)) { 337 CFIndex extensionLength = CFStringGetLength(_CFBundleSiblingResourceDirectoryExtension); 338 buffLen -= 4; 339 // If this is an _debug, we should strip that before looking for the bundle 340 if (buffLen >= 7 && (_wcsnicmp((wchar_t *)&buff[buffLen-6], L"_debug", 6) == 0)) buffLen -= 6; 341 342 if (buffLen + 1 + extensionLength < CFMaxPathSize) { 343 buff[buffLen] = '.'; 344 buffLen ++; 345 CFStringGetCharacters(_CFBundleSiblingResourceDirectoryExtension, CFRangeMake(0, extensionLength), buff + buffLen); 346 buffLen += extensionLength; 347 outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull); 348 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true); 349 if (!_CFURLExists(url)) { 350 CFRelease(url); 351 url = NULL; 352 } 353 CFRelease(outstr); 354 } 355 } 356 #endif 357 358 if (!url) { 359 buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); // Remove exe name 360 361 if (buffLen > 0) { 362 // See if this is a new bundle. If it is, we have to remove more path components. 363 CFIndex startOfLastDir = _CFStartOfLastPathComponent(buff, buffLen); 364 if (startOfLastDir > 0 && startOfLastDir < buffLen) { 365 CFStringRef lastDirName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfLastDir]), buffLen - startOfLastDir); 366 367 if (CFEqual(lastDirName, _CFBundleGetPlatformExecutablesSubdirectoryName())) { 368 // This is a new bundle. Back off a few more levels 369 if (buffLen > 0) { 370 // Remove platform folder 371 buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); 372 } 373 if (buffLen > 0) { 374 // Remove executables folder (if present) 375 CFIndex startOfNextDir = _CFStartOfLastPathComponent(buff, buffLen); 376 if (startOfNextDir > 0 && startOfNextDir < buffLen) { 377 CFStringRef nextDirName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfNextDir]), buffLen - startOfNextDir); 378 if (CFEqual(nextDirName, _CFBundleExecutablesDirectoryName)) buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); 379 CFRelease(nextDirName); 380 } 381 } 382 if (buffLen > 0) { 383 // Remove support files folder 384 buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); 385 } 386 } 387 CFRelease(lastDirName); 388 } 389 } 390 391 if (buffLen > 0) { 392 outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull); 393 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true); 394 CFRelease(outstr); 395 } 396 } 397 return url; 398 } 399 400 static CFURLRef _CFBundleCopyResolvedURLForExecutableURL(CFURLRef url) { 401 // this is necessary so that we match any sanitization CFURL may perform on the result of _CFBundleCopyBundleURLForExecutableURL() 402 CFURLRef absoluteURL, url1, url2, outURL = NULL; 403 CFStringRef str, str1, str2; 404 absoluteURL = CFURLCopyAbsoluteURL(url); 405 str = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); 406 if (str) { 407 UniChar buff[CFMaxPathSize]; 408 CFIndex buffLen = CFStringGetLength(str), len1; 409 if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; 410 CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); 411 len1 = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); 412 if (len1 > 0 && len1 + 1 < buffLen) { 413 str1 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff, len1); 414 CFIndex skipSlashCount = 1; 415 #if TARGET_OS_WIN32 416 // On Windows, _CFLengthAfterDeletingLastPathComponent will return a value of 3 if the path is at the root (e.g. C:\). This includes the \, which is not the case for URLs with subdirectories 417 if (len1 == 3 && buff[1] == ':' && buff[2] == '\\') { 418 skipSlashCount = 0; 419 } 420 #endif 421 str2 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff + len1 + skipSlashCount, buffLen - len1 - skipSlashCount); 422 if (str1 && str2) { 423 url1 = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str1, PLATFORM_PATH_STYLE, true); 424 if (url1) { 425 url2 = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, str2, PLATFORM_PATH_STYLE, false, url1); 426 if (url2) { 427 outURL = CFURLCopyAbsoluteURL(url2); 428 CFRelease(url2); 429 } 430 CFRelease(url1); 431 } 432 } 433 if (str1) CFRelease(str1); 434 if (str2) CFRelease(str2); 435 } 436 CFRelease(str); 437 } 438 if (!outURL) { 439 outURL = absoluteURL; 440 } else { 441 CFRelease(absoluteURL); 442 } 443 return outURL; 444 } 445 446 // MARK: - Exported Functions 447 448 CF_EXPORT CFURLRef _CFBundleCopyExecutableURLInDirectory(CFURLRef url) { 449 return _CFBundleCopyExecutableURLInDirectory2(NULL, url, NULL, true); 450 } 451 452 CF_EXPORT CFURLRef _CFBundleCopyOtherExecutableURLInDirectory(CFURLRef url) { 453 // As of 10.13, there does not appear to be anyone actually invoking this function (either in the OS or other apps). The search list is also pretty far out of date. 454 // Therefore we will just return the same result as _CFBundleCopyExecutableURLInDirectory. 455 return _CFBundleCopyExecutableURLInDirectory(url); 456 } 457 458 CF_EXPORT CFURLRef CFBundleCopyExecutableURL(CFBundleRef bundle) { 459 return _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, NULL, false); 460 } 461 462 CF_EXPORT CFURLRef CFBundleCopyAuxiliaryExecutableURL(CFBundleRef bundle, CFStringRef executableName) { 463 return _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, executableName, true); 464 } 465 466 CF_EXPORT CFURLRef _CFBundleCopyBundleURLForExecutableURL(CFURLRef url) { 467 CFURLRef resolvedURL, outurl = NULL; 468 CFStringRef str; 469 resolvedURL = _CFBundleCopyResolvedURLForExecutableURL(url); 470 str = CFURLCopyFileSystemPath(resolvedURL, PLATFORM_PATH_STYLE); 471 if (str) { 472 outurl = _CFBundleCopyBundleURLForExecutablePath(str); 473 CFRelease(str); 474 } 475 CFRelease(resolvedURL); 476 return outurl; 477 } 478 479 CF_EXPORT CFBundleRef _CFBundleCreateWithExecutableURLIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url) { 480 CFBundleRef bundle = NULL; 481 CFURLRef bundleURL = _CFBundleCopyBundleURLForExecutableURL(url), resolvedURL = _CFBundleCopyResolvedURLForExecutableURL(url); 482 if (bundleURL && resolvedURL) { 483 // We used to call _CFBundleCreateIfLooksLikeBundle here, but switched to the regular CFBundleCreate because we want this to return a result for certain flat bundles as well. 484 // It is assumed that users of this SPI do not want this bundle to persist forever, so we use the Unique version of CFBundleCreate. 485 bundle = _CFBundleCreateUnique(allocator, bundleURL); 486 if (bundle) { 487 CFURLRef executableURL = _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, NULL, true); 488 char buff1[CFMaxPathSize], buff2[CFMaxPathSize]; 489 if (!executableURL || !CFURLGetFileSystemRepresentation(resolvedURL, true, (uint8_t *)buff1, CFMaxPathSize) || !CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff2, CFMaxPathSize) || 0 != strcmp(buff1, buff2)) { 490 CFRelease(bundle); 491 bundle = NULL; 492 } 493 if (executableURL) CFRelease(executableURL); 494 } 495 } 496 if (bundleURL) CFRelease(bundleURL); 497 if (resolvedURL) CFRelease(resolvedURL); 498 return bundle; 499 } 500 501 CF_EXPORT CFBundleRef _CFBundleCreateWithExecutableURLIfMightBeBundle(CFAllocatorRef allocator, CFURLRef url) { 502 CFBundleRef result = _CFBundleCreateWithExecutableURLIfLooksLikeBundle(allocator, url); 503 504 // This function applies additional requirements on a bundle to return a result 505 // The above makes sure that: 506 // 0. CFBundleCreate must succeed using a URL derived from the executable URL 507 // 1. The bundle must have an executableURL, and it must match the passed in executable URL 508 509 // This function additionally requires that 510 // 2. If flat, the bundle must have a non-empty Info.plist. (15663535) 511 if (result) { 512 uint8_t localVersion = _CFBundleEffectiveLayoutVersion(result); 513 if (3 == localVersion || 4 == localVersion) { 514 CFDictionaryRef infoPlist = CFBundleGetInfoDictionary(result); 515 if (!infoPlist || (infoPlist && CFDictionaryGetCount(infoPlist) == 0)) { 516 CFRelease(result); 517 result = NULL; 518 } 519 } 520 } 521 return result; 522 }