/ CFUtilities.c
CFUtilities.c
1 /* 2 * Copyright (c) 2015 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 /* CFUtilities.c 25 Copyright (c) 1998-2014, Apple Inc. All rights reserved. 26 Responsibility: Tony Parker 27 */ 28 29 #include <CoreFoundation/CFPriv.h> 30 #include "CFInternal.h" 31 #include "CFLocaleInternal.h" 32 #include <CoreFoundation/CFPriv.h> 33 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 34 #include <CoreFoundation/CFBundle.h> 35 #endif 36 #include <CoreFoundation/CFURLAccess.h> 37 #include <CoreFoundation/CFPropertyList.h> 38 #if DEPLOYMENT_TARGET_WINDOWS 39 #include <process.h> 40 #endif 41 #include <math.h> 42 #include <string.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 46 #include <asl.h> 47 #else 48 #define ASL_LEVEL_EMERG 0 49 #define ASL_LEVEL_DEBUG 7 50 #endif 51 52 53 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 54 #include <unistd.h> 55 #include <sys/uio.h> 56 #include <mach/mach.h> 57 #include <pthread.h> 58 #include <mach-o/loader.h> 59 #include <mach-o/dyld.h> 60 #include <crt_externs.h> 61 #include <dlfcn.h> 62 #include <vproc.h> 63 #include <vproc_priv.h> 64 #include <sys/sysctl.h> 65 #include <sys/stat.h> 66 #include <mach/mach.h> 67 #include <mach/mach_vm.h> 68 #include <sys/mman.h> 69 #include <stdio.h> 70 #include <sys/errno.h> 71 #include <mach/mach_time.h> 72 #include <Block.h> 73 #endif 74 #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD 75 #include <string.h> 76 #include <pthread.h> 77 #include <sys/mman.h> 78 #endif 79 80 /* Comparator is passed the address of the values. */ 81 /* Binary searches a sorted-increasing array of some type. 82 Return value is either 1) the index of the element desired, 83 if the target value exists in the list, 2) greater than or 84 equal to count, if the element is greater than all the values 85 in the list, or 3) the index of the element greater than the 86 target value. 87 88 For example, a search in the list of integers: 89 2 3 5 7 11 13 17 90 91 For... Will Return... 92 2 0 93 5 2 94 23 7 95 1 0 96 9 4 97 98 For instance, if you just care about found/not found: 99 index = CFBSearch(list, count, elem); 100 if (count <= index || list[index] != elem) { 101 * Not found * 102 } else { 103 * Found * 104 } 105 106 */ 107 CF_PRIVATE CFIndex CFBSearch(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) { 108 const char *ptr = (const char *)list; 109 while (0 < count) { 110 CFIndex half = count / 2; 111 const char *probe = ptr + elementSize * half; 112 CFComparisonResult cr = comparator(element, probe, context); 113 if (0 == cr) return (probe - (const char *)list) / elementSize; 114 ptr = (cr < 0) ? ptr : probe + elementSize; 115 count = (cr < 0) ? half : (half + (count & 1) - 1); 116 } 117 return (ptr - (const char *)list) / elementSize; 118 } 119 120 121 #define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1; 122 123 CFHashCode CFHashBytes(uint8_t *bytes, CFIndex length) { 124 /* The ELF hash algorithm, used in the ELF object file format */ 125 UInt32 H = 0, T1, T2; 126 SInt32 rem = length; 127 while (3 < rem) { 128 ELF_STEP(bytes[length - rem]); 129 ELF_STEP(bytes[length - rem + 1]); 130 ELF_STEP(bytes[length - rem + 2]); 131 ELF_STEP(bytes[length - rem + 3]); 132 rem -= 4; 133 } 134 switch (rem) { 135 case 3: ELF_STEP(bytes[length - 3]); 136 case 2: ELF_STEP(bytes[length - 2]); 137 case 1: ELF_STEP(bytes[length - 1]); 138 case 0: ; 139 } 140 return H; 141 } 142 143 #undef ELF_STEP 144 145 146 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 147 CF_PRIVATE uintptr_t __CFFindPointer(uintptr_t ptr, uintptr_t start) { 148 vm_map_t task = mach_task_self(); 149 mach_vm_address_t address = start; 150 for (;;) { 151 mach_vm_size_t size = 0; 152 vm_region_basic_info_data_64_t info; 153 mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; 154 mach_port_t object_name; 155 kern_return_t ret = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &object_name); 156 if (KERN_SUCCESS != ret) break; 157 boolean_t scan = (info.protection & VM_PROT_WRITE) ? 1 : 0; 158 if (scan) { 159 uintptr_t *addr = (uintptr_t *)((uintptr_t)address); 160 uintptr_t *end = (uintptr_t *)((uintptr_t)address + (uintptr_t)size); 161 while (addr < end) { 162 if ((uintptr_t *)start <= addr && *addr == ptr) { 163 return (uintptr_t)addr; 164 } 165 addr++; 166 } 167 } 168 address += size; 169 } 170 return 0; 171 } 172 173 CF_PRIVATE void __CFDumpAllPointerLocations(uintptr_t ptr) { 174 uintptr_t addr = 0; 175 do { 176 addr = __CFFindPointer(ptr, sizeof(void *) + addr); 177 printf("%p\n", (void *)addr); 178 } while (addr != 0); 179 } 180 #endif 181 182 // Looks for localized version of "nonLocalized" in the SystemVersion bundle 183 // If not found, and returnNonLocalizedFlag == true, will return the non localized string (retained of course), otherwise NULL 184 // If bundlePtr != NULL, will use *bundlePtr and will return the bundle in there; otherwise bundle is created and released 185 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 186 static CFStringRef _CFCopyLocalizedVersionKey(CFBundleRef *bundlePtr, CFStringRef nonLocalized) { 187 CFStringRef localized = NULL; 188 CFBundleRef locBundle = bundlePtr ? *bundlePtr : NULL; 189 if (!locBundle) { 190 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR("/System/Library/CoreServices/SystemVersion.bundle"), kCFURLPOSIXPathStyle, false); 191 if (url) { 192 locBundle = CFBundleCreate(kCFAllocatorSystemDefault, url); 193 CFRelease(url); 194 } 195 } 196 if (locBundle) { 197 localized = CFBundleCopyLocalizedString(locBundle, nonLocalized, nonLocalized, CFSTR("SystemVersion")); 198 if (bundlePtr) *bundlePtr = locBundle; else CFRelease(locBundle); 199 } 200 return localized ? localized : (CFStringRef)CFRetain(nonLocalized); 201 } 202 #endif 203 204 static CFDictionaryRef _CFCopyVersionDictionary(CFStringRef path) { 205 CFPropertyListRef plist = NULL; 206 207 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 208 CFDataRef data; 209 CFURLRef url; 210 211 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false); 212 #pragma GCC diagnostic push 213 #pragma GCC diagnostic ignored "-Wdeprecated" 214 if (url && CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, url, &data, NULL, NULL, NULL)) { 215 #pragma GCC diagnostic pop 216 plist = CFPropertyListCreateWithData(kCFAllocatorSystemDefault, data, kCFPropertyListMutableContainers, NULL, NULL); 217 CFRelease(data); 218 } 219 if (url) CFRelease(url); 220 221 if (plist) { 222 #if DEPLOYMENT_TARGET_EMBEDDED_MINI 223 CFStringRef fullVersion, vers, versExtra, build; 224 CFStringRef versionString = CFRetain(_kCFSystemVersionProductVersionStringKey); 225 CFStringRef buildString = CFRetain(_kCFSystemVersionBuildStringKey); 226 CFStringRef fullVersionString = CFRetain(CFSTR("FullVersionString")); 227 #else 228 CFBundleRef locBundle = NULL; 229 CFStringRef fullVersion, vers, versExtra, build; 230 CFStringRef versionString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionProductVersionStringKey); 231 CFStringRef buildString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionBuildStringKey); 232 CFStringRef fullVersionString = _CFCopyLocalizedVersionKey(&locBundle, CFSTR("FullVersionString")); 233 if (locBundle) CFRelease(locBundle); 234 #endif 235 236 // Now build the full version string 237 if (CFEqual(fullVersionString, CFSTR("FullVersionString"))) { 238 CFRelease(fullVersionString); 239 fullVersionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %%@ (%@ %%@)"), versionString, buildString); 240 } 241 vers = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionKey); 242 versExtra = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionExtraKey); 243 if (vers && versExtra) vers = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %@"), vers, versExtra); 244 build = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionBuildVersionKey); 245 fullVersion = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, fullVersionString, (vers ? vers : CFSTR("?")), build ? build : CFSTR("?")); 246 if (vers && versExtra) CFRelease(vers); 247 248 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionStringKey, versionString); 249 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildStringKey, buildString); 250 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR("FullVersionString"), fullVersion); 251 CFRelease(versionString); 252 CFRelease(buildString); 253 CFRelease(fullVersionString); 254 CFRelease(fullVersion); 255 } 256 #elif DEPLOYMENT_TARGET_WINDOWS 257 OSVERSIONINFOEX osvi; 258 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); 259 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 260 BOOL result = GetVersionEx((OSVERSIONINFO *)&osvi); 261 if (!result) return NULL; 262 263 plist = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 10, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 264 265 // e.g. 10.7 266 CFStringRef versionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%ld.%ld(%ld,%ld)"), osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor); 267 268 // e.g. 11A508 269 CFStringRef buildString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%ld"), osvi.dwBuildNumber); 270 271 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionKey, versionString); 272 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildVersionKey, buildString); 273 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductNameKey, CFSTR("Windows")); // hard coded for now 274 275 CFRelease(versionString); 276 CFRelease(buildString); 277 #endif 278 return (CFDictionaryRef)plist; 279 } 280 281 CFStringRef CFCopySystemVersionString(void) { 282 CFStringRef versionString; 283 CFDictionaryRef dict = _CFCopyServerVersionDictionary(); 284 if (!dict) dict = _CFCopySystemVersionDictionary(); 285 if (!dict) return NULL; 286 versionString = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("FullVersionString")); 287 if (versionString) CFRetain(versionString); 288 CFRelease(dict); 289 return versionString; 290 } 291 292 // Obsolete: These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired 293 // In fact, they do not cache any more, because the file can change after 294 // apps are running in some situations, and apps need the new info. 295 // Proper caching and testing to see if the file has changed, without race 296 // conditions, would require semi-convoluted use of fstat(). 297 298 static CFStringRef copySystemVersionPath(CFStringRef suffix) { 299 #if TARGET_IPHONE_SIMULATOR 300 const char *simulatorRoot = getenv("IPHONE_SIMULATOR_ROOT"); 301 if (!simulatorRoot) simulatorRoot = getenv("CFFIXED_USER_HOME"); 302 if (!simulatorRoot) simulatorRoot = "/"; 303 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%s%@"), simulatorRoot, suffix); 304 #else 305 return suffix; 306 #endif 307 } 308 309 310 CFDictionaryRef _CFCopySystemVersionDictionary(void) { 311 CFStringRef path = copySystemVersionPath(CFSTR("/System/Library/CoreServices/SystemVersion.plist")); 312 CFPropertyListRef plist = _CFCopyVersionDictionary(path); 313 CFRelease(path); 314 return (CFDictionaryRef)plist; 315 } 316 317 CFDictionaryRef _CFCopyServerVersionDictionary(void) { 318 CFStringRef path = copySystemVersionPath(CFSTR("/System/Library/CoreServices/ServerVersion.plist")); 319 CFPropertyListRef plist = _CFCopyVersionDictionary(path); 320 CFRelease(path); 321 return (CFDictionaryRef)plist; 322 } 323 324 CONST_STRING_DECL(_kCFSystemVersionProductNameKey, "ProductName") 325 CONST_STRING_DECL(_kCFSystemVersionProductCopyrightKey, "ProductCopyright") 326 CONST_STRING_DECL(_kCFSystemVersionProductVersionKey, "ProductVersion") 327 CONST_STRING_DECL(_kCFSystemVersionProductVersionExtraKey, "ProductVersionExtra") 328 CONST_STRING_DECL(_kCFSystemVersionProductUserVisibleVersionKey, "ProductUserVisibleVersion") 329 CONST_STRING_DECL(_kCFSystemVersionBuildVersionKey, "ProductBuildVersion") 330 CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey, "Version") 331 CONST_STRING_DECL(_kCFSystemVersionBuildStringKey, "Build") 332 333 334 CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { 335 return true; 336 } 337 338 339 340 341 #if DEPLOYMENT_TARGET_MACOSX 342 CF_PRIVATE void *__CFLookupCarbonCoreFunction(const char *name) { 343 static void *image = NULL; 344 if (NULL == image) { 345 image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY | RTLD_LOCAL); 346 } 347 void *dyfunc = NULL; 348 if (image) { 349 dyfunc = dlsym(image, name); 350 } 351 return dyfunc; 352 } 353 #endif 354 355 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 356 CF_PRIVATE void *__CFLookupCoreServicesInternalFunction(const char *name) { 357 static void *image = NULL; 358 if (NULL == image) { 359 image = dlopen("/System/Library/PrivateFrameworks/CoreServicesInternal.framework/CoreServicesInternal", RTLD_LAZY | RTLD_LOCAL); 360 } 361 void *dyfunc = NULL; 362 if (image) { 363 dyfunc = dlsym(image, name); 364 } 365 return dyfunc; 366 } 367 368 CF_PRIVATE void *__CFLookupCFNetworkFunction(const char *name) { 369 static void *image = NULL; 370 if (NULL == image) { 371 const char *path = NULL; 372 if (!__CFProcessIsRestricted()) { 373 path = __CFgetenv("CFNETWORK_LIBRARY_PATH"); 374 } 375 if (!path) { 376 path = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork"; 377 } 378 image = dlopen(path, RTLD_LAZY | RTLD_LOCAL); 379 } 380 void *dyfunc = NULL; 381 if (image) { 382 dyfunc = dlsym(image, name); 383 } 384 return dyfunc; 385 } 386 #endif 387 388 CF_PRIVATE CFIndex __CFActiveProcessorCount() { 389 int32_t pcnt; 390 #if DEPLOYMENT_TARGET_WINDOWS 391 SYSTEM_INFO sysInfo; 392 GetSystemInfo(&sysInfo); 393 DWORD_PTR activeProcessorMask = sysInfo.dwActiveProcessorMask; 394 // assumes sizeof(DWORD_PTR) is 64 bits or less 395 uint64_t v = activeProcessorMask; 396 v = v - ((v >> 1) & 0x5555555555555555ULL); 397 v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL); 398 v = (v + (v >> 4)) & 0xf0f0f0f0f0f0f0fULL; 399 pcnt = (v * 0x0101010101010101ULL) >> ((sizeof(v) - 1) * 8); 400 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 401 int32_t mib[] = {CTL_HW, HW_AVAILCPU}; 402 size_t len = sizeof(pcnt); 403 int32_t result = sysctl(mib, sizeof(mib) / sizeof(int32_t), &pcnt, &len, NULL, 0); 404 if (result != 0) { 405 pcnt = 0; 406 } 407 #else 408 // Assume the worst 409 pcnt = 1; 410 #endif 411 return pcnt; 412 } 413 414 CF_PRIVATE void __CFGetUGIDs(uid_t *euid, gid_t *egid) { 415 #if 1 && (DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI) 416 uid_t uid; 417 gid_t gid; 418 if (0 == pthread_getugid_np(&uid, &gid)) { 419 if (euid) *euid = uid; 420 if (egid) *egid = gid; 421 } else 422 #endif 423 { 424 if (euid) *euid = geteuid(); 425 if (egid) *egid = getegid(); 426 } 427 } 428 429 const char *_CFPrintForDebugger(const void *obj) { 430 static char *result = NULL; 431 CFStringRef str; 432 CFIndex cnt = 0; 433 434 free(result); // Let go of result from previous call. 435 result = NULL; 436 if (obj) { 437 if (CFGetTypeID(obj) == CFStringGetTypeID()) { 438 // Makes Ali marginally happier 439 str = __CFCopyFormattingDescription(obj, NULL); 440 if (!str) str = CFCopyDescription(obj); 441 } else { 442 str = CFCopyDescription(obj); 443 } 444 } else { 445 str = (CFStringRef)CFRetain(CFSTR("(null)")); 446 } 447 448 if (str != NULL) { 449 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, NULL, 0, &cnt); 450 } 451 result = (char *) malloc(cnt + 2); // 1 for '\0', 1 for an optional '\n' 452 if (str != NULL) { 453 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, (UInt8 *) result, cnt, &cnt); 454 } 455 result[cnt] = '\0'; 456 457 if (str) CFRelease(str); 458 return result; 459 } 460 461 static void _CFShowToFile(FILE *file, Boolean flush, const void *obj) { 462 CFStringRef str; 463 CFIndex idx, cnt; 464 CFStringInlineBuffer buffer; 465 bool lastNL = false; 466 467 if (obj) { 468 if (CFGetTypeID(obj) == CFStringGetTypeID()) { 469 // Makes Ali marginally happier 470 str = __CFCopyFormattingDescription(obj, NULL); 471 if (!str) str = CFCopyDescription(obj); 472 } else { 473 str = CFCopyDescription(obj); 474 } 475 } else { 476 str = (CFStringRef)CFRetain(CFSTR("(null)")); 477 } 478 cnt = CFStringGetLength(str); 479 480 #if DEPLOYMENT_TARGET_WINDOWS 481 UniChar *ptr = (UniChar *)CFStringGetCharactersPtr(str); 482 BOOL freePtr = false; 483 if (!ptr) { 484 CFIndex strLen = CFStringGetLength(str); 485 // +2, 1 for newline, 1 for null 486 CFIndex bufSize = sizeof(UniChar *) * (CFStringGetMaximumSizeForEncoding(strLen, kCFStringEncodingUnicode) + 2); 487 CFIndex bytesUsed = 0; 488 ptr = (UniChar *)malloc(bufSize); 489 CFStringGetCharacters(str, CFRangeMake(0, strLen), ptr); 490 ptr[strLen] = L'\n'; 491 ptr[strLen+1] = 0; 492 freePtr = true; 493 } 494 OutputDebugStringW((wchar_t *)ptr); 495 if (freePtr) free(ptr); 496 #else 497 CFStringInitInlineBuffer(str, &buffer, CFRangeMake(0, cnt)); 498 for (idx = 0; idx < cnt; idx++) { 499 UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buffer, idx); 500 if (ch < 128) { 501 fprintf_l(file, NULL, "%c", ch); 502 lastNL = (ch == '\n'); 503 } else { 504 fprintf_l(file, NULL, "\\u%04x", ch); 505 } 506 } 507 if (!lastNL) { 508 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 509 fprintf_l(file, NULL, "\n"); 510 #else 511 fprintf(file, NULL, "\n"); 512 #endif 513 if (flush) fflush(file); 514 } 515 #endif 516 517 if (str) CFRelease(str); 518 } 519 520 void CFShow(const void *obj) { 521 _CFShowToFile(stderr, true, obj); 522 } 523 524 525 // message must be a UTF8-encoded, null-terminated, byte buffer with at least length bytes 526 typedef void (*CFLogFunc)(int32_t lev, const char *message, size_t length, char withBanner); 527 528 static Boolean also_do_stderr() { 529 #if DEPLOYMENT_TARGET_EMBEDDED_MINI 530 // just log to stderr, other logging facilities are out 531 return true; 532 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 533 if (!issetugid() && __CFgetenv("CFLOG_FORCE_STDERR")) { 534 return true; 535 } 536 struct stat sb; 537 int ret = fstat(STDERR_FILENO, &sb); 538 if (ret < 0) return false; 539 mode_t m = sb.st_mode & S_IFMT; 540 if (S_IFREG == m || S_IFSOCK == m) return true; 541 if (!(S_IFIFO == m || S_IFCHR == m)) return false; // disallow any whacky stuff 542 #if 0 // launchd no longer repeats everything it hears 543 // if it could be a pipe back to launchd, fail 544 int64_t val = 0; 545 // assumes val is not written to on error 546 vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &val); 547 if (val) return false; 548 #endif 549 #endif 550 return true; 551 } 552 553 extern char *__CFBundleMainID; 554 555 static void __CFLogCString(int32_t lev, const char *message, size_t length, char withBanner) { 556 char *banner = NULL; 557 char *time = NULL; 558 char *thread = NULL; 559 char *uid = NULL; 560 int bannerLen; 561 bannerLen = 0; 562 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 563 // 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. 564 if (withBanner) { 565 double dummy; 566 CFAbsoluteTime at = CFAbsoluteTimeGetCurrent(); 567 time_t tv = floor(at + kCFAbsoluteTimeIntervalSince1970); 568 struct tm mine; 569 localtime_r(&tv, &mine); 570 int32_t year = mine.tm_year + 1900; 571 int32_t month = mine.tm_mon + 1; 572 int32_t day = mine.tm_mday; 573 int32_t hour = mine.tm_hour; 574 int32_t minute = mine.tm_min; 575 int32_t second = mine.tm_sec; 576 int32_t ms = (int32_t)floor(1000.0 * modf(at, &dummy)); 577 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 578 uint64_t tid = 0; 579 if (0 != pthread_threadid_np(NULL, &tid)) tid = pthread_mach_thread_np(pthread_self()); 580 asprintf(&banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%llu] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), tid); 581 asprintf(&thread, "%x", pthread_mach_thread_np(pthread_self())); 582 #elif DEPLOYMENT_TARGET_WINDOWS 583 bannerLen = asprintf(&banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), GetCurrentThreadId()); 584 asprintf(&thread, "%x", GetCurrentThreadId()); 585 #else 586 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()); 587 asprintf(&thread, "%lx", pthread_self()); 588 #endif 589 asprintf(&time, "%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, ms); 590 591 } 592 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 593 uid_t euid; 594 __CFGetUGIDs(&euid, NULL); 595 asprintf(&uid, "%d", euid); 596 aslclient asl = asl_open(NULL, __CFBundleMainID[0] ? __CFBundleMainID : "com.apple.console", ASL_OPT_NO_DELAY); 597 aslmsg msg = asl_new(ASL_TYPE_MSG); 598 asl_set(msg, "CFLog Local Time", time); // not to be documented, not public API 599 asl_set(msg, "CFLog Thread", thread); // not to be documented, not public API 600 asl_set(msg, "ReadUID", uid); 601 static const char *levstr[] = {"0", "1", "2", "3", "4", "5", "6", "7"}; 602 asl_set(msg, ASL_KEY_LEVEL, levstr[lev]); 603 asl_set(msg, ASL_KEY_MSG, message); 604 asl_send(asl, msg); 605 asl_free(msg); 606 asl_close(asl); 607 #endif 608 #endif // DEPLOYMENT_TARGET 609 610 if (also_do_stderr()) { 611 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 612 struct iovec v[3]; 613 v[0].iov_base = banner; 614 v[0].iov_len = banner ? strlen(banner) : 0; 615 v[1].iov_base = (char *)message; 616 v[1].iov_len = length; 617 v[2].iov_base = "\n"; 618 v[2].iov_len = (message[length - 1] != '\n') ? 1 : 0; 619 int nv = (v[0].iov_base ? 1 : 0) + 1 + (v[2].iov_len ? 1 : 0); 620 static CFLock_t lock = CFLockInit; 621 __CFLock(&lock); 622 writev(STDERR_FILENO, v[0].iov_base ? v : v + 1, nv); 623 __CFUnlock(&lock); 624 #elif DEPLOYMENT_TARGET_WINDOWS 625 size_t bufLen = bannerLen + length + 1; 626 char *buf = (char *)malloc(sizeof(char) * bufLen); 627 if (banner) { 628 // Copy the banner into the debug string 629 memmove_s(buf, bufLen, banner, bannerLen); 630 631 // Copy the message into the debug string 632 strcpy_s(buf + bannerLen, bufLen - bannerLen, message); 633 } else { 634 strcpy_s(buf, bufLen, message); 635 } 636 buf[bufLen - 1] = '\0'; 637 fprintf_s(stderr, "%s\n", buf); 638 // This Win32 API call only prints when a debugger is active 639 // OutputDebugStringA(buf); 640 free(buf); 641 #else 642 size_t bufLen = bannerLen + length + 1; 643 char *buf = (char *)malloc(sizeof(char) * bufLen); 644 if (banner) { 645 // Copy the banner into the debug string 646 memmove(buf, banner, bannerLen); 647 648 // Copy the message into the debug string 649 strncpy(buf + bannerLen, message, bufLen - bannerLen); 650 } else { 651 strncpy(buf, message, bufLen); 652 } 653 buf[bufLen - 1] = '\0'; 654 fprintf(stderr, "%s\n", buf); 655 free(buf); 656 #endif 657 } 658 659 if (thread) free(thread); 660 if (time) free(time); 661 if (banner) free(banner); 662 if (uid) free(uid); 663 } 664 665 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) { 666 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 667 uintptr_t val = (uintptr_t)_CFGetTSD(__CFTSDKeyIsInCFLog); 668 if (3 < val) return; // allow up to 4 nested invocations 669 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)(val + 1), NULL); 670 #endif 671 CFStringRef str = format ? _CFStringCreateWithFormatAndArgumentsAux2(kCFAllocatorSystemDefault, copyDescFunc, contextDescFunc, formatOptions, (CFStringRef)format, args) : 0; 672 CFIndex blen = str ? CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1 : 0; 673 char *buf = str ? (char *)malloc(blen) : 0; 674 if (str && buf) { 675 Boolean converted = CFStringGetCString(str, buf, blen, kCFStringEncodingUTF8); 676 size_t len = strlen(buf); 677 // silently ignore 0-length or really large messages, and levels outside the valid range 678 if (converted && !(len <= 0 || (1 << 24) < len) && !(lev < ASL_LEVEL_EMERG || ASL_LEVEL_DEBUG < lev)) { 679 (logit ? logit : __CFLogCString)(lev, buf, len, 1); 680 } 681 } 682 if (buf) free(buf); 683 if (str) CFRelease(str); 684 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 685 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)val, NULL); 686 #endif 687 } 688 689 CF_EXPORT void _CFLogvEx(CFLogFunc logit, CFStringRef (*copyDescFunc)(void *, const void *), CFDictionaryRef formatOptions, int32_t lev, CFStringRef format, va_list args) { 690 _CFLogvEx2(logit, copyDescFunc, NULL, formatOptions, lev, format, args); 691 } 692 693 // 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 694 CF_PRIVATE void _CFLogSimple(int32_t lev, char *format, ...) { 695 va_list args; 696 va_start(args, format); 697 char formattedMessage[1024]; 698 int length = vsnprintf(formattedMessage, 1024, format, args); 699 if (length > 0) { 700 __CFLogCString(lev, formattedMessage, length, 0); 701 } 702 va_end(args); 703 } 704 705 void CFLog(int32_t lev, CFStringRef format, ...) { 706 va_list args; 707 va_start(args, format); 708 _CFLogvEx2(NULL, NULL, NULL, NULL, lev, format, args); 709 va_end(args); 710 } 711 712 713 714 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 715 716 kern_return_t _CFDiscorporateMemoryAllocate(CFDiscorporateMemory *hm, size_t size, bool purgeable) { 717 kern_return_t ret = KERN_SUCCESS; 718 size = round_page(size); 719 if (0 == size) size = vm_page_size; 720 memset(hm, 0, sizeof(CFDiscorporateMemory)); 721 void *addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, VM_MAKE_TAG(0) | (purgeable ? VM_FLAGS_PURGABLE : 0), 0); 722 if ((uintptr_t)addr == -1) { 723 ret = KERN_NO_SPACE; 724 } 725 if (KERN_SUCCESS == ret) { 726 hm->address = (mach_vm_address_t)(uintptr_t)addr; 727 hm->size = (mach_vm_size_t)size; 728 hm->port = MACH_PORT_NULL; 729 hm->corporeal = true; 730 hm->purgeable = purgeable; 731 } 732 if (KERN_SUCCESS == ret) ret = mach_make_memory_entry_64(mach_task_self(), &hm->size, hm->address, VM_PROT_DEFAULT, &hm->port, MACH_PORT_NULL); 733 if (KERN_SUCCESS == ret) hm->corporeal = true; 734 return ret; 735 } 736 737 kern_return_t _CFDiscorporateMemoryDeallocate(CFDiscorporateMemory *hm) { 738 kern_return_t ret1 = KERN_SUCCESS, ret2 = KERN_SUCCESS; 739 if (hm->corporeal) ret1 = mach_vm_deallocate(mach_task_self(), hm->address, hm->size); 740 hm->address = MACH_VM_MIN_ADDRESS; 741 hm->corporeal = false; 742 ret2 = mach_port_deallocate(mach_task_self(), hm->port); 743 hm->port = MACH_PORT_NULL; 744 return ret1 != KERN_SUCCESS ? ret1 : ret2; 745 } 746 747 kern_return_t _CFDiscorporateMemoryDematerialize(CFDiscorporateMemory *hm) { 748 kern_return_t ret = KERN_SUCCESS; 749 if (!hm->corporeal) ret = KERN_INVALID_MEMORY_CONTROL; 750 int state = VM_PURGABLE_VOLATILE; 751 if (KERN_SUCCESS == ret) vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state); 752 if (KERN_SUCCESS == ret) ret = mach_vm_deallocate(mach_task_self(), hm->address, hm->size); 753 if (KERN_SUCCESS == ret) hm->address = MACH_VM_MIN_ADDRESS; 754 if (KERN_SUCCESS == ret) hm->corporeal = false; 755 return ret; 756 } 757 758 kern_return_t _CFDiscorporateMemoryMaterialize(CFDiscorporateMemory *hm) { 759 kern_return_t ret = KERN_SUCCESS; 760 if (hm->corporeal) ret = KERN_INVALID_MEMORY_CONTROL; 761 if (KERN_SUCCESS == ret) ret = mach_vm_map(mach_task_self(), &hm->address, hm->size, 0, VM_FLAGS_ANYWHERE, hm->port, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT); 762 if (KERN_SUCCESS == ret) hm->corporeal = true; 763 int state = VM_PURGABLE_NONVOLATILE; 764 if (KERN_SUCCESS == ret) ret = vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state); 765 if (KERN_SUCCESS == ret) if (VM_PURGABLE_EMPTY == state) ret = KERN_PROTECTION_FAILURE; // same as VM_PURGABLE_EMPTY 766 return ret; 767 } 768 769 #endif 770 771 #if DEPLOYMENT_TARGET_MACOSX 772 773 #define SUDDEN_TERMINATION_ENABLE_VPROC 1 774 775 #if SUDDEN_TERMINATION_ENABLE_VPROC 776 777 static OSSpinLock __CFProcessKillingLock = OS_SPINLOCK_INIT; 778 static CFIndex __CFProcessKillingDisablingCount = 1; 779 static Boolean __CFProcessKillingWasTurnedOn = false; 780 781 void _CFSuddenTerminationDisable(void) { 782 OSSpinLockLock(&__CFProcessKillingLock); 783 __CFProcessKillingDisablingCount++; 784 _vproc_transaction_begin(); 785 OSSpinLockUnlock(&__CFProcessKillingLock); 786 } 787 788 void _CFSuddenTerminationEnable(void) { 789 // 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. 790 OSSpinLockLock(&__CFProcessKillingLock); 791 __CFProcessKillingDisablingCount--; 792 if (__CFProcessKillingDisablingCount==0 && !__CFProcessKillingWasTurnedOn) { 793 _vproc_transactions_enable(); 794 __CFProcessKillingWasTurnedOn = true; 795 } else { 796 // Mail seems to have sudden termination disabling/enabling imbalance bugs that make _vproc_transaction_end() kill the app but we don't want that to prevent our submission of the fix 6382488. 797 if (__CFProcessKillingDisablingCount>=0) { 798 _vproc_transaction_end(); 799 } else { 800 CFLog(kCFLogLevelError, CFSTR("-[NSProcessInfo enableSuddenTermination] has been invoked more times than necessary to balance invocations of -[NSProcessInfo disableSuddenTermination]. Ignoring.")); 801 } 802 } 803 OSSpinLockUnlock(&__CFProcessKillingLock); 804 } 805 806 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) { 807 // 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. 808 OSSpinLockLock(&__CFProcessKillingLock); 809 // Check _vproc_transaction_count() because other code in the process might go straight to the vproc APIs but also check __CFProcessKillingWasTurnedOn because _vproc_transaction_count() can return 0 when transactions didn't even get enabled. 810 if (__CFProcessKillingWasTurnedOn) { 811 _vproc_transaction_try_exit(exitStatus); 812 } 813 OSSpinLockUnlock(&__CFProcessKillingLock); 814 } 815 816 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) { 817 // The user has had their final opportunity to cancel quitting. Exit as soon as the process is clean. Same carefulness as in _CFSuddenTerminationExitIfTerminationEnabled(). 818 OSSpinLockLock(&__CFProcessKillingLock); 819 if (__CFProcessKillingWasTurnedOn) { 820 } 821 OSSpinLockUnlock(&__CFProcessKillingLock); 822 } 823 824 size_t _CFSuddenTerminationDisablingCount(void) { 825 // Until sudden termination has been really enabled vproc's notion of the count is off by one but we can't just return __CFProcessKillingDisablingCount() because that doesn't take into account stuff that calls the vproc_transaction functions behind our back. 826 return _vproc_transaction_count() + (__CFProcessKillingWasTurnedOn ? 0 : 1); 827 } 828 829 #else 830 831 #warning Building with vproc sudden termination API disabled. 832 833 static OSSpinLockUnlock __CFProcessKillingLock = OS_SPINLOCK_INIT; 834 static size_t __CFProcessKillingDisablingCount = 1; 835 static Boolean __CFProcessExitNextTimeKillingIsEnabled = false; 836 static int32_t __CFProcessExitStatus = 0; 837 static int __CFProcessIsKillableNotifyToken; 838 static Boolean __CFProcessIsKillableNotifyTokenIsFigured = false; 839 840 CF_PRIVATE void _CFSetSuddenTerminationEnabled(Boolean isEnabled) { 841 if (!__CFProcessIsKillableNotifyTokenIsFigured) { 842 char *notificationName = NULL; 843 asprintf(¬ificationName, "com.apple.isKillable.%i", getpid()); 844 uint32_t notifyResult = notify_register_check(notificationName, &__CFProcessIsKillableNotifyToken); 845 if (notifyResult != NOTIFY_STATUS_OK) { 846 CFLog(kCFLogLevelError, CFSTR("%s: notify_register_check() returned %i."), __PRETTY_FUNCTION__, notifyResult); 847 } 848 free(notificationName); 849 __CFProcessIsKillableNotifyTokenIsFigured = true; 850 } 851 uint32_t notifyResult = notify_set_state(__CFProcessIsKillableNotifyToken, isEnabled); 852 if (notifyResult != NOTIFY_STATUS_OK) { 853 CFLog(kCFLogLevelError, CFSTR("%s: notify_set_state() returned %i"), __PRETTY_FUNCTION__, notifyResult); 854 } 855 } 856 857 void _CFSuddenTerminationDisable(void) { 858 OSSpinLockLock(&__CFProcessKillingLock); 859 if (__CFProcessKillingDisablingCount == 0) { 860 _CFSetSuddenTerminationEnabled(false); 861 } 862 __CFProcessKillingDisablingCount++; 863 OSSpinLockUnlock(&__CFProcessKillingLock); 864 } 865 866 void _CFSuddenTerminationEnable(void) { 867 OSSpinLockLock(&__CFProcessKillingLock); 868 __CFProcessKillingDisablingCount--; 869 if (__CFProcessKillingDisablingCount == 0) { 870 if (__CFProcessExitNextTimeKillingIsEnabled) { 871 _exit(__CFProcessExitStatus); 872 } else { 873 _CFSetSuddenTerminationEnabled(true); 874 } 875 } 876 OSSpinLockUnlock(&__CFProcessKillingLock); 877 } 878 879 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) { 880 OSSpinLockLock(&__CFProcessKillingLock); 881 if (__CFProcessKillingDisablingCount == 0) { 882 _exit(exitStatus); 883 } 884 OSSpinLockUnlock(&__CFProcessKillingLock); 885 } 886 887 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) { 888 OSSpinLockLock(&__CFProcessKillingLock); 889 if (__CFProcessKillingDisablingCount == 0) { 890 _exit(exitStatus); 891 } else { 892 __CFProcessExitNextTimeKillingIsEnabled = YES; 893 __CFProcessExitStatus = exitStatus; 894 } 895 OSSpinLockUnlock(&__CFProcessKillingLock); 896 } 897 898 size_t _CFSuddenTerminationDisablingCount(void) { 899 return __CFProcessKillingDisablingCount; 900 } 901 902 #endif 903 904 #endif 905 906 #if 0 907 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 908 909 typedef void (^ThrottleTypeA)(void); // allows calls per nanoseconds 910 typedef void (^ThrottleTypeB)(uint64_t amt); // allows amount per nanoseconds 911 912 CF_PRIVATE ThrottleTypeA __CFCreateThrottleTypeA(uint16_t calls, uint64_t nanoseconds) { 913 struct mach_timebase_info info; 914 mach_timebase_info(&info); 915 uint64_t period = nanoseconds / info.numer * info.denom; 916 917 if (0 == calls || 0 == period) return NULL; 918 919 __block OSSpinLock b_lock = OS_SPINLOCK_INIT; 920 __block uint64_t b_values[calls]; 921 __block uint64_t *b_oldest = b_values; 922 memset(b_values, 0, sizeof(b_values)); 923 924 return Block_copy(^{ 925 uint64_t curr_time = mach_absolute_time(); 926 OSSpinLockLock(&b_lock); 927 uint64_t next_time = *b_oldest + period; 928 *b_oldest = (curr_time < next_time) ? next_time : curr_time; 929 b_oldest++; 930 if (b_values + calls <= b_oldest) b_oldest = b_values; 931 OSSpinLockUnlock(&b_lock); 932 if (curr_time < next_time) { 933 mach_wait_until(next_time); 934 } 935 }); 936 } 937 938 CF_PRIVATE ThrottleTypeB __CFCreateThrottleTypeB(uint64_t amount, uint64_t nanoseconds) { 939 struct mach_timebase_info info; 940 mach_timebase_info(&info); 941 uint64_t period = nanoseconds / info.numer * info.denom; 942 943 if (0 == amount || 0 == period) return NULL; 944 945 __block OSSpinLock b_lock = OS_SPINLOCK_INIT; 946 __block uint64_t b_sum = 0ULL; 947 __block uint16_t b_num_values = 8; 948 __block uint64_t *b_values = calloc(b_num_values, 2 * sizeof(uint64_t)); 949 __block uint64_t *b_oldest = b_values; 950 951 return Block_copy(^(uint64_t amt){ 952 OSSpinLockLock(&b_lock); 953 // unimplemented 954 OSSpinLockUnlock(&b_lock); 955 }); 956 } 957 958 #endif 959 #endif 960 961 #pragma mark File Reading 962 963 #include <sys/stat.h> 964 #include <fcntl.h> 965 #include <errno.h> 966 #if DEPLOYMENT_TARGET_WINDOWS 967 #include <io.h> 968 #include <direct.h> 969 #define close _close 970 #define write _write 971 #define read _read 972 #define open _NS_open 973 #define stat _NS_stat 974 #define fstat _fstat 975 #define statinfo _stat 976 977 #define mach_task_self() 0 978 979 #else 980 #define statinfo stat 981 #endif 982 983 static CFErrorRef _CFErrorWithFilePathCodeDomain(CFStringRef domain, CFIndex code, CFStringRef path) { 984 CFStringRef key = CFSTR("NSFilePath"); 985 CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorSystemDefault, (const void **)&key, (const void **)&path, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 986 CFErrorRef result = CFErrorCreate(kCFAllocatorSystemDefault, domain, code, userInfo); 987 CFRelease(userInfo); 988 return result; 989 } 990 991 // Caller is responsible for freeing memory. munmap() if map == true, else malloc(). 992 CF_PRIVATE Boolean _CFReadMappedFromFile(CFStringRef path, Boolean map, Boolean uncached, void **outBytes, CFIndex *outLength, CFErrorRef *errorPtr) { 993 void *bytes = NULL; 994 unsigned long length; 995 char cpath[CFMaxPathSize]; 996 if (!CFStringGetFileSystemRepresentation(path, cpath, CFMaxPathSize)) { 997 // TODO: real error codes 998 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainCocoa, -1, path); 999 return false; 1000 } 1001 1002 struct statinfo statBuf; 1003 int32_t fd = -1; 1004 1005 fd = open(cpath, O_RDONLY|CF_OPENFLGS, 0666); 1006 if (fd < 0) { 1007 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, errno, path); 1008 return false; 1009 } 1010 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 1011 if (uncached) (void)fcntl(fd, F_NOCACHE, 1); // Non-zero arg turns off caching; we ignore error as uncached is just a hint 1012 #endif 1013 if (fstat(fd, &statBuf) < 0) { 1014 int32_t savederrno = errno; 1015 close(fd); 1016 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1017 return false; 1018 } 1019 if ((statBuf.st_mode & S_IFMT) != S_IFREG) { 1020 close(fd); 1021 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, EACCES, path); 1022 return false; 1023 } 1024 if (statBuf.st_size < 0LL) { // too small 1025 close(fd); 1026 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, ENOMEM, path); 1027 return false; 1028 } 1029 #if __LP64__ 1030 #else 1031 if (statBuf.st_size > (1LL << 31)) { // refuse to do more than 2GB 1032 close(fd); 1033 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, EFBIG, path); 1034 return false; 1035 } 1036 #endif 1037 1038 if (0LL == statBuf.st_size) { 1039 bytes = malloc(8); // don't return constant string -- it's freed! 1040 length = 0; 1041 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 1042 } else if (map) { 1043 if((void *)-1 == (bytes = mmap(0, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0))) { 1044 int32_t savederrno = errno; 1045 close(fd); 1046 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1047 return false; 1048 } 1049 length = (unsigned long)statBuf.st_size; 1050 } else { 1051 bytes = malloc(statBuf.st_size); 1052 if (bytes == NULL) { 1053 close(fd); 1054 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, ENOMEM, path); 1055 return false; 1056 } 1057 size_t numBytesRemaining = (size_t)statBuf.st_size; 1058 void *readLocation = bytes; 1059 while (numBytesRemaining > 0) { 1060 size_t numBytesRequested = (numBytesRemaining < (1LL << 31)) ? numBytesRemaining : ((1LL << 31) - 1); // This loop is basically a workaround for 4870206 1061 ssize_t numBytesRead = read(fd, readLocation, numBytesRequested); 1062 if (numBytesRead <= 0) { 1063 if (numBytesRead < 0) { 1064 int32_t savederrno = errno; 1065 free(bytes); 1066 close(fd); 1067 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path); 1068 bytes = NULL; 1069 return false; 1070 } else { 1071 // This is a bizarre case; 0 bytes read. Might indicate end-of-file? 1072 break; 1073 } 1074 } else { 1075 readLocation += numBytesRead; 1076 numBytesRemaining -= numBytesRead; 1077 } 1078 } 1079 length = (unsigned long)statBuf.st_size - numBytesRemaining; 1080 } 1081 #elif DEPLOYMENT_TARGET_WINDOWS 1082 } else { 1083 bytes = malloc(statBuf.st_size); 1084 DWORD numBytesRead; 1085 if (!ReadFile((HANDLE)_get_osfhandle(fd), bytes, statBuf.st_size, &numBytesRead, NULL)) { 1086 DWORD lastError = GetLastError(); 1087 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, lastError, path); 1088 free(bytes); 1089 close(fd); 1090 errno = lastError; 1091 bytes = NULL; 1092 return false; 1093 } 1094 length = numBytesRead; 1095 } 1096 #endif 1097 close(fd); 1098 *outBytes = bytes; 1099 *outLength = length; 1100 return true; 1101 }