CFUtilities.c
1 /* CFUtilities.c 2 Copyright (c) 1998-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 #if __has_include(<xpc/xpc_transaction_deprecate.h>) 12 #import <xpc/xpc_transaction_deprecate.h> 13 #undef XPC_TRANSACTION_DEPRECATED 14 #define XPC_TRANSACTION_DEPRECATED 15 #endif 16 17 #include <CoreFoundation/CFBase.h> 18 #include <CoreFoundation/CFPriv.h> 19 #include "CFInternal.h" 20 #include "CFLocaleInternal.h" 21 #if !TARGET_OS_WASI 22 #include "CFBundle_Internal.h" 23 #endif 24 #include <CoreFoundation/CFPriv.h> 25 #if TARGET_OS_MAC || TARGET_OS_WIN32 26 #include <CoreFoundation/CFBundle.h> 27 #endif 28 #include <CoreFoundation/CFURLAccess.h> 29 #if !TARGET_OS_WASI 30 #include <CoreFoundation/CFPropertyList.h> 31 #endif 32 #if TARGET_OS_WIN32 33 #include <process.h> 34 #endif 35 #if TARGET_OS_ANDROID 36 #include <android/log.h> 37 #endif 38 #include <math.h> 39 #include <string.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #if TARGET_OS_MAC 43 #include <asl.h> 44 #else 45 #define ASL_LEVEL_EMERG 0 46 #define ASL_LEVEL_DEBUG 7 47 #endif 48 49 50 #if TARGET_OS_MAC 51 #include <unistd.h> 52 #include <sys/uio.h> 53 #include <mach/mach.h> 54 #include <mach-o/loader.h> 55 #include <mach-o/dyld.h> 56 #include <crt_externs.h> 57 #include <dlfcn.h> 58 #include <vproc.h> 59 #include <libproc.h> 60 #include <sys/sysctl.h> 61 #include <sys/stat.h> 62 #include <mach/mach.h> 63 #include <mach/mach_vm.h> 64 #include <sys/mman.h> 65 #include <stdio.h> 66 #include <sys/errno.h> 67 #include <mach/mach_time.h> 68 #include <Block.h> 69 #include <os/lock.h> 70 #endif 71 72 #if TARGET_OS_LINUX || TARGET_OS_BSD || TARGET_OS_WASI 73 #include <string.h> 74 #include <sys/mman.h> 75 #endif 76 77 #if __has_include(<unistd.h>) 78 #include <unistd.h> 79 #endif 80 #if _POSIX_THREADS 81 #include <pthread.h> 82 #endif 83 84 #if TARGET_OS_LINUX 85 #ifdef HAVE_SCHED_GETAFFINITY 86 #include <sched.h> 87 #endif 88 #endif 89 90 91 #if !defined(CF_HAVE_HW_CONFIG_COMMPAGE) && defined(_COMM_PAGE_LOGICAL_CPUS) && defined(_COMM_PAGE_PHYSICAL_CPUS) && defined(_COMM_PAGE_ACTIVE_CPUS) && !__has_feature(address_sanitizer) 92 #define CF_HAVE_HW_CONFIG_COMMPAGE 1 93 #else 94 #define CF_HAVE_HW_CONFIG_COMMPAGE 0 95 #endif 96 97 CF_PRIVATE os_log_t _CFOSLog(void) { 98 static os_log_t logger = NULL; 99 100 static dispatch_once_t onceToken; 101 dispatch_once(&onceToken, ^{ 102 logger = os_log_create("com.apple.foundation", "general"); 103 }); 104 return logger; 105 } 106 107 CF_PRIVATE os_log_t _CFMethodSignatureROMLog(void) { 108 static os_log_t logger; 109 static dispatch_once_t onceToken; 110 dispatch_once(&onceToken, ^{ 111 logger = os_log_create("com.apple.foundation", "MethodSignatureROM"); 112 }); 113 return logger; 114 } 115 116 CF_PRIVATE os_log_t _CFRuntimeIssuesLog(void) { 117 static os_log_t logger; 118 static dispatch_once_t onceToken; 119 dispatch_once(&onceToken, ^{ 120 logger = os_log_create(OS_LOG_SUBSYSTEM_RUNTIME_ISSUES, "CoreFoundation"); 121 }); 122 return logger; 123 } 124 125 CF_PRIVATE os_log_t _CFFoundationRuntimeIssuesLog(void) { 126 static os_log_t logger; 127 static dispatch_once_t onceToken; 128 dispatch_once(&onceToken, ^{ 129 logger = os_log_create(OS_LOG_SUBSYSTEM_RUNTIME_ISSUES, "Foundation"); 130 }); 131 return logger; 132 } 133 134 /* Comparator is passed the address of the values. */ 135 /* Binary searches a sorted-increasing array of some type. 136 Return value is either 1) the index of the element desired, 137 if the target value exists in the list, 2) greater than or 138 equal to count, if the element is greater than all the values 139 in the list, or 3) the index of the element greater than the 140 target value. 141 142 For example, a search in the list of integers: 143 2 3 5 7 11 13 17 144 145 For... Will Return... 146 2 0 147 5 2 148 23 7 149 1 0 150 9 4 151 152 For instance, if you just care about found/not found: 153 index = CFBSearch(list, count, elem); 154 if (count <= index || list[index] != elem) { 155 * Not found * 156 } else { 157 * Found * 158 } 159 160 */ 161 CF_PRIVATE CFIndex CFBSearch(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) { 162 const char *ptr = (const char *)list; 163 while (0 < count) { 164 CFIndex half = count / 2; 165 const char *probe = ptr + elementSize * half; 166 CFComparisonResult cr = comparator(element, probe, context); 167 if (0 == cr) return (probe - (const char *)list) / elementSize; 168 ptr = (cr < 0) ? ptr : probe + elementSize; 169 count = (cr < 0) ? half : (half + (count & 1) - 1); 170 } 171 return (ptr - (const char *)list) / elementSize; 172 } 173 174 175 #define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1; 176 177 CFHashCode CFHashBytes(uint8_t *bytes, CFIndex length) { 178 /* The ELF hash algorithm, used in the ELF object file format */ 179 UInt32 H = 0, T1, T2; 180 SInt32 rem = length; 181 while (3 < rem) { 182 ELF_STEP(bytes[length - rem]); 183 ELF_STEP(bytes[length - rem + 1]); 184 ELF_STEP(bytes[length - rem + 2]); 185 ELF_STEP(bytes[length - rem + 3]); 186 rem -= 4; 187 } 188 switch (rem) { 189 case 3: ELF_STEP(bytes[length - 3]); 190 case 2: ELF_STEP(bytes[length - 2]); 191 case 1: ELF_STEP(bytes[length - 1]); 192 case 0: ; 193 } 194 return H; 195 } 196 197 #undef ELF_STEP 198 199 200 #if TARGET_OS_MAC 201 CF_PRIVATE uintptr_t __CFFindPointer(uintptr_t ptr, uintptr_t start) { 202 vm_map_t task = mach_task_self(); 203 mach_vm_address_t address = start; 204 for (;;) { 205 mach_vm_size_t size = 0; 206 vm_region_basic_info_data_64_t info; 207 mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; 208 mach_port_t object_name; 209 kern_return_t ret = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &object_name); 210 if (KERN_SUCCESS != ret) break; 211 boolean_t scan = (info.protection & VM_PROT_WRITE) ? 1 : 0; 212 if (scan) { 213 uintptr_t *addr = (uintptr_t *)((uintptr_t)address); 214 uintptr_t *end = (uintptr_t *)((uintptr_t)address + (uintptr_t)size); 215 while (addr < end) { 216 if ((uintptr_t *)start <= addr && *addr == ptr) { 217 return (uintptr_t)addr; 218 } 219 addr++; 220 } 221 } 222 address += size; 223 } 224 return 0; 225 } 226 227 CF_PRIVATE void __CFDumpAllPointerLocations(uintptr_t ptr) { 228 uintptr_t addr = 0; 229 do { 230 addr = __CFFindPointer(ptr, sizeof(void *) + addr); 231 printf("%p\n", (void *)addr); 232 } while (addr != 0); 233 } 234 #endif 235 236 CF_PRIVATE CFDataRef _CFDataCreateFromURL(CFURLRef resourceURL, CFErrorRef *error) { 237 CFDataRef result = NULL; 238 #pragma GCC diagnostic push 239 #pragma GCC diagnostic ignored "-Wdeprecated" 240 SInt32 errorCode = 0; 241 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, resourceURL, &result, NULL, NULL, &errorCode)) { 242 if (error) { 243 // This error domain is not quite right, but it's better than Cocoa 244 *error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainOSStatus, errorCode, NULL); 245 return NULL; 246 } 247 } 248 #pragma GCC diagnostic pop 249 return result; 250 } 251 252 static CFStringRef copySystemVersionPath(CFStringRef suffix) { 253 #if TARGET_IPHONE_SIMULATOR 254 const char *simulatorRoot = getenv("IPHONE_SIMULATOR_ROOT"); 255 if (!simulatorRoot) simulatorRoot = getenv("CFFIXED_USER_HOME"); 256 if (!simulatorRoot) simulatorRoot = "/"; 257 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%s%@"), simulatorRoot, suffix); 258 #else 259 return CFStringCreateCopy(kCFAllocatorSystemDefault, suffix); 260 #endif 261 } 262 263 // Looks for localized version of "nonLocalized" in the SystemVersion bundle 264 // If not found, and returnNonLocalizedFlag == true, will return the non localized string (retained of course), otherwise NULL 265 // If bundlePtr != NULL, will use *bundlePtr and will return the bundle in there; otherwise bundle is created and released 266 #if TARGET_OS_MAC 267 static CFStringRef _CFCopyLocalizedVersionKey(CFBundleRef *bundlePtr, CFStringRef nonLocalized) { 268 CFStringRef localized = NULL; 269 CFBundleRef locBundle = bundlePtr ? *bundlePtr : NULL; 270 if (!locBundle) { 271 CFStringRef path = copySystemVersionPath(CFSTR("/System/Library/CoreServices/SystemVersion.bundle")); 272 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false); 273 CFRelease(path); 274 if (url) { 275 locBundle = CFBundleCreate(kCFAllocatorSystemDefault, url); 276 CFRelease(url); 277 } 278 } 279 if (locBundle) { 280 localized = CFBundleCopyLocalizedString(locBundle, nonLocalized, nonLocalized, CFSTR("SystemVersion")); 281 if (bundlePtr) *bundlePtr = locBundle; else CFRelease(locBundle); 282 } 283 return localized ? localized : (CFStringRef)CFRetain(nonLocalized); 284 } 285 #endif 286 287 #if TARGET_OS_WIN32 288 static BOOL _CFGetWindowsSystemVersion(OSVERSIONINFOEX *osvi) { 289 ZeroMemory(osvi, sizeof(OSVERSIONINFOEX)); 290 osvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 291 return GetVersionEx((OSVERSIONINFO *)osvi); 292 } 293 #endif 294 295 static CFDictionaryRef _CFCopyVersionDictionary(CFStringRef path) { 296 CFPropertyListRef plist = NULL; 297 298 #if TARGET_OS_MAC 299 CFDataRef data; 300 CFURLRef url; 301 302 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false); 303 #pragma GCC diagnostic push 304 #pragma GCC diagnostic ignored "-Wdeprecated" 305 if (url && CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, url, &data, NULL, NULL, NULL)) { 306 #pragma GCC diagnostic pop 307 plist = CFPropertyListCreateWithData(kCFAllocatorSystemDefault, data, kCFPropertyListMutableContainers, NULL, NULL); 308 CFRelease(data); 309 } 310 if (url) CFRelease(url); 311 312 if (plist) { 313 CFBundleRef locBundle = NULL; 314 CFStringRef fullVersion, vers, versExtra, build; 315 CFStringRef versionString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionProductVersionStringKey); 316 CFStringRef buildString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionBuildStringKey); 317 CFStringRef fullVersionString = _CFCopyLocalizedVersionKey(&locBundle, CFSTR("FullVersionString")); 318 if (locBundle) CFRelease(locBundle); 319 320 // Now build the full version string 321 if (CFEqual(fullVersionString, CFSTR("FullVersionString"))) { 322 CFRelease(fullVersionString); 323 fullVersionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %%@ (%@ %%@)"), versionString, buildString); 324 } 325 vers = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionKey); 326 versExtra = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionExtraKey); 327 if (vers && versExtra) vers = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %@"), vers, versExtra); 328 build = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionBuildVersionKey); 329 fullVersion = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, fullVersionString, (vers ? vers : CFSTR("?")), build ? build : CFSTR("?")); 330 if (vers && versExtra) CFRelease(vers); 331 332 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionStringKey, versionString); 333 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildStringKey, buildString); 334 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR("FullVersionString"), fullVersion); 335 CFRelease(versionString); 336 CFRelease(buildString); 337 CFRelease(fullVersionString); 338 CFRelease(fullVersion); 339 } 340 #elif TARGET_OS_WIN32 341 OSVERSIONINFOEX osvi; 342 if (!_CFGetWindowsSystemVersion(&osvi)) return NULL; 343 344 plist = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 10, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 345 346 // e.g. 10.7 347 CFStringRef versionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%ld.%ld(%ld,%ld)"), osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor); 348 349 // e.g. 11A508 350 CFStringRef buildString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%ld"), osvi.dwBuildNumber); 351 352 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionKey, versionString); 353 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildVersionKey, buildString); 354 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductNameKey, CFSTR("Windows")); // hard coded for now 355 356 CFRelease(versionString); 357 CFRelease(buildString); 358 #endif 359 return (CFDictionaryRef)plist; 360 } 361 362 #if !TARGET_OS_WASI 363 CFStringRef _CFCopySystemVersionDictionaryValue(CFStringRef key) { 364 CFStringRef versionString; 365 CFDictionaryRef dict = _CFCopyServerVersionDictionary(); 366 if (!dict) dict = _CFCopySystemVersionDictionary(); 367 if (!dict) return NULL; 368 versionString = (CFStringRef)CFDictionaryGetValue(dict, key); 369 if (versionString) CFRetain(versionString); 370 CFRelease(dict); 371 return versionString; 372 } 373 374 CFStringRef CFCopySystemVersionString(void) { 375 return _CFCopySystemVersionDictionaryValue(CFSTR("FullVersionString")); 376 } 377 378 CFDictionaryRef _CFCopySystemVersionDictionary(void) { 379 static dispatch_once_t onceToken; 380 static CFDictionaryRef result = NULL; 381 dispatch_once(&onceToken, ^{ 382 // TODO: Populate this dictionary differently on non-Darwin platforms 383 CFStringRef path = copySystemVersionPath(CFSTR("/System/Library/CoreServices/SystemVersion.plist")); 384 CFPropertyListRef plist = _CFCopyVersionDictionary(path); 385 CFRelease(path); 386 result = (CFDictionaryRef)plist; 387 }); 388 if (result) { 389 return CFRetain(result); 390 } else { 391 return NULL; 392 } 393 } 394 395 CFDictionaryRef _CFCopySystemVersionPlatformDictionary(void) { 396 static dispatch_once_t onceToken; 397 static CFDictionaryRef result = NULL; 398 dispatch_once(&onceToken, ^{ 399 CFStringRef path = copySystemVersionPath(CFSTR("/System/Library/CoreServices/.SystemVersionPlatform.plist")); 400 CFPropertyListRef plist = _CFCopyVersionDictionary(path); 401 CFRelease(path); 402 if (!plist) { 403 // Fall back to the normal one in case the .SystemVersionPlatform.plist symlink is missing 404 plist = _CFCopySystemVersionDictionary(); 405 } 406 result = (CFDictionaryRef)plist; 407 }); 408 if (result) { 409 return CFRetain(result); 410 } else { 411 return NULL; 412 } 413 } 414 415 CFDictionaryRef _CFCopyServerVersionDictionary(void) { 416 static dispatch_once_t onceToken; 417 static CFDictionaryRef result = NULL; 418 dispatch_once(&onceToken, ^{ 419 CFStringRef path = copySystemVersionPath(CFSTR("/System/Library/CoreServices/ServerVersion.plist")); 420 CFPropertyListRef plist = _CFCopyVersionDictionary(path); 421 CFRelease(path); 422 result = (CFDictionaryRef)plist; 423 }); 424 if (result) { 425 return CFRetain(result); 426 } else { 427 return NULL; 428 } 429 } 430 431 static CFOperatingSystemVersion _CFCalculateOSVersion(void) { 432 CFOperatingSystemVersion versionStruct = {-1, 0, 0}; 433 #if TARGET_OS_WIN32 434 OSVERSIONINFOEX windowsVersion; 435 if (_CFGetWindowsSystemVersion(&windowsVersion)) { 436 versionStruct.majorVersion = windowsVersion.dwMajorVersion; 437 versionStruct.minorVersion = windowsVersion.dwMinorVersion; 438 versionStruct.patchVersion = windowsVersion.dwBuildNumber; 439 } 440 #else 441 CFStringRef resolvedProductVersionKey = _kCFSystemVersionProductVersionKey; 442 CFStringRef productVersion = _CFCopySystemVersionDictionaryValue(resolvedProductVersionKey); 443 if (productVersion) { 444 CFArrayRef components = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, productVersion, CFSTR(".")); 445 if (components) { 446 const CFIndex cnt = CFArrayGetCount(components); 447 if (0 < cnt) { 448 versionStruct.majorVersion = CFStringGetIntValue(CFArrayGetValueAtIndex(components, 0)); 449 if (1 < cnt) { 450 versionStruct.minorVersion = CFStringGetIntValue(CFArrayGetValueAtIndex(components, 1)); 451 if (2 < cnt) { 452 versionStruct.patchVersion = CFStringGetIntValue(CFArrayGetValueAtIndex(components, 2)); 453 } 454 } 455 } 456 CFRelease(components); 457 } 458 CFRelease(productVersion); 459 } 460 #endif 461 return versionStruct; 462 } 463 464 CFOperatingSystemVersion _CFOperatingSystemVersionGetCurrent(void) { 465 static CFOperatingSystemVersion version; 466 static dispatch_once_t onceToken; 467 dispatch_once(&onceToken, ^{ 468 version = _CFCalculateOSVersion(); 469 }); 470 return version; 471 } 472 473 Boolean _CFOperatingSystemVersionIsAtLeastVersion(CFOperatingSystemVersion version) { 474 const CFOperatingSystemVersion ourVersion = _CFOperatingSystemVersionGetCurrent(); 475 if (ourVersion.majorVersion < version.majorVersion) return false; 476 if (ourVersion.majorVersion > version.majorVersion) return true; 477 if (ourVersion.minorVersion < version.minorVersion) return false; 478 if (ourVersion.minorVersion > version.minorVersion) return true; 479 if (ourVersion.patchVersion < version.patchVersion) return false; 480 if (ourVersion.patchVersion > version.patchVersion) return true; 481 return true; 482 } 483 484 CONST_STRING_DECL(_kCFSystemVersionProductNameKey, "ProductName") 485 CONST_STRING_DECL(_kCFSystemVersionProductCopyrightKey, "ProductCopyright") 486 CONST_STRING_DECL(_kCFSystemVersionProductVersionKey, "ProductVersion") 487 CONST_STRING_DECL(_kCFSystemVersionProductVersionExtraKey, "ProductVersionExtra") 488 CONST_STRING_DECL(_kCFSystemVersionProductUserVisibleVersionKey, "ProductUserVisibleVersion") 489 CONST_STRING_DECL(_kCFSystemVersionBuildVersionKey, "ProductBuildVersion") 490 CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey, "Version") 491 CONST_STRING_DECL(_kCFSystemVersionBuildStringKey, "Build") 492 #endif 493 494 CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { 495 return true; 496 } 497 498 #if TARGET_OS_OSX 499 CF_PRIVATE void *__CFLookupCarbonCoreFunction(const char *name) { 500 static void *image = NULL; 501 static dispatch_once_t onceToken; 502 dispatch_once(&onceToken, ^{ 503 image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY | RTLD_LOCAL); 504 }); 505 void *dyfunc = NULL; 506 if (image) { 507 dyfunc = dlsym(image, name); 508 } 509 return dyfunc; 510 } 511 #endif 512 513 #if TARGET_OS_MAC 514 CF_PRIVATE void *__CFLookupCoreServicesInternalFunction(const char *name) { 515 static void *image = NULL; 516 static dispatch_once_t onceToken; 517 dispatch_once(&onceToken, ^{ 518 image = dlopen("/System/Library/PrivateFrameworks/CoreServicesInternal.framework/CoreServicesInternal", RTLD_LAZY | RTLD_LOCAL); 519 }); 520 void *dyfunc = NULL; 521 if (image) { 522 dyfunc = dlsym(image, name); 523 } 524 return dyfunc; 525 } 526 527 CF_PRIVATE void *__CFLookupCFNetworkFunction(const char *name) { 528 static void *image = NULL; 529 static dispatch_once_t onceToken; 530 dispatch_once(&onceToken, ^{ 531 const char *path = __CFgetenvIfNotRestricted("CFNETWORK_LIBRARY_PATH"); 532 if (!path) { 533 path = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork"; 534 } 535 image = dlopen(path, RTLD_LAZY | RTLD_LOCAL); 536 }); 537 void *dyfunc = NULL; 538 if (image) { 539 dyfunc = dlsym(image, name); 540 } 541 return dyfunc; 542 } 543 #endif 544 545 // TSAN does not know about the commpage: [39153032] 546 __attribute__((no_sanitize_thread)) 547 CF_PRIVATE CFIndex __CFActiveProcessorCount(void) { 548 #if CF_HAVE_HW_CONFIG_COMMPAGE 549 static const uintptr_t p = _COMM_PAGE_ACTIVE_CPUS; 550 return (CFIndex)(*(uint8_t*)p); 551 #else 552 553 int32_t pcnt; 554 #if TARGET_OS_WIN32 555 SYSTEM_INFO sysInfo; 556 GetSystemInfo(&sysInfo); 557 DWORD_PTR activeProcessorMask = sysInfo.dwActiveProcessorMask; 558 // assumes sizeof(DWORD_PTR) is 64 bits or less 559 uint64_t v = activeProcessorMask; 560 v = v - ((v >> 1) & 0x5555555555555555ULL); 561 v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL); 562 v = (v + (v >> 4)) & 0xf0f0f0f0f0f0f0fULL; 563 pcnt = (v * 0x0101010101010101ULL) >> ((sizeof(v) - 1) * 8); 564 #elif TARGET_OS_MAC 565 int32_t mib[] = {CTL_HW, HW_AVAILCPU}; 566 size_t len = sizeof(pcnt); 567 int32_t result = sysctl(mib, sizeof(mib) / sizeof(int32_t), &pcnt, &len, NULL, 0); 568 if (result != 0) { 569 pcnt = 0; 570 } 571 #elif TARGET_OS_LINUX || TARGET_OS_BSD 572 573 #ifdef HAVE_SCHED_GETAFFINITY 574 cpu_set_t set; 575 if (sched_getaffinity (getpid(), sizeof(set), &set) == 0) { 576 return CPU_COUNT (&set); 577 } 578 #endif 579 580 pcnt = sysconf(_SC_NPROCESSORS_ONLN); 581 #else 582 // Assume the worst 583 pcnt = 1; 584 #endif 585 return pcnt; 586 #endif 587 } 588 589 CF_PRIVATE CFIndex __CFProcessorCount() { 590 int32_t pcnt; 591 #if TARGET_OS_MAC 592 int32_t mib[] = {CTL_HW, HW_NCPU}; 593 size_t len = sizeof(pcnt); 594 int32_t result = sysctl(mib, sizeof(mib) / sizeof(int32_t), &pcnt, &len, NULL, 0); 595 if (result != 0) { 596 pcnt = 0; 597 } 598 #elif TARGET_OS_LINUX || TARGET_OS_BSD 599 pcnt = sysconf(_SC_NPROCESSORS_CONF); 600 #else 601 // Assume the worst 602 pcnt = 1; 603 #endif 604 return pcnt; 605 } 606 607 CF_PRIVATE uint64_t __CFMemorySize() { 608 uint64_t memsize = 0; 609 #if TARGET_OS_MAC 610 int32_t mib[] = {CTL_HW, HW_MEMSIZE}; 611 size_t len = sizeof(memsize); 612 int32_t result = sysctl(mib, sizeof(mib) / sizeof(int32_t), &memsize, &len, NULL, 0); 613 if (result != 0) { 614 memsize = 0; 615 } 616 #elif TARGET_OS_LINUX || TARGET_OS_BSD 617 memsize = sysconf(_SC_PHYS_PAGES); 618 memsize *= sysconf(_SC_PAGESIZE); 619 #endif 620 return memsize; 621 } 622 623 #if TARGET_OS_MAC 624 static uid_t _CFGetSVUID(BOOL *successful) { 625 uid_t uid = -1; 626 struct kinfo_proc kinfo; 627 u_int miblen = 4; 628 size_t len; 629 int mib[miblen]; 630 int ret; 631 mib[0] = CTL_KERN; 632 mib[1] = KERN_PROC; 633 mib[2] = KERN_PROC_PID; 634 mib[3] = getpid(); 635 len = sizeof(struct kinfo_proc); 636 ret = sysctl(mib, miblen, &kinfo, &len, NULL, 0); 637 if (ret != 0) { 638 uid = -1; 639 *successful = NO; 640 } else { 641 uid = kinfo.kp_eproc.e_pcred.p_svuid; 642 *successful = YES; 643 } 644 return uid; 645 } 646 #endif 647 648 CF_INLINE BOOL _CFCanChangeEUIDs(void) { 649 #if TARGET_OS_MAC 650 static BOOL canChangeEUIDs; 651 static dispatch_once_t onceToken; 652 dispatch_once(&onceToken, ^{ 653 uid_t euid = geteuid(); 654 uid_t uid = getuid(); 655 BOOL gotSVUID = NO; 656 uid_t svuid = _CFGetSVUID(&gotSVUID); 657 canChangeEUIDs = (uid == 0 || uid != euid || svuid != euid || !gotSVUID); 658 }); 659 return canChangeEUIDs; 660 #else 661 return true; 662 #endif 663 } 664 665 #if !TARGET_OS_WASI 666 typedef struct _ugids { 667 uid_t _euid; 668 uid_t _egid; 669 } ugids; 670 671 // work around 33477678 by making these file-level globals 672 static ugids __CFGetUGIDs_cachedUGIDs = { -1, -1 }; 673 static dispatch_once_t __CFGetUGIDs_onceToken; 674 675 CF_PRIVATE void __CFGetUGIDs(uid_t *euid, gid_t *egid) { 676 ugids(^lookup)(void) = ^{ 677 ugids ids; 678 #if 1 && TARGET_OS_MAC 679 if (0 != pthread_getugid_np(&ids._euid, &ids._egid)) 680 #endif 681 { 682 ids._euid = geteuid(); 683 ids._egid = getegid(); 684 } 685 return ids; 686 }; 687 688 ugids ids; 689 690 if (_CFCanChangeEUIDs()) { 691 ids = lookup(); 692 } else { 693 dispatch_once(&__CFGetUGIDs_onceToken, ^{ 694 __CFGetUGIDs_cachedUGIDs = lookup(); 695 }); 696 ids = __CFGetUGIDs_cachedUGIDs; 697 } 698 699 if (euid) *euid = ids._euid; 700 if (egid) *egid = ids._egid; 701 } 702 703 CF_EXPORT void _CFGetUGIDs(uid_t *euid, gid_t *egid) { 704 __CFGetUGIDs(euid, egid); 705 } 706 707 CF_EXPORT uid_t _CFGetEUID(void) { 708 uid_t euid; 709 __CFGetUGIDs(&euid, NULL); 710 return euid; 711 } 712 713 CF_EXPORT uid_t _CFGetEGID(void) { 714 uid_t egid; 715 __CFGetUGIDs(NULL, &egid); 716 return egid; 717 } 718 #endif 719 720 const char *_CFPrintForDebugger(const void *obj) { 721 static char *result = NULL; 722 CFStringRef str; 723 CFIndex cnt = 0; 724 725 free(result); // Let go of result from previous call. 726 result = NULL; 727 if (obj) { 728 if (CFGetTypeID(obj) == CFStringGetTypeID()) { 729 // Makes Ali marginally happier 730 str = __CFCopyFormattingDescription(obj, NULL); 731 if (!str) str = CFCopyDescription(obj); 732 } else { 733 str = CFCopyDescription(obj); 734 } 735 } else { 736 str = (CFStringRef)CFRetain(CFSTR("(null)")); 737 } 738 739 if (str != NULL) { 740 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, NULL, 0, &cnt); 741 } 742 result = (char *) malloc(cnt + 2); // 1 for '\0', 1 for an optional '\n' 743 if (str != NULL) { 744 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, (UInt8 *) result, cnt, &cnt); 745 } 746 result[cnt] = '\0'; 747 748 if (str) CFRelease(str); 749 return result; 750 } 751 752 static void _CFShowToFile(FILE *file, Boolean flush, const void *obj) { 753 CFStringRef str; 754 CFIndex idx, cnt; 755 CFStringInlineBuffer buffer; 756 bool lastNL = false; 757 758 if (obj) { 759 if (CFGetTypeID(obj) == CFStringGetTypeID()) { 760 // Makes Ali marginally happier 761 str = __CFCopyFormattingDescription(obj, NULL); 762 if (!str) str = CFCopyDescription(obj); 763 } else { 764 str = CFCopyDescription(obj); 765 } 766 } else { 767 str = (CFStringRef)CFRetain(CFSTR("(null)")); 768 } 769 cnt = CFStringGetLength(str); 770 771 #if TARGET_OS_WIN32 772 UniChar *ptr = (UniChar *)CFStringGetCharactersPtr(str); 773 BOOL freePtr = false; 774 if (!ptr) { 775 CFIndex strLen = CFStringGetLength(str); 776 // +2, 1 for newline, 1 for null 777 CFIndex bufSize = sizeof(UniChar *) * (CFStringGetMaximumSizeForEncoding(strLen, kCFStringEncodingUnicode) + 2); 778 CFIndex bytesUsed = 0; 779 ptr = (UniChar *)malloc(bufSize); 780 CFStringGetCharacters(str, CFRangeMake(0, strLen), ptr); 781 ptr[strLen] = L'\n'; 782 ptr[strLen+1] = 0; 783 freePtr = true; 784 } 785 OutputDebugStringW((wchar_t *)ptr); 786 if (freePtr) free(ptr); 787 #else 788 CFStringInitInlineBuffer(str, &buffer, CFRangeMake(0, cnt)); 789 for (idx = 0; idx < cnt; idx++) { 790 UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buffer, idx); 791 if (ch < 128) { 792 fprintf_l(file, NULL, "%c", ch); 793 lastNL = (ch == '\n'); 794 } else { 795 fprintf_l(file, NULL, "\\u%04x", ch); 796 } 797 } 798 if (!lastNL) { 799 #if TARGET_OS_MAC 800 fprintf_l(file, NULL, "\n"); 801 #else 802 fprintf(file, "\n"); 803 #endif 804 if (flush) fflush(file); 805 } 806 #endif 807 808 if (str) CFRelease(str); 809 } 810 811 void CFShow(const void *obj) { 812 _CFShowToFile(stderr, true, obj); 813 } 814 815 816 // message must be a UTF8-encoded, null-terminated, byte buffer with at least length bytes 817 typedef void (*CFLogFunc)(int32_t lev, const char *message, size_t length, char withBanner); 818 819 #if TARGET_OS_MAC 820 static Boolean debugger_attached() { 821 BOOL debuggerAttached = NO; 822 struct proc_bsdshortinfo info; 823 const int size = proc_pidinfo(getpid(), PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof(info)); 824 if (size == PROC_PIDT_SHORTBSDINFO_SIZE) { 825 debuggerAttached = (info.pbsi_flags & PROC_FLAG_TRACED); 826 } 827 return debuggerAttached; 828 } 829 #endif 830 831 // We need to support both the 'modern' os_log focused approach to logging and the previous implementation. This enumeration gives us a way to distinguish between the two more verbosely than a boolean. 832 typedef enum { 833 _cf_logging_style_os_log, 834 _cf_logging_style_legacy 835 } _cf_logging_style; 836 837 static bool also_do_stderr(const _cf_logging_style style) { 838 bool result = false; 839 #if TARGET_OS_LINUX || TARGET_OS_WIN32 840 // just log to stderr, other logging facilities are out 841 result = true; 842 #elif TARGET_OS_MAC 843 if (style == _cf_logging_style_os_log) { 844 // 845 // This might seem a bit odd, so an explanation is in order: 846 // 847 // If a debugger is attached we defer to os_log to do all logging for us. This 848 // happens many frames up/sideways from here. 849 // 850 // Otherwise, we check for the force flag as we always have. 851 // 852 // That failing, we'll stat STDERR and see if its sxomething we like. 853 // 854 // NOTE: As nice as it would be to dispatch_once this, all of these things 855 // can and do change at runtime under certain circumstances, so we need to 856 // keep it dynamic. 857 // 858 if (debugger_attached() || __CFgetenv("OS_ACTIVITY_DT_MODE")) { 859 return false; 860 } 861 862 if (!__CFProcessIsRestricted() && __CFgetenv("CFLOG_FORCE_STDERR")) { 863 return true; 864 } 865 866 struct stat sb; 867 int ret = fstat(STDERR_FILENO, &sb); 868 if (ret < 0) { 869 return false; 870 } 871 872 const mode_t m = sb.st_mode & S_IFMT; 873 if (S_IFCHR == m || S_IFREG == m || S_IFIFO == m || S_IFSOCK == m) { 874 return true; 875 } 876 // NOTE: we return false now, as the expectation is that os_log path owns all logging 877 result = false; 878 } else if (style == _cf_logging_style_legacy) { 879 // compatibility path 880 if (!issetugid() && __CFgetenv("CFLOG_FORCE_STDERR")) { 881 return true; 882 } 883 struct stat sb; 884 int ret = fstat(STDERR_FILENO, &sb); 885 if (ret < 0) return false; 886 mode_t m = sb.st_mode & S_IFMT; 887 if (S_IFREG == m || S_IFSOCK == m) return true; 888 if (!(S_IFIFO == m || S_IFCHR == m)) return false; // disallow any whacky stuff 889 result = true; 890 } 891 #else 892 // just log to stderr, other logging facilities are out 893 result = true; 894 #endif 895 return result; 896 } 897 898 #if TARGET_OS_WIN32 899 static struct tm *localtime_r(time_t *tv, struct tm *result) { 900 struct tm *tm = localtime(tv); 901 if (tm) { 902 *result = *tm; 903 } 904 return tm; 905 } 906 #endif 907 908 static void _populateBanner(char **banner, char **time, char **thread, int *bannerLen) { 909 double dummy; 910 CFAbsoluteTime at = CFAbsoluteTimeGetCurrent(); 911 time_t tv = (time_t)floor(at + kCFAbsoluteTimeIntervalSince1970); 912 struct tm mine; 913 localtime_r(&tv, &mine); 914 int32_t year = mine.tm_year + 1900; 915 int32_t month = mine.tm_mon + 1; 916 int32_t day = mine.tm_mday; 917 int32_t hour = mine.tm_hour; 918 int32_t minute = mine.tm_min; 919 int32_t second = mine.tm_sec; 920 int32_t ms = (int32_t)floor(1000.0 * modf(at, &dummy)); 921 #if TARGET_OS_MAC 922 uint64_t tid = 0; 923 if (0 != pthread_threadid_np(NULL, &tid)) tid = pthread_mach_thread_np(pthread_self()); 924 asprintf(banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%llu] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), tid); 925 asprintf(thread, "%x", pthread_mach_thread_np(pthread_self())); 926 #elif TARGET_OS_WIN32 927 bannerLen = asprintf(banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%lx] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), GetCurrentThreadId()); 928 asprintf(thread, "%lx", GetCurrentThreadId()); 929 #elif TARGET_OS_WASI 930 bannerLen = asprintf(banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d [%x] ", year, month, day, hour, minute, second, ms, (unsigned int)pthread_self()); 931 asprintf(thread, "%lx", pthread_self()); 932 #else 933 bannerLen = asprintf(banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), (unsigned int)pthread_self()); 934 asprintf(thread, "%lx", pthread_self()); 935 #endif 936 asprintf(time, "%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, ms); 937 } 938 939 static void _logToStderr(char *banner, const char *message, size_t length) { 940 #if TARGET_OS_MAC 941 struct iovec v[3]; 942 v[0].iov_base = banner; 943 v[0].iov_len = banner ? strlen(banner) : 0; 944 v[1].iov_base = (char *)message; 945 v[1].iov_len = length; 946 v[2].iov_base = "\n"; 947 v[2].iov_len = (message[length - 1] != '\n') ? 1 : 0; 948 int nv = (v[0].iov_base ? 1 : 0) + 1 + (v[2].iov_len ? 1 : 0); 949 static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; 950 os_unfair_lock_lock(&lock); 951 writev(STDERR_FILENO, v[0].iov_base ? v : v + 1, nv); 952 os_unfair_lock_unlock(&lock); 953 #elif TARGET_OS_WIN32 954 size_t bannerLen = strlen(banner); 955 size_t bufLen = bannerLen + length + 1; 956 char *buf = (char *)malloc(sizeof(char) * bufLen); 957 if (banner) { 958 // Copy the banner into the debug string 959 memmove_s(buf, bufLen, banner, bannerLen); 960 961 // Copy the message into the debug string 962 strcpy_s(buf + bannerLen, bufLen - bannerLen, message); 963 } else { 964 strcpy_s(buf, bufLen, message); 965 } 966 buf[bufLen - 1] = '\0'; 967 fprintf_s(stderr, "%s\n", buf); 968 // This Win32 API call only prints when a debugger is active 969 // OutputDebugStringA(buf); 970 free(buf); 971 #else 972 size_t bannerLen = strlen(banner); 973 size_t bufLen = bannerLen + length + 1; 974 char *buf = (char *)malloc(sizeof(char) * bufLen); 975 if (banner) { 976 // Copy the banner into the debug string 977 memmove(buf, banner, bannerLen); 978 979 // Copy the message into the debug string 980 strncpy(buf + bannerLen, message, bufLen - bannerLen); 981 } else { 982 strncpy(buf, message, bufLen); 983 } 984 buf[bufLen - 1] = '\0'; 985 fprintf(stderr, "%s\n", buf); 986 free(buf); 987 #endif 988 } 989 990 static void __CFLogCString(int32_t lev, const char *message, size_t length, char withBanner) { 991 char *banner = NULL; 992 char *time = NULL; 993 char *thread = NULL; 994 int bannerLen; 995 bannerLen = 0; 996 997 // The banner path may use CF functions, but the rest of this function should not. It may be called at times when CF is not fully setup or torn down. 998 if (withBanner) { 999 _populateBanner(&banner, &time, &thread, &bannerLen); 1000 } 1001 1002 _logToStderr(banner, message, length); 1003 1004 if (thread) free(thread); 1005 if (time) free(time); 1006 if (banner) free(banner); 1007 } 1008 1009 static void __CFLogCStringLegacy(int32_t lev, const char *message, size_t length, char withBanner) { 1010 char *banner = NULL; 1011 char *time = NULL; 1012 char *uid = NULL; 1013 char *thread = NULL; 1014 int bannerLen; 1015 bannerLen = 0; 1016 1017 // The banner path may use CF functions, but the rest of this function should not. It may be called at times when CF is not fully setup or torn down. 1018 if (withBanner) { 1019 _populateBanner(&banner, &time, &thread, &bannerLen); 1020 } 1021 1022 #if TARGET_OS_MAC 1023 #pragma GCC diagnostic push 1024 #pragma GCC diagnostic ignored "-Wdeprecated" 1025 uid_t euid; 1026 __CFGetUGIDs(&euid, NULL); 1027 asprintf(&uid, "%d", euid); 1028 aslclient asl = asl_open(NULL, (__CFBundleMainID && __CFBundleMainID[0]) ? __CFBundleMainID : "com.apple.console", ASL_OPT_NO_DELAY); 1029 aslmsg msg = asl_new(ASL_TYPE_MSG); 1030 asl_set(msg, "CFLog Local Time", time); // not to be documented, not public API 1031 asl_set(msg, "CFLog Thread", thread); // not to be documented, not public API 1032 asl_set(msg, "ReadUID", uid); 1033 static const char * const levstr[] = {"0", "1", "2", "3", "4", "5", "6", "7"}; 1034 asl_set(msg, ASL_KEY_LEVEL, levstr[lev]); 1035 asl_set(msg, ASL_KEY_MSG, message); 1036 asl_send(asl, msg); 1037 asl_free(msg); 1038 asl_close(asl); 1039 #pragma GCC diagnostic pop 1040 #endif // DEPLOYMENT_TARGET 1041 1042 if (also_do_stderr(_cf_logging_style_legacy)) { 1043 _logToStderr(banner, message, length); 1044 } 1045 1046 if (thread) free(thread); 1047 if (time) free(time); 1048 if (banner) free(banner); 1049 if (uid) free(uid); 1050 } 1051 1052 1053 static void _CFLogvEx2Predicate(CFLogFunc logit, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, int32_t lev, CFStringRef format, va_list args, _cf_logging_style loggingStyle) { 1054 #if TARGET_OS_MAC 1055 uintptr_t val = (uintptr_t)_CFGetTSD(__CFTSDKeyIsInCFLog); 1056 if (3 < val) return; // allow up to 4 nested invocations 1057 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)(val + 1), NULL); 1058 #endif 1059 CFStringRef str = format ? _CFStringCreateWithFormatAndArgumentsAux2(kCFAllocatorSystemDefault, copyDescFunc, contextDescFunc, formatOptions, (CFStringRef)format, args) : 0; 1060 CFIndex blen = str ? CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1 : 0; 1061 char *buf = str ? (char *)malloc(blen) : 0; 1062 if (str && buf) { 1063 Boolean converted = CFStringGetCString(str, buf, blen, kCFStringEncodingUTF8); 1064 size_t len = strlen(buf); 1065 // silently ignore 0-length or really large messages, and levels outside the valid range 1066 if (converted && !(len <= 0 || (1 << 24) < len) && !(lev < ASL_LEVEL_EMERG || ASL_LEVEL_DEBUG < lev)) { 1067 if (logit) { 1068 logit(lev, buf, len, 1); 1069 } 1070 else if (loggingStyle == _cf_logging_style_os_log) { 1071 __CFLogCString(lev, buf, len, 1); 1072 } 1073 else if (loggingStyle == _cf_logging_style_legacy) { 1074 __CFLogCStringLegacy(lev, buf, len, 1); 1075 } 1076 } 1077 } 1078 if (buf) free(buf); 1079 if (str) CFRelease(str); 1080 #if TARGET_OS_MAC 1081 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)val, NULL); 1082 #endif 1083 } 1084 1085 CF_EXPORT void _CFLogvEx2(CFLogFunc logit, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, int32_t lev, CFStringRef format, va_list args) { 1086 _CFLogvEx2Predicate(logit, copyDescFunc, contextDescFunc, formatOptions, lev, format, args, _cf_logging_style_legacy); 1087 } 1088 1089 CF_EXPORT void _CFLogvEx3(CFLogFunc logit, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, int32_t lev, CFStringRef format, va_list args, void *addr) { 1090 _CFLogvEx2Predicate(logit, copyDescFunc, contextDescFunc, formatOptions, lev, format, args, _cf_logging_style_legacy); 1091 1092 } 1093 1094 CF_EXPORT void _CFLogvEx(CFLogFunc logit, CFStringRef (*copyDescFunc)(void *, const void *), CFDictionaryRef formatOptions, int32_t lev, CFStringRef format, va_list args) { 1095 _CFLogvEx2(logit, copyDescFunc, NULL, formatOptions, lev, format, args); 1096 } 1097 1098 // This CF-only log function uses no CF functionality, so it may be called anywhere within CF - including thread teardown or prior to full CF setup 1099 CF_PRIVATE void _CFLogSimple(int32_t lev, char *format, ...) { 1100 va_list args; 1101 va_start(args, format); 1102 char formattedMessage[1024]; 1103 int length = vsnprintf(formattedMessage, 1024, format, args); 1104 if (length > 0) { 1105 __CFLogCStringLegacy(lev, formattedMessage, length, 0); 1106 } 1107 va_end(args); 1108 } 1109 1110 void CFLog(int32_t lev, CFStringRef format, ...) { 1111 va_list args; 1112 va_start(args, format); 1113 1114 #if !TARGET_OS_WASI 1115 _CFLogvEx3(NULL, NULL, NULL, NULL, lev, format, args, __builtin_return_address(0)); 1116 #else 1117 _CFLogvEx3(NULL, NULL, NULL, NULL, lev, format, args, NULL); 1118 #endif 1119 va_end(args); 1120 } 1121 1122 #if DEPLOYMENT_RUNTIME_SWIFT 1123 // Temporary as Swift cannot import varag C functions 1124 void CFLog1(CFLogLevel lev, CFStringRef message) { 1125 #if TARGET_OS_ANDROID 1126 android_LogPriority priority = ANDROID_LOG_UNKNOWN; 1127 switch (lev) { 1128 case kCFLogLevelEmergency: priority = ANDROID_LOG_FATAL; break; 1129 case kCFLogLevelAlert: priority = ANDROID_LOG_ERROR; break; 1130 case kCFLogLevelCritical: priority = ANDROID_LOG_ERROR; break; 1131 case kCFLogLevelError: priority = ANDROID_LOG_ERROR; break; 1132 case kCFLogLevelWarning: priority = ANDROID_LOG_WARN; break; 1133 case kCFLogLevelNotice: priority = ANDROID_LOG_WARN; break; 1134 case kCFLogLevelInfo: priority = ANDROID_LOG_INFO; break; 1135 case kCFLogLevelDebug: priority = ANDROID_LOG_DEBUG; break; 1136 } 1137 1138 if (message == NULL) message = CFSTR("NULL"); 1139 1140 char stack_buffer[1024] = { 0 }; 1141 char *buffer = &stack_buffer[0]; 1142 CFStringEncoding encoding = kCFStringEncodingUTF8; 1143 CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(message), encoding) + 1; 1144 const char *tag = "Swift"; // process name not available from NDK 1145 1146 if (maxLength > sizeof(stack_buffer) / sizeof(stack_buffer[0])) { 1147 buffer = calloc(sizeof(char), maxLength); 1148 if (!buffer) { 1149 maxLength = sizeof(stack_buffer) / sizeof(stack_buffer[0]); 1150 buffer = &stack_buffer[0]; 1151 buffer[maxLength-1] = '\000'; 1152 } 1153 } 1154 1155 if (maxLength == 1) { 1156 // was crashing with zero length strings 1157 // https://bugs.swift.org/browse/SR-2666 1158 strcpy(buffer, " "); // log empty string 1159 } 1160 else 1161 CFStringGetCString(message, buffer, maxLength, encoding); 1162 1163 __android_log_print(priority, tag, "%s", buffer); 1164 fprintf(stderr, "%s\n", buffer); 1165 1166 if (buffer != &stack_buffer[0]) free(buffer); 1167 #else 1168 CFLog(lev, CFSTR("%@"), message); 1169 #endif 1170 } 1171 #endif 1172 1173 1174 1175 #if TARGET_OS_MAC 1176 1177 kern_return_t _CFDiscorporateMemoryAllocate(CFDiscorporateMemory *hm, size_t size, bool purgeable) { 1178 kern_return_t ret = KERN_SUCCESS; 1179 size = round_page(size); 1180 if (0 == size) size = vm_page_size; 1181 memset(hm, 0, sizeof(CFDiscorporateMemory)); 1182 1183 ret = mach_vm_allocate(mach_task_self(), &hm->map_address, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_FOUNDATION) | (purgeable ? VM_FLAGS_PURGABLE : 0)); 1184 if (KERN_SUCCESS != ret) { 1185 hm->map_address = 0; 1186 return ret; 1187 } 1188 1189 hm->address = hm->map_address; 1190 hm->size = (mach_vm_size_t)size; 1191 hm->purgeable = purgeable; 1192 return ret; 1193 } 1194 1195 kern_return_t _CFDiscorporateMemoryDeallocate(CFDiscorporateMemory *hm) { 1196 kern_return_t ret = KERN_SUCCESS; 1197 if (hm->map_address != 0) ret = mach_vm_deallocate(mach_task_self(), hm->map_address, hm->size); 1198 if (KERN_SUCCESS != ret) 1199 CFLog(kCFLogLevelError, CFSTR("CFDiscorporateMemoryDeallocate: mach_vm_deallocate returned %s"), mach_error_string(ret)); 1200 hm->address = 0; 1201 hm->size = 0; 1202 hm->map_address = 0; 1203 hm->purgeable = false; 1204 return ret; 1205 } 1206 1207 kern_return_t _CFDiscorporateMemoryDematerialize(CFDiscorporateMemory *hm) { 1208 kern_return_t ret = KERN_SUCCESS; 1209 if (hm->address == 0) ret = KERN_INVALID_MEMORY_CONTROL; 1210 int state = VM_PURGABLE_VOLATILE; 1211 if (KERN_SUCCESS == ret) vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state); 1212 if (KERN_SUCCESS == ret) hm->address = 0; 1213 return ret; 1214 } 1215 1216 kern_return_t _CFDiscorporateMemoryMaterialize(CFDiscorporateMemory *hm) { 1217 kern_return_t ret = KERN_SUCCESS; 1218 if (hm->address != 0) ret = KERN_INVALID_MEMORY_CONTROL; 1219 int state = VM_PURGABLE_NONVOLATILE; 1220 if (KERN_SUCCESS == ret) ret = vm_purgable_control(mach_task_self(), (vm_address_t)hm->map_address, VM_PURGABLE_SET_STATE, &state); 1221 if (KERN_SUCCESS == ret) hm->address = hm->map_address; 1222 if (KERN_SUCCESS == ret) if (VM_PURGABLE_EMPTY == state) ret = KERN_PROTECTION_FAILURE; // same as VM_PURGABLE_EMPTY 1223 return ret; 1224 } 1225 1226 #endif 1227 1228 #if TARGET_OS_OSX 1229 static os_unfair_lock __CFProcessKillingLock = OS_UNFAIR_LOCK_INIT; 1230 static CFIndex __CFProcessKillingDisablingCount = 1; 1231 static Boolean __CFProcessKillingWasTurnedOn = false; 1232 #endif 1233 1234 void _CFSuddenTerminationDisable(void) { 1235 #if TARGET_OS_OSX 1236 os_unfair_lock_lock(&__CFProcessKillingLock); 1237 __CFProcessKillingDisablingCount++; 1238 os_unfair_lock_unlock(&__CFProcessKillingLock); 1239 #else 1240 // Do nothing 1241 #endif 1242 } 1243 1244 void _CFSuddenTerminationEnable(void) { 1245 #if TARGET_OS_OSX 1246 // In our model the first call of _CFSuddenTerminationEnable() that does not balance a previous call of _CFSuddenTerminationDisable() actually enables sudden termination so we have to keep a count that's almost redundant with vproc's. 1247 os_unfair_lock_lock(&__CFProcessKillingLock); 1248 __CFProcessKillingDisablingCount--; 1249 if (__CFProcessKillingDisablingCount==0 && !__CFProcessKillingWasTurnedOn) { 1250 __CFProcessKillingWasTurnedOn = true; 1251 } else { 1252 } 1253 os_unfair_lock_unlock(&__CFProcessKillingLock); 1254 #else 1255 // Do nothing 1256 #endif 1257 } 1258 1259 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) { 1260 #if TARGET_OS_OSX 1261 // This is for when the caller wants to try to exit quickly if possible but not automatically exit the process when it next becomes clean, because quitting might still be cancelled by the user. 1262 os_unfair_lock_lock(&__CFProcessKillingLock); 1263 os_unfair_lock_unlock(&__CFProcessKillingLock); 1264 #else 1265 // Elsewhere, sudden termination is always disabled 1266 #endif 1267 } 1268 1269 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) { 1270 #if TARGET_OS_OSX 1271 // The user has had their final opportunity to cancel quitting. Exit as soon as the process is clean. Same carefulness as in _CFSuddenTerminationExitIfTerminationEnabled(). 1272 os_unfair_lock_lock(&__CFProcessKillingLock); 1273 if (__CFProcessKillingWasTurnedOn) { 1274 } 1275 os_unfair_lock_unlock(&__CFProcessKillingLock); 1276 #else 1277 // Elsewhere, sudden termination is always disabled 1278 #endif 1279 } 1280 1281 size_t _CFSuddenTerminationDisablingCount(void) { 1282 #if TARGET_OS_OSX 1283 return (__CFProcessKillingWasTurnedOn ? 0 : 1); 1284 #else 1285 // Elsewhere, sudden termination is always disabled 1286 return 1; 1287 #endif 1288 } 1289 1290 #pragma mark File Reading 1291 1292 #include <sys/stat.h> 1293 #include <fcntl.h> 1294 #include <errno.h> 1295 #if TARGET_OS_WIN32 1296 #include <io.h> 1297 #include <direct.h> 1298 #define close _close 1299 #define write _write 1300 #define read _read 1301 #define open _NS_open 1302 #define stat _NS_stat 1303 #define fstat _fstat 1304 #define statinfo _stat 1305 1306 #define mach_task_self() 0 1307 1308 #else 1309 #define statinfo stat 1310 #endif 1311 1312 static CFErrorRef _CFErrorWithFilePathCodeDomain(CFStringRef domain, CFIndex code, CFStringRef path) { 1313 CFStringRef key = CFSTR("NSFilePath"); 1314 CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorSystemDefault, (const void **)&key, (const void **)&path, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1315 CFErrorRef result = CFErrorCreate(kCFAllocatorSystemDefault, domain, code, userInfo); 1316 CFRelease(userInfo); 1317 return result; 1318 } 1319 1320 // Caller is responsible for freeing memory. munmap() if map == true, else malloc(). 1321 CF_PRIVATE Boolean _CFReadMappedFromFile(CFStringRef path, Boolean map, Boolean uncached, void **outBytes, CFIndex *outLength, CFErrorRef *errorPtr) { 1322 void *bytes = NULL; 1323 unsigned long length; 1324 char cpath[CFMaxPathSize]; 1325 if (!CFStringGetFileSystemRepresentation(path, cpath, CFMaxPathSize)) { 1326 // TODO: real error codes 1327 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainCocoa, -1, path); 1328 return false; 1329 } 1330 1331 struct statinfo statBuf; 1332 int32_t fd = -1; 1333 1334 fd = open(cpath, O_RDONLY|CF_OPENFLGS, 0666); 1335 if (fd < 0) { 1336 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, errno, path); 1337 return false; 1338 } 1339 #if TARGET_OS_MAC 1340 if (uncached) (void)fcntl(fd, F_NOCACHE, 1); // Non-zero arg turns off caching; we ignore error as uncached is just a hint 1341 #endif 1342 if (fstat(fd, &statBuf) < 0) { 1343 int32_t savederrno = errno; 1344 close(fd); 1345 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1346 return false; 1347 } 1348 if ((statBuf.st_mode & S_IFMT) != S_IFREG) { 1349 close(fd); 1350 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, EACCES, path); 1351 return false; 1352 } 1353 if (statBuf.st_size < 0LL) { // too small 1354 close(fd); 1355 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, ENOMEM, path); 1356 return false; 1357 } 1358 #if TARGET_RT_64_BIT 1359 #else 1360 if (statBuf.st_size > (1ull << 31)) { // refuse to do more than 2GB 1361 close(fd); 1362 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, EFBIG, path); 1363 return false; 1364 } 1365 #endif 1366 1367 if (0LL == statBuf.st_size) { 1368 bytes = malloc(8); // don't return constant string -- it's freed! 1369 length = 0; 1370 #if TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD || TARGET_OS_WASI 1371 } else if (map) { 1372 if((void *)-1 == (bytes = mmap(0, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0))) { 1373 int32_t savederrno = errno; 1374 close(fd); 1375 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1376 return false; 1377 } 1378 length = (unsigned long)statBuf.st_size; 1379 } else { 1380 bytes = malloc(statBuf.st_size); 1381 if (bytes == NULL) { 1382 close(fd); 1383 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, ENOMEM, path); 1384 return false; 1385 } 1386 size_t numBytesRemaining = (size_t)statBuf.st_size; 1387 void *readLocation = bytes; 1388 while (numBytesRemaining > 0) { 1389 size_t numBytesRequested = (numBytesRemaining < (1LL << 31)) ? numBytesRemaining : ((1LL << 31) - 1); // This loop is basically a workaround for 4870206 1390 ssize_t numBytesRead = read(fd, readLocation, numBytesRequested); 1391 if (numBytesRead <= 0) { 1392 if (numBytesRead < 0) { 1393 int32_t savederrno = errno; 1394 free(bytes); 1395 close(fd); 1396 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1397 bytes = NULL; 1398 return false; 1399 } else { 1400 // This is a bizarre case; 0 bytes read. Might indicate end-of-file? 1401 break; 1402 } 1403 } else { 1404 readLocation += numBytesRead; 1405 numBytesRemaining -= numBytesRead; 1406 } 1407 } 1408 length = (unsigned long)statBuf.st_size - numBytesRemaining; 1409 } 1410 #elif TARGET_OS_WIN32 1411 } else { 1412 bytes = malloc(statBuf.st_size); 1413 DWORD numBytesRead; 1414 if (!ReadFile((HANDLE)_get_osfhandle(fd), bytes, statBuf.st_size, &numBytesRead, NULL)) { 1415 DWORD lastError = GetLastError(); 1416 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, lastError, path); 1417 free(bytes); 1418 close(fd); 1419 errno = lastError; 1420 bytes = NULL; 1421 return false; 1422 } 1423 length = numBytesRead; 1424 } 1425 #endif 1426 close(fd); 1427 *outBytes = bytes; 1428 *outLength = length; 1429 return true; 1430 } 1431 1432 /* __CFStringGetCharacterFromInlineBufferQuickReverse is the same as __CFStringGetCharacterFromInlineBufferQuick(), but expects idx to be decremented 1433 */ 1434 CF_INLINE UniChar __CFStringGetCharacterFromInlineBufferQuickReverse(CFStringInlineBuffer *buf, CFIndex idx) { 1435 if (buf->directUniCharBuffer) return buf->directUniCharBuffer[idx + buf->rangeToBuffer.location]; 1436 if (buf->directCStringBuffer) return (UniChar)(buf->directCStringBuffer[idx + buf->rangeToBuffer.location]); 1437 if (idx >= buf->bufferedRangeEnd || idx < buf->bufferedRangeStart) { 1438 if ((buf->bufferedRangeStart = idx - __kCFStringInlineBufferLength + 1) < 0) buf->bufferedRangeStart = 0; 1439 buf->bufferedRangeEnd = buf->bufferedRangeStart + __kCFStringInlineBufferLength; 1440 if (buf->bufferedRangeEnd > buf->rangeToBuffer.length) buf->bufferedRangeEnd = buf->rangeToBuffer.length; 1441 CFStringGetCharacters(buf->theString, CFRangeMake(buf->rangeToBuffer.location + buf->bufferedRangeStart, buf->bufferedRangeEnd - buf->bufferedRangeStart), buf->buffer); 1442 } 1443 return buf->buffer[idx - buf->bufferedRangeStart]; 1444 } 1445 1446 /* UniCharInitInlineBuffer initializes an in-line buffer to use an array of UniChar with CFStringGetCharacterFromInlineBuffer (and related SPI that use CFStringInlineBuffer). 1447 */ 1448 CF_INLINE void UniCharInitInlineBuffer(const UniChar *uchars, CFIndex ucharsLength, CFStringInlineBuffer *buf) { 1449 buf->theString = NULL; 1450 buf->rangeToBuffer = CFRangeMake(0, ucharsLength); 1451 buf->directUniCharBuffer = uchars; 1452 buf->directCStringBuffer = NULL; 1453 buf->bufferedRangeStart = buf->bufferedRangeEnd = 0; 1454 } 1455 1456 // __IsInvalidExtensionCharacter returns true if C is a space, a path separator, or a unicode directional control character 1457 CF_INLINE Boolean __IsInvalidExtensionCharacter(UniChar c) 1458 { 1459 // Unicode directional control characters 1460 enum { 1461 UNICHAR_ARABIC_LETTER_MARK = 0x061c, 1462 UNICHAR_LEFT_TO_RIGHT_MARK = 0x200e, 1463 UNICHAR_RIGHT_TO_LEFT_MARK = 0x200f, 1464 UNICHAR_LEFT_TO_RIGHT_EMBEDDING = 0x202a, 1465 UNICHAR_RIGHT_TO_LEFT_EMBEDDING = 0x202b, 1466 UNICHAR_POP_DIRECTIONAL_FORMATTING = 0x202c, 1467 UNICHAR_LEFT_TO_RIGHT_OVERRIDE = 0x202d, 1468 UNICHAR_RIGHT_TO_LEFT_OVERRIDE = 0x202e, 1469 UNICHAR_LEFT_TO_RIGHT_ISOLATE = 0x2066, 1470 UNICHAR_RIGHT_TO_LEFT_ISOLATE = 0x2067, 1471 UNICHAR_FIRST_STRONG_ISOLATE = 0x2068, 1472 UNICHAR_POP_DIRECTIONAL_ISOLATE = 0x2069, 1473 }; 1474 1475 Boolean result; 1476 switch ( c ) { 1477 case (UniChar)' ': 1478 case (UniChar)'/': 1479 case UNICHAR_ARABIC_LETTER_MARK: 1480 case UNICHAR_LEFT_TO_RIGHT_MARK: 1481 case UNICHAR_RIGHT_TO_LEFT_MARK: 1482 case UNICHAR_LEFT_TO_RIGHT_EMBEDDING: 1483 case UNICHAR_RIGHT_TO_LEFT_EMBEDDING: 1484 case UNICHAR_POP_DIRECTIONAL_FORMATTING: 1485 case UNICHAR_LEFT_TO_RIGHT_OVERRIDE: 1486 case UNICHAR_RIGHT_TO_LEFT_OVERRIDE: 1487 case UNICHAR_LEFT_TO_RIGHT_ISOLATE: 1488 case UNICHAR_RIGHT_TO_LEFT_ISOLATE: 1489 case UNICHAR_FIRST_STRONG_ISOLATE: 1490 case UNICHAR_POP_DIRECTIONAL_ISOLATE: 1491 result = true; 1492 break; 1493 1494 default: 1495 result = false; 1496 break; 1497 } 1498 return ( result ); 1499 } 1500 1501 /* __GetPathExtensionRangesFromPathComponentBuffer finds the range of the primary path extension (if any) for the given path component. If requested, __GetPathExtensionRangesFromPathComponentBuffer also finds the secondary path extension (if any) for the given path component. 1502 */ 1503 CF_INLINE void __GetPathExtensionRangesFromPathComponentBuffer(CFStringInlineBuffer *buffer, CFRange *outPrimaryExtRange, CFRange *outSecondaryExtRange) { 1504 CFRange primaryExtRange = CFRangeMake(kCFNotFound, 0); 1505 CFRange secondaryExtRange = CFRangeMake(kCFNotFound, 0); 1506 1507 if ( buffer && (outPrimaryExtRange || outSecondaryExtRange) ) { 1508 // 1509 // Look backwards for a period. The period may not be the first or last character of the path component, 1510 // but otherwise the extension may be of any length and contain any characters 1511 // except space, a path separator, or any unicode directional control character. 1512 // 1513 // The secondary extension is the "apparent" extension the user would see if 1514 // the primary extension is hidden. Since this is a security concern, the 1515 // rules for the secondary extension are looser. Leading and trailing spaces 1516 // are ignored, since the user might think "foo.txt .app" is a text file if 1517 // ".app" is hidden. 1518 1519 CFIndex nameLen = buffer->rangeToBuffer.length; 1520 1521 for ( CFIndex i = nameLen - 1; i > 0; i-- ) { 1522 UniChar c = __CFStringGetCharacterFromInlineBufferQuickReverse(buffer, i); 1523 if ( (UniChar)'.' == c ) { 1524 // Found a dot 1525 CFIndex e2i = 0; 1526 if ( i == (nameLen - 1) ) { 1527 // Last char disqualified 1528 break; 1529 } 1530 1531 primaryExtRange = CFRangeMake(i + 1, nameLen - (i + 1)); 1532 1533 if ( outSecondaryExtRange == NULL ) { 1534 break; 1535 } 1536 1537 // Look for an "apparent" secondary extension, using modified rules. 1538 CFIndex e2len = 0; 1539 1540 // Skip trailing space (one-liner): 1541 for ( --i; (i > 0) && (__CFStringGetCharacterFromInlineBufferQuickReverse(buffer, i) == (UniChar)' '); i-- ); 1542 1543 // Scan apparent extension text 1544 for ( ; i > 0; i-- ) { 1545 c = __CFStringGetCharacterFromInlineBufferQuickReverse(buffer, i); 1546 if ( ((UniChar)'.' == c) || ((UniChar)' ' == c) ) { 1547 break; 1548 } 1549 e2len++; 1550 } 1551 1552 e2i = i + 1; 1553 1554 // Skip leading space (one-liner): 1555 for ( ; (i > 0) && (__CFStringGetCharacterFromInlineBufferQuickReverse(buffer, i) == (UniChar)' '); i-- ); 1556 1557 if ( (i > 0) && (__CFStringGetCharacterFromInlineBufferQuickReverse(buffer, i) == (UniChar)'.') && (e2len > 0) ) { 1558 secondaryExtRange = CFRangeMake( e2i, e2len ); 1559 } 1560 1561 break; 1562 } 1563 else if ( __IsInvalidExtensionCharacter(c) ) { 1564 // Found a space, a path separator, or a unicode directional control character -- terminate the loop 1565 break; 1566 } 1567 } 1568 } 1569 1570 if ( outPrimaryExtRange ) { 1571 *outPrimaryExtRange = primaryExtRange; 1572 } 1573 1574 if ( outSecondaryExtRange ) { 1575 *outSecondaryExtRange = secondaryExtRange; 1576 } 1577 } 1578 1579 /* __ExtensionIsValidToAppend determines if the characters in extension are valid to be appended as an extension. 1580 */ 1581 CF_INLINE Boolean __ExtensionIsValidToAppend(CFStringInlineBuffer *buffer) { 1582 Boolean result; 1583 1584 if ( buffer && buffer->rangeToBuffer.length ) { 1585 // 1586 // Look backwards for a period. If a period is found, it must not be the 1587 // last character and any characters after the period must be valid in an 1588 // extension, and all characters before the period are considered valid 1589 // except for the path separator character '/' (this allow strings like 1590 // "tar.gz" to be appended as an extension). The path separator 1591 // character '/' isn't allowed anywhere in the extension. If there's no 1592 // period, the entire string must be characters valid in an extension. 1593 // The extension may be of any length. 1594 1595 CFIndex nameLen = buffer->rangeToBuffer.length; 1596 if ( nameLen ) { 1597 result = true; // assume the best 1598 1599 for ( CFIndex i = nameLen - 1; i >= 0; i-- ) { 1600 UniChar c = __CFStringGetCharacterFromInlineBufferQuickReverse(buffer, i); 1601 if ( (UniChar)'.' == c ) { 1602 // Found a period. If it's the last character, then return false; otherwise true 1603 result = i < nameLen - 1; 1604 if ( result ) { 1605 // check the rest of the string before the period for '/' 1606 for ( CFIndex j = i - 1; j >= 0; j-- ) { 1607 c = __CFStringGetCharacterFromInlineBufferQuickReverse(buffer, j); 1608 if ( c == (UniChar)'/'){ 1609 result = false; 1610 break; 1611 } 1612 } 1613 } 1614 break; 1615 } 1616 else if ( __IsInvalidExtensionCharacter(c) ) { 1617 // Found an invalid extension character. Return false. 1618 result = false; 1619 break; 1620 } 1621 } 1622 } 1623 else { 1624 // NOTE: The implementation before we switched to _CFGetPathExtensionRangesFromPathComponent 1625 // allowed an empty string to be appended as an extension. That is not a valid extension. 1626 result = false; 1627 } 1628 } 1629 else { 1630 // no buffer or no extension 1631 result = false; 1632 } 1633 return ( result ); 1634 } 1635 1636 /* _CFGetExtensionInfoFromPathComponent/_CFGetPathExtensionRangesFromPathComponentUniChars finds the range of the primary path extension (if any) for the given path component. If requested, _CFGetExtensionInfoFromPathComponent/_CFGetPathExtensionRangesFromPathComponentUniChars also finds the secondary path extension (if any) for the given path component. 1637 */ 1638 CF_EXPORT void _CFGetPathExtensionRangesFromPathComponentUniChars(const UniChar *uchars, CFIndex ucharsLength, CFRange *outPrimaryExtRange, CFRange *outSecondaryExtRange) { 1639 CFStringInlineBuffer buffer; 1640 UniCharInitInlineBuffer(uchars, ucharsLength, &buffer); 1641 __GetPathExtensionRangesFromPathComponentBuffer(&buffer, outPrimaryExtRange, outSecondaryExtRange); 1642 } 1643 1644 CF_EXPORT void _CFGetPathExtensionRangesFromPathComponent(CFStringRef inName, CFRange *outPrimaryExtRange, CFRange *outSecondaryExtRange) { 1645 CFStringInlineBuffer buffer; 1646 CFStringInitInlineBuffer(inName, &buffer, CFRangeMake(0, CFStringGetLength(inName))); 1647 __GetPathExtensionRangesFromPathComponentBuffer(&buffer, outPrimaryExtRange, outSecondaryExtRange); 1648 } 1649 1650 /* _CFPathExtensionIsValid/_CFExtensionUniCharsIsValidToAppend determines if the characters in extension are valid to be appended as an extension. 1651 */ 1652 CF_EXPORT Boolean _CFExtensionUniCharsIsValidToAppend(const UniChar *uchars, CFIndex ucharsLength) { 1653 CFStringInlineBuffer buffer; 1654 UniCharInitInlineBuffer(uchars, ucharsLength, &buffer); 1655 return ( __ExtensionIsValidToAppend(&buffer) ); 1656 } 1657 1658 CF_EXPORT Boolean _CFExtensionIsValidToAppend(CFStringRef extension) { 1659 CFStringInlineBuffer buffer; 1660 CFStringInitInlineBuffer(extension, &buffer, CFRangeMake(0, CFStringGetLength(extension))); 1661 return ( __ExtensionIsValidToAppend(&buffer) ); 1662 } 1663 1664 1665 #if DEPLOYMENT_RUNTIME_SWIFT && !TARGET_OS_WASI 1666 1667 CFDictionaryRef __CFGetEnvironment() { 1668 static dispatch_once_t once = 0L; 1669 static CFMutableDictionaryRef envDict = NULL; 1670 dispatch_once(&once, ^{ 1671 #if TARGET_OS_MAC 1672 extern char ***_NSGetEnviron(void); 1673 char **envp = *_NSGetEnviron(); 1674 #elif TARGET_OS_BSD || TARGET_OS_CYGWIN || TARGET_OS_WASI 1675 extern char **environ; 1676 char **envp = environ; 1677 #elif TARGET_OS_LINUX 1678 #if !defined(environ) && !TARGET_OS_ANDROID 1679 #define environ __environ 1680 #endif 1681 char **envp = environ; 1682 #elif TARGET_OS_WIN32 1683 char **envp = _environ; 1684 #endif 1685 envDict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1686 for (; *envp; ++envp) { 1687 char *eqp = *envp; // '=' pointer 1688 while (*eqp && *eqp != '=') ++eqp; 1689 1690 char *endp = eqp; 1691 while (*endp) ++endp; 1692 1693 if (endp == eqp) { // oops, badly formed value 1694 continue; 1695 } 1696 CFStringRef key = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)*envp, (eqp - *envp), kCFStringEncodingUTF8, false); 1697 if (key == NULL) { 1698 key = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)*envp, (eqp - *envp), CFStringGetSystemEncoding(), false); 1699 } 1700 CFStringRef value = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)(eqp + 1), (endp - (eqp + 1)), kCFStringEncodingUTF8, false); 1701 if (key == NULL) { 1702 key = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)(eqp + 1), (endp - (eqp + 1)), CFStringGetSystemEncoding(), false); 1703 } 1704 if (NULL == key || NULL == value) { 1705 if (key != NULL) CFRelease(key); 1706 if (value != NULL) CFRelease(value); 1707 continue; 1708 } 1709 CFStringRef existingValue = CFDictionaryGetValue(envDict, key); 1710 if (existingValue == NULL) { 1711 CFDictionarySetValue(envDict, key, value); 1712 } else if (CFEqual(existingValue, value)){ 1713 CFLog(kCFLogLevelWarning, CFSTR("Warning: duplicate definition for key '%@' found in environment -- subsequent definitions are ignored. The first definition was '%@', the ignored definition is '%@'."), key, existingValue, value); 1714 } 1715 CFRelease(key); 1716 CFRelease(value); 1717 } 1718 }); 1719 return envDict; 1720 } 1721 1722 int32_t __CFGetPid() { 1723 return getpid(); 1724 } 1725 1726 #endif