]> git.saurik.com Git - apple/cf.git/blob - CFUtilities.c
CF-744.tar.gz
[apple/cf.git] / CFUtilities.c
1 /*
2 * Copyright (c) 2012 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-2012, 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 #include <CoreFoundation/CFTimeZone.h>
39 #include <CoreFoundation/CFCalendar.h>
40 #if DEPLOYMENT_TARGET_WINDOWS
41 #include <process.h>
42 #endif
43 #include <math.h>
44 #include <string.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
48 #include <asl.h>
49 #else
50 #define ASL_LEVEL_EMERG 0
51 #define ASL_LEVEL_DEBUG 7
52 #endif
53
54 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
55 #include <unistd.h>
56 #include <sys/uio.h>
57 #include <mach/mach.h>
58 #include <pthread.h>
59 #include <mach-o/loader.h>
60 #include <mach-o/dyld.h>
61 #include <crt_externs.h>
62 #include <dlfcn.h>
63 #include <vproc.h>
64 #include <vproc_priv.h>
65 #include <sys/sysctl.h>
66 #include <sys/stat.h>
67 #include <mach/mach.h>
68 #include <mach/mach_vm.h>
69 #include <sys/mman.h>
70 #include <stdio.h>
71 #include <sys/errno.h>
72 #include <mach/mach_time.h>
73 #include <Block.h>
74 #endif
75 #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
76 #include <string.h>
77 #include <pthread.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 __private_extern__ 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 __private_extern__ 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 __private_extern__ 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 #if DEPLOYMENT_TARGET_WINDOWS
183 struct _args {
184 void *func;
185 void *arg;
186 HANDLE handle;
187 };
188 static unsigned __stdcall __CFWinThreadFunc(void *arg) {
189 struct _args *args = (struct _args*)arg;
190 ((void (*)(void *))args->func)(args->arg);
191 CloseHandle(args->handle);
192 CFAllocatorDeallocate(kCFAllocatorSystemDefault, arg);
193 _endthreadex(0);
194 return 0;
195 }
196 #endif
197
198 __private_extern__ void *__CFStartSimpleThread(void *func, void *arg) {
199 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
200 pthread_attr_t attr;
201 pthread_t tid = 0;
202 pthread_attr_init(&attr);
203 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
204 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
205 pthread_attr_setstacksize(&attr, 60 * 1024); // 60K stack for our internal threads is sufficient
206 OSMemoryBarrier(); // ensure arg is fully initialized and set in memory
207 pthread_create(&tid, &attr, func, arg);
208 pthread_attr_destroy(&attr);
209 //warning CF: we dont actually know that a pthread_t is the same size as void *
210 return (void *)tid;
211 #elif DEPLOYMENT_TARGET_WINDOWS
212 unsigned tid;
213 struct _args *args = (struct _args*)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct _args), 0);
214 if (__CFOASafe) __CFSetLastAllocationEventName(args, "CFUtilities (thread-args)");
215 HANDLE handle;
216 args->func = func;
217 args->arg = arg;
218 /* The thread is created suspended, because otherwise there would be a race between the assignment below of the handle field, and it's possible use in the thread func above. */
219 args->handle = (HANDLE)_beginthreadex(NULL, 0, __CFWinThreadFunc, args, CREATE_SUSPENDED, &tid);
220 handle = args->handle;
221 ResumeThread(handle);
222 return handle;
223 #endif
224 }
225
226
227 // Looks for localized version of "nonLocalized" in the SystemVersion bundle
228 // If not found, and returnNonLocalizedFlag == true, will return the non localized string (retained of course), otherwise NULL
229 // If bundlePtr != NULL, will use *bundlePtr and will return the bundle in there; otherwise bundle is created and released
230 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
231 static CFStringRef _CFCopyLocalizedVersionKey(CFBundleRef *bundlePtr, CFStringRef nonLocalized) {
232 CFStringRef localized = NULL;
233 CFBundleRef locBundle = bundlePtr ? *bundlePtr : NULL;
234 if (!locBundle) {
235 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR("/System/Library/CoreServices/SystemVersion.bundle"), kCFURLPOSIXPathStyle, false);
236 if (url) {
237 locBundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
238 CFRelease(url);
239 }
240 }
241 if (locBundle) {
242 localized = CFBundleCopyLocalizedString(locBundle, nonLocalized, nonLocalized, CFSTR("SystemVersion"));
243 if (bundlePtr) *bundlePtr = locBundle; else CFRelease(locBundle);
244 }
245 return localized ? localized : (CFStringRef)CFRetain(nonLocalized);
246 }
247 #endif
248
249 static CFDictionaryRef _CFCopyVersionDictionary(CFStringRef path) {
250 CFPropertyListRef plist = NULL;
251
252 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
253 CFDataRef data;
254 CFURLRef url;
255
256 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false);
257 if (url && CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, url, &data, NULL, NULL, NULL)) {
258 plist = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListMutableContainers, NULL);
259 CFRelease(data);
260 }
261 if (url) CFRelease(url);
262
263 if (plist) {
264 CFBundleRef locBundle = NULL;
265 CFStringRef fullVersion, vers, versExtra, build;
266 CFStringRef versionString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionProductVersionStringKey);
267 CFStringRef buildString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionBuildStringKey);
268 CFStringRef fullVersionString = _CFCopyLocalizedVersionKey(&locBundle, CFSTR("FullVersionString"));
269 if (locBundle) CFRelease(locBundle);
270
271 // Now build the full version string
272 if (CFEqual(fullVersionString, CFSTR("FullVersionString"))) {
273 CFRelease(fullVersionString);
274 fullVersionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %%@ (%@ %%@)"), versionString, buildString);
275 }
276 vers = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionKey);
277 versExtra = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionProductVersionExtraKey);
278 if (vers && versExtra) vers = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@ %@"), vers, versExtra);
279 build = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionBuildVersionKey);
280 fullVersion = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, fullVersionString, (vers ? vers : CFSTR("?")), build ? build : CFSTR("?"));
281 if (vers && versExtra) CFRelease(vers);
282
283 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionStringKey, versionString);
284 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildStringKey, buildString);
285 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR("FullVersionString"), fullVersion);
286 CFRelease(versionString);
287 CFRelease(buildString);
288 CFRelease(fullVersionString);
289 CFRelease(fullVersion);
290 }
291 #elif DEPLOYMENT_TARGET_WINDOWS
292 OSVERSIONINFOEX osvi;
293 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
294 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
295 BOOL result = GetVersionEx((OSVERSIONINFO *)&osvi);
296 if (!result) return NULL;
297
298 plist = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 10, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
299
300 // e.g. 10.7
301 CFStringRef versionString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%ld.%ld(%ld,%ld)"), osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor);
302
303 // e.g. 11A508
304 CFStringRef buildString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%ld"), osvi.dwBuildNumber);
305
306 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionKey, versionString);
307 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildVersionKey, buildString);
308 CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductNameKey, CFSTR("Windows")); // hard coded for now
309
310 CFRelease(versionString);
311 CFRelease(buildString);
312 #endif
313 return (CFDictionaryRef)plist;
314 }
315
316 CFStringRef CFCopySystemVersionString(void) {
317 CFStringRef versionString;
318 CFDictionaryRef dict = _CFCopyServerVersionDictionary();
319 if (!dict) dict = _CFCopySystemVersionDictionary();
320 if (!dict) return NULL;
321 versionString = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("FullVersionString"));
322 if (versionString) CFRetain(versionString);
323 CFRelease(dict);
324 return versionString;
325 }
326
327 // Obsolete: These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired
328 // In fact, they do not cache any more, because the file can change after
329 // apps are running in some situations, and apps need the new info.
330 // Proper caching and testing to see if the file has changed, without race
331 // conditions, would require semi-convoluted use of fstat().
332
333 CFDictionaryRef _CFCopySystemVersionDictionary(void) {
334 CFPropertyListRef plist = NULL;
335 plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/SystemVersion.plist"));
336 return (CFDictionaryRef)plist;
337 }
338
339 CFDictionaryRef _CFCopyServerVersionDictionary(void) {
340 CFPropertyListRef plist = NULL;
341 plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/ServerVersion.plist"));
342 return (CFDictionaryRef)plist;
343 }
344
345 CONST_STRING_DECL(_kCFSystemVersionProductNameKey, "ProductName")
346 CONST_STRING_DECL(_kCFSystemVersionProductCopyrightKey, "ProductCopyright")
347 CONST_STRING_DECL(_kCFSystemVersionProductVersionKey, "ProductVersion")
348 CONST_STRING_DECL(_kCFSystemVersionProductVersionExtraKey, "ProductVersionExtra")
349 CONST_STRING_DECL(_kCFSystemVersionProductUserVisibleVersionKey, "ProductUserVisibleVersion")
350 CONST_STRING_DECL(_kCFSystemVersionBuildVersionKey, "ProductBuildVersion")
351 CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey, "Version")
352 CONST_STRING_DECL(_kCFSystemVersionBuildStringKey, "Build")
353
354
355 CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) {
356 return true;
357 }
358
359
360
361
362 #if DEPLOYMENT_TARGET_MACOSX
363 __private_extern__ void *__CFLookupCarbonCoreFunction(const char *name) {
364 static void *image = NULL;
365 if (NULL == image) {
366 image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY | RTLD_LOCAL);
367 }
368 void *dyfunc = NULL;
369 if (image) {
370 dyfunc = dlsym(image, name);
371 }
372 return dyfunc;
373 }
374 #endif
375
376 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
377 __private_extern__ void *__CFLookupCoreServicesInternalFunction(const char *name) {
378 static void *image = NULL;
379 if (NULL == image) {
380 image = dlopen("/System/Library/PrivateFrameworks/CoreServicesInternal.framework/CoreServicesInternal", RTLD_LAZY | RTLD_LOCAL);
381 }
382 void *dyfunc = NULL;
383 if (image) {
384 dyfunc = dlsym(image, name);
385 }
386 return dyfunc;
387 }
388
389 __private_extern__ void *__CFLookupCFNetworkFunction(const char *name) {
390 static void *image = NULL;
391 if (NULL == image) {
392 const char *path = NULL;
393 if (!issetugid()) {
394 path = __CFgetenv("CFNETWORK_LIBRARY_PATH");
395 }
396 if (!path) {
397 path = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
398 }
399 image = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
400 }
401 void *dyfunc = NULL;
402 if (image) {
403 dyfunc = dlsym(image, name);
404 }
405 return dyfunc;
406 }
407 #endif
408
409
410 #ifndef __CFGetSessionID_defined
411
412 __private_extern__ uint32_t __CFGetSessionID(void) {
413 return 0;
414 }
415
416 #endif
417
418 __private_extern__ CFIndex __CFActiveProcessorCount() {
419 int32_t pcnt;
420 #if DEPLOYMENT_TARGET_WINDOWS
421 SYSTEM_INFO sysInfo;
422 GetSystemInfo(&sysInfo);
423 DWORD_PTR activeProcessorMask = sysInfo.dwActiveProcessorMask;
424 // assumes sizeof(DWORD_PTR) is 64 bits or less
425 uint64_t v = activeProcessorMask;
426 v = v - ((v >> 1) & 0x5555555555555555ULL);
427 v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
428 v = (v + (v >> 4)) & 0xf0f0f0f0f0f0f0fULL;
429 pcnt = (v * 0x0101010101010101ULL) >> ((sizeof(v) - 1) * 8);
430 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
431 int32_t mib[] = {CTL_HW, HW_AVAILCPU};
432 size_t len = sizeof(pcnt);
433 int32_t result = sysctl(mib, sizeof(mib) / sizeof(int32_t), &pcnt, &len, NULL, 0);
434 if (result != 0) {
435 pcnt = 0;
436 }
437 #else
438 // Assume the worst
439 pcnt = 1;
440 #endif
441 return pcnt;
442 }
443
444 __private_extern__ void __CFGetUGIDs(uid_t *euid, gid_t *egid) {
445 #if 1 && (DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI)
446 uid_t uid;
447 gid_t gid;
448 if (0 == pthread_getugid_np(&uid, &gid)) {
449 if (euid) *euid = uid;
450 if (egid) *egid = gid;
451 } else
452 #endif
453 {
454 if (euid) *euid = geteuid();
455 if (egid) *egid = getegid();
456 }
457 }
458
459 const char *_CFPrintForDebugger(const void *obj) {
460 static char *result = NULL;
461 CFStringRef str;
462 CFIndex cnt = 0;
463
464 free(result); // Let go of result from previous call.
465 result = NULL;
466 if (obj) {
467 if (CFGetTypeID(obj) == CFStringGetTypeID()) {
468 // Makes Ali marginally happier
469 str = __CFCopyFormattingDescription(obj, NULL);
470 if (!str) str = CFCopyDescription(obj);
471 } else {
472 str = CFCopyDescription(obj);
473 }
474 } else {
475 str = (CFStringRef)CFRetain(CFSTR("(null)"));
476 }
477
478 if (str != NULL) {
479 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, NULL, 0, &cnt);
480 }
481 result = (char *) malloc(cnt + 2); // 1 for '\0', 1 for an optional '\n'
482 if (str != NULL) {
483 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, FALSE, (UInt8 *) result, cnt, &cnt);
484 }
485 result[cnt] = '\0';
486
487 if (str) CFRelease(str);
488 return result;
489 }
490
491 static void _CFShowToFile(FILE *file, Boolean flush, const void *obj) {
492 CFStringRef str;
493 CFIndex idx, cnt;
494 CFStringInlineBuffer buffer;
495 bool lastNL = false;
496
497 if (obj) {
498 if (CFGetTypeID(obj) == CFStringGetTypeID()) {
499 // Makes Ali marginally happier
500 str = __CFCopyFormattingDescription(obj, NULL);
501 if (!str) str = CFCopyDescription(obj);
502 } else {
503 str = CFCopyDescription(obj);
504 }
505 } else {
506 str = (CFStringRef)CFRetain(CFSTR("(null)"));
507 }
508 cnt = CFStringGetLength(str);
509
510 #if DEPLOYMENT_TARGET_WINDOWS
511 UniChar *ptr = (UniChar *)CFStringGetCharactersPtr(str);
512 BOOL freePtr = false;
513 if (!ptr) {
514 CFIndex strLen = CFStringGetLength(str);
515 // +2, 1 for newline, 1 for null
516 CFIndex bufSize = sizeof(UniChar *) * (CFStringGetMaximumSizeForEncoding(strLen, kCFStringEncodingUnicode) + 2);
517 CFIndex bytesUsed = 0;
518 ptr = (UniChar *)malloc(bufSize);
519 CFStringGetCharacters(str, CFRangeMake(0, strLen), ptr);
520 ptr[strLen] = L'\n';
521 ptr[strLen+1] = 0;
522 freePtr = true;
523 }
524 OutputDebugStringW((wchar_t *)ptr);
525 if (freePtr) free(ptr);
526 #else
527 CFStringInitInlineBuffer(str, &buffer, CFRangeMake(0, cnt));
528 for (idx = 0; idx < cnt; idx++) {
529 UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buffer, idx);
530 if (ch < 128) {
531 fprintf_l(file, NULL, "%c", ch);
532 lastNL = (ch == '\n');
533 } else {
534 fprintf_l(file, NULL, "\\u%04x", ch);
535 }
536 }
537 if (!lastNL) {
538 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
539 fprintf_l(file, NULL, "\n");
540 #else
541 fprintf(file, NULL, "\n");
542 #endif
543 if (flush) fflush(file);
544 }
545 #endif
546
547 if (str) CFRelease(str);
548 }
549
550 void CFShow(const void *obj) {
551 _CFShowToFile(stderr, true, obj);
552 }
553
554
555 // message must be a UTF8-encoded, null-terminated, byte buffer with at least length bytes
556 typedef void (*CFLogFunc)(int32_t lev, const char *message, size_t length, char withBanner);
557
558 static Boolean also_do_stderr() {
559 #if DEPLOYMENT_TARGET_EMBEDDED_MINI
560 // just log to stderr, other logging facilities are out
561 return true;
562 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
563 if (!issetugid() && __CFgetenv("CFLOG_FORCE_STDERR")) {
564 return true;
565 }
566 struct stat sb;
567 int ret = fstat(STDERR_FILENO, &sb);
568 if (ret < 0) return false;
569 mode_t m = sb.st_mode & S_IFMT;
570 if (S_IFREG == m || S_IFSOCK == m) return true;
571 if (!(S_IFIFO == m || S_IFCHR == m)) return false; // disallow any whacky stuff
572 // if it could be a pipe back to launchd, fail
573 int64_t val = 0;
574 // assumes val is not written to on error
575 vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &val);
576 if (val) return false;
577 #endif
578 return true;
579 }
580
581 extern char *__CFBundleMainID;
582
583 static void __CFLogCString(int32_t lev, const char *message, size_t length, char withBanner) {
584 char *banner = NULL;
585 char *time = NULL;
586 char *thread = NULL;
587 char *uid = NULL;
588 #if DEPLOYMENT_TARGET_WINDOWS
589 int bannerLen = 0;
590 #endif
591 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
592 // 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.
593 if (withBanner) {
594 CFAbsoluteTime at = CFAbsoluteTimeGetCurrent();
595 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFCalendarIdentifierGregorian);
596 if (!calendar) goto after_banner;
597 CFTimeZoneRef tz = CFTimeZoneCopySystem();
598 if (!tz) {
599 CFRelease(calendar);
600 goto after_banner;
601 }
602 CFCalendarSetTimeZone(calendar, tz);
603 CFRelease(tz);
604 int32_t year, month, day, hour, minute, second;
605 Boolean dec = CFCalendarDecomposeAbsoluteTime(calendar, at, "yMdHms", &year, &month, &day, &hour, &minute, &second);
606 CFRelease(calendar);
607 if (!dec) goto after_banner;
608 double atf;
609 int32_t ms = (int32_t)floor(1000.0 * modf(at, &atf));
610 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
611 asprintf(&banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), pthread_mach_thread_np(pthread_self()));
612 asprintf(&thread, "%x", pthread_mach_thread_np(pthread_self()));
613 #elif DEPLOYMENT_TARGET_WINDOWS
614 bannerLen = asprintf(&banner, "%04d-%02d-%02d %02d:%02d:%02d.%03d %s[%d:%x] ", year, month, day, hour, minute, second, ms, *_CFGetProgname(), getpid(), GetCurrentThreadId());
615 asprintf(&thread, "%x", GetCurrentThreadId());
616 #else
617 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());
618 asprintf(&thread, "%x", pthread_self());
619 #endif
620 asprintf(&time, "%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, ms);
621
622 }
623 after_banner:;
624 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
625 uid_t euid;
626 __CFGetUGIDs(&euid, NULL);
627 asprintf(&uid, "%d", euid);
628 aslclient asl = asl_open(NULL, __CFBundleMainID[0] ? __CFBundleMainID : "com.apple.console", ASL_OPT_NO_DELAY);
629 aslmsg msg = asl_new(ASL_TYPE_MSG);
630 asl_set(msg, "CFLog Local Time", time); // not to be documented, not public API
631 asl_set(msg, "CFLog Thread", thread); // not to be documented, not public API
632 asl_set(msg, "ReadUID", uid);
633 static const char *levstr[] = {"0", "1", "2", "3", "4", "5", "6", "7"};
634 asl_set(msg, ASL_KEY_LEVEL, levstr[lev]);
635 asl_set(msg, ASL_KEY_MSG, message);
636 asl_send(asl, msg);
637 asl_free(msg);
638 asl_close(asl);
639 #endif
640 #endif // DEPLOYMENT_TARGET
641
642 if (also_do_stderr()) {
643 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
644 struct iovec v[3];
645 v[0].iov_base = banner;
646 v[0].iov_len = banner ? strlen(banner) : 0;
647 v[1].iov_base = (char *)message;
648 v[1].iov_len = length;
649 v[2].iov_base = "\n";
650 v[2].iov_len = (message[length - 1] != '\n') ? 1 : 0;
651 int nv = (v[0].iov_base ? 1 : 0) + 1 + (v[2].iov_len ? 1 : 0);
652 static CFSpinLock_t lock = CFSpinLockInit;
653 __CFSpinLock(&lock);
654 writev(STDERR_FILENO, v[0].iov_base ? v : v + 1, nv);
655 __CFSpinUnlock(&lock);
656 #elif DEPLOYMENT_TARGET_WINDOWS
657 size_t bufLen = bannerLen + length + 1;
658 char *buf = (char *)malloc(sizeof(char) * bufLen);
659 if (banner) {
660 // Copy the banner into the debug string
661 memmove_s(buf, bufLen, banner, bannerLen);
662
663 // Copy the message into the debug string
664 strcpy_s(buf + bannerLen, bufLen - bannerLen, message);
665 } else {
666 strcpy_s(buf, bufLen, message);
667 }
668 buf[bufLen - 1] = '\0';
669 fprintf_s(stderr, "%s\n", buf);
670 // This Win32 API call only prints when a debugger is active
671 // OutputDebugStringA(buf);
672 free(buf);
673 #else
674 size_t bufLen = bannerLen + length + 1;
675 char *buf = (char *)malloc(sizeof(char) * bufLen);
676 if (banner) {
677 // Copy the banner into the debug string
678 memmove(buf, banner, bannerLen);
679
680 // Copy the message into the debug string
681 strncpy(buf + bannerLen, message, bufLen - bannerLen);
682 } else {
683 strncpy(buf, message, bufLen);
684 }
685 buf[bufLen - 1] = '\0';
686 fprintf(stderr, "%s\n", buf);
687 free(buf);
688 #endif
689 }
690
691 if (thread) free(thread);
692 if (time) free(time);
693 if (banner) free(banner);
694 if (uid) free(uid);
695 }
696
697 CF_EXPORT void _CFLogvEx(CFLogFunc logit, CFStringRef (*copyDescFunc)(void *, const void *), CFDictionaryRef formatOptions, int32_t lev, CFStringRef format, va_list args) {
698 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
699 uintptr_t val = (uintptr_t)_CFGetTSD(__CFTSDKeyIsInCFLog);
700 if (3 < val) return; // allow up to 4 nested invocations
701 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)(val + 1), NULL);
702 #endif
703 CFStringRef str = format ? _CFStringCreateWithFormatAndArgumentsAux(kCFAllocatorSystemDefault, copyDescFunc, formatOptions, (CFStringRef)format, args) : 0;
704 CFIndex blen = str ? CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1 : 0;
705 char *buf = str ? (char *)malloc(blen) : 0;
706 if (str && buf) {
707 Boolean converted = CFStringGetCString(str, buf, blen, kCFStringEncodingUTF8);
708 size_t len = strlen(buf);
709 // silently ignore 0-length or really large messages, and levels outside the valid range
710 if (converted && !(len <= 0 || (1 << 24) < len) && !(lev < ASL_LEVEL_EMERG || ASL_LEVEL_DEBUG < lev)) {
711 (logit ? logit : __CFLogCString)(lev, buf, len, 1);
712 }
713 }
714 if (buf) free(buf);
715 if (str) CFRelease(str);
716 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
717 _CFSetTSD(__CFTSDKeyIsInCFLog, (void *)val, NULL);
718 #endif
719 }
720
721 // 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
722 __private_extern__ void _CFLogSimple(int32_t lev, char *format, ...) {
723 va_list args;
724 va_start(args, format);
725 char formattedMessage[1024];
726 int length = vsnprintf(formattedMessage, 1024, format, args);
727 if (length > 0) {
728 __CFLogCString(lev, formattedMessage, length, 0);
729 }
730 va_end(args);
731 }
732
733 void CFLog(int32_t lev, CFStringRef format, ...) {
734 va_list args;
735 va_start(args, format);
736 _CFLogvEx(NULL, NULL, NULL, lev, format, args);
737 va_end(args);
738 }
739
740
741
742 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
743
744 kern_return_t _CFDiscorporateMemoryAllocate(CFDiscorporateMemory *hm, size_t size, bool purgeable) {
745 kern_return_t ret = KERN_SUCCESS;
746 size = round_page(size);
747 if (0 == size) size = vm_page_size;
748 memset(hm, 0, sizeof(CFDiscorporateMemory));
749 void *addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, VM_MAKE_TAG(0) | (purgeable ? VM_FLAGS_PURGABLE : 0), 0);
750 if ((uintptr_t)addr == -1) {
751 ret = KERN_NO_SPACE;
752 }
753 if (KERN_SUCCESS == ret) {
754 hm->address = (mach_vm_address_t)(uintptr_t)addr;
755 hm->size = (mach_vm_size_t)size;
756 hm->port = MACH_PORT_NULL;
757 hm->corporeal = true;
758 hm->purgeable = purgeable;
759 }
760 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);
761 if (KERN_SUCCESS == ret) hm->corporeal = true;
762 return ret;
763 }
764
765 kern_return_t _CFDiscorporateMemoryDeallocate(CFDiscorporateMemory *hm) {
766 kern_return_t ret1 = KERN_SUCCESS, ret2 = KERN_SUCCESS;
767 if (hm->corporeal) ret1 = mach_vm_deallocate(mach_task_self(), hm->address, hm->size);
768 hm->address = MACH_VM_MIN_ADDRESS;
769 hm->corporeal = false;
770 ret2 = mach_port_deallocate(mach_task_self(), hm->port);
771 hm->port = MACH_PORT_NULL;
772 return ret1 != KERN_SUCCESS ? ret1 : ret2;
773 }
774
775 kern_return_t _CFDiscorporateMemoryDematerialize(CFDiscorporateMemory *hm) {
776 kern_return_t ret = KERN_SUCCESS;
777 if (!hm->corporeal) ret = KERN_INVALID_MEMORY_CONTROL;
778 int state = VM_PURGABLE_VOLATILE;
779 if (KERN_SUCCESS == ret) vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state);
780 if (KERN_SUCCESS == ret) ret = mach_vm_deallocate(mach_task_self(), hm->address, hm->size);
781 if (KERN_SUCCESS == ret) hm->address = MACH_VM_MIN_ADDRESS;
782 if (KERN_SUCCESS == ret) hm->corporeal = false;
783 return ret;
784 }
785
786 kern_return_t _CFDiscorporateMemoryMaterialize(CFDiscorporateMemory *hm) {
787 kern_return_t ret = KERN_SUCCESS;
788 if (hm->corporeal) ret = KERN_INVALID_MEMORY_CONTROL;
789 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);
790 if (KERN_SUCCESS == ret) hm->corporeal = true;
791 int state = VM_PURGABLE_NONVOLATILE;
792 if (KERN_SUCCESS == ret) ret = vm_purgable_control(mach_task_self(), (vm_address_t)hm->address, VM_PURGABLE_SET_STATE, &state);
793 if (KERN_SUCCESS == ret) if (VM_PURGABLE_EMPTY == state) ret = KERN_PROTECTION_FAILURE; // same as VM_PURGABLE_EMPTY
794 return ret;
795 }
796
797 #endif
798
799 #if DEPLOYMENT_TARGET_MACOSX
800
801 #define SUDDEN_TERMINATION_ENABLE_VPROC 1
802
803 #if SUDDEN_TERMINATION_ENABLE_VPROC
804
805 static CFSpinLock_t __CFProcessKillingLock = CFSpinLockInit;
806 static CFIndex __CFProcessKillingDisablingCount = 1;
807 static Boolean __CFProcessKillingWasTurnedOn = false;
808
809 void _CFSuddenTerminationDisable(void) {
810 __CFSpinLock(&__CFProcessKillingLock);
811 __CFProcessKillingDisablingCount++;
812 _vproc_transaction_begin();
813 __CFSpinUnlock(&__CFProcessKillingLock);
814 }
815
816 void _CFSuddenTerminationEnable(void) {
817 // 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.
818 __CFSpinLock(&__CFProcessKillingLock);
819 __CFProcessKillingDisablingCount--;
820 if (__CFProcessKillingDisablingCount==0 && !__CFProcessKillingWasTurnedOn) {
821 _vproc_transactions_enable();
822 __CFProcessKillingWasTurnedOn = true;
823 } else {
824 // 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.
825 if (__CFProcessKillingDisablingCount>=0) {
826 _vproc_transaction_end();
827 } else {
828 CFLog(kCFLogLevelError, CFSTR("-[NSProcessInfo enableSuddenTermination] has been invoked more times than necessary to balance invocations of -[NSProcessInfo disableSuddenTermination]. Ignoring."));
829 }
830 }
831 __CFSpinUnlock(&__CFProcessKillingLock);
832 }
833
834 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) {
835 // 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.
836 __CFSpinLock(&__CFProcessKillingLock);
837 // 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.
838 if (_vproc_transaction_count()==0 && __CFProcessKillingWasTurnedOn) {
839 _exit(exitStatus);
840 }
841 __CFSpinUnlock(&__CFProcessKillingLock);
842 }
843
844 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) {
845 // The user has had their final opportunity to cancel quitting. Exit as soon as the process is clean. Same carefulness as in _CFSuddenTerminationExitIfTerminationEnabled().
846 __CFSpinLock(&__CFProcessKillingLock);
847 if (__CFProcessKillingWasTurnedOn) {
848 _vproc_transaction_try_exit(exitStatus);
849 }
850 __CFSpinUnlock(&__CFProcessKillingLock);
851 }
852
853 size_t _CFSuddenTerminationDisablingCount(void) {
854 // 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.
855 return _vproc_transaction_count() + (__CFProcessKillingWasTurnedOn ? 0 : 1);
856 }
857
858 #else
859
860 #warning Building with vproc sudden termination API disabled.
861
862 static CFSpinLock_t __CFProcessKillingLock = CFSpinLockInit;
863 static size_t __CFProcessKillingDisablingCount = 1;
864 static Boolean __CFProcessExitNextTimeKillingIsEnabled = false;
865 static int32_t __CFProcessExitStatus = 0;
866 static int __CFProcessIsKillableNotifyToken;
867 static Boolean __CFProcessIsKillableNotifyTokenIsFigured = false;
868
869 __private_extern__ void _CFSetSuddenTerminationEnabled(Boolean isEnabled) {
870 if (!__CFProcessIsKillableNotifyTokenIsFigured) {
871 char *notificationName = NULL;
872 asprintf(&notificationName, "com.apple.isKillable.%i", getpid());
873 uint32_t notifyResult = notify_register_check(notificationName, &__CFProcessIsKillableNotifyToken);
874 if (notifyResult != NOTIFY_STATUS_OK) {
875 CFLog(kCFLogLevelError, CFSTR("%s: notify_register_check() returned %i."), __PRETTY_FUNCTION__, notifyResult);
876 }
877 free(notificationName);
878 __CFProcessIsKillableNotifyTokenIsFigured = true;
879 }
880 uint32_t notifyResult = notify_set_state(__CFProcessIsKillableNotifyToken, isEnabled);
881 if (notifyResult != NOTIFY_STATUS_OK) {
882 CFLog(kCFLogLevelError, CFSTR("%s: notify_set_state() returned %i"), __PRETTY_FUNCTION__, notifyResult);
883 }
884 }
885
886 void _CFSuddenTerminationDisable(void) {
887 __CFSpinLock(&__CFProcessKillingLock);
888 if (__CFProcessKillingDisablingCount == 0) {
889 _CFSetSuddenTerminationEnabled(false);
890 }
891 __CFProcessKillingDisablingCount++;
892 __CFSpinUnlock(&__CFProcessKillingLock);
893 }
894
895 void _CFSuddenTerminationEnable(void) {
896 __CFSpinLock(&__CFProcessKillingLock);
897 __CFProcessKillingDisablingCount--;
898 if (__CFProcessKillingDisablingCount == 0) {
899 if (__CFProcessExitNextTimeKillingIsEnabled) {
900 _exit(__CFProcessExitStatus);
901 } else {
902 _CFSetSuddenTerminationEnabled(true);
903 }
904 }
905 __CFSpinUnlock(&__CFProcessKillingLock);
906 }
907
908 void _CFSuddenTerminationExitIfTerminationEnabled(int exitStatus) {
909 __CFSpinLock(&__CFProcessKillingLock);
910 if (__CFProcessKillingDisablingCount == 0) {
911 _exit(exitStatus);
912 }
913 __CFSpinUnlock(&__CFProcessKillingLock);
914 }
915
916 void _CFSuddenTerminationExitWhenTerminationEnabled(int exitStatus) {
917 __CFSpinLock(&__CFProcessKillingLock);
918 if (__CFProcessKillingDisablingCount == 0) {
919 _exit(exitStatus);
920 } else {
921 __CFProcessExitNextTimeKillingIsEnabled = YES;
922 __CFProcessExitStatus = exitStatus;
923 }
924 __CFSpinUnlock(&__CFProcessKillingLock);
925 }
926
927 size_t _CFSuddenTerminationDisablingCount(void) {
928 return __CFProcessKillingDisablingCount;
929 }
930
931 #endif
932
933 #endif
934
935 #if 0
936 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
937
938 typedef void (^ThrottleTypeA)(void); // allows calls per nanoseconds
939 typedef void (^ThrottleTypeB)(uint64_t amt); // allows amount per nanoseconds
940
941 __private_extern__ ThrottleTypeA __CFCreateThrottleTypeA(uint16_t calls, uint64_t nanoseconds) {
942 struct mach_timebase_info info;
943 mach_timebase_info(&info);
944 uint64_t period = nanoseconds / info.numer * info.denom;
945
946 if (0 == calls || 0 == period) return NULL;
947
948 __block OSSpinLock b_lock = OS_SPINLOCK_INIT;
949 __block uint64_t b_values[calls];
950 __block uint64_t *b_oldest = b_values;
951 memset(b_values, 0, sizeof(b_values));
952
953 return Block_copy(^{
954 uint64_t curr_time = mach_absolute_time();
955 OSSpinLockLock(&b_lock);
956 uint64_t next_time = *b_oldest + period;
957 *b_oldest = (curr_time < next_time) ? next_time : curr_time;
958 b_oldest++;
959 if (b_values + calls <= b_oldest) b_oldest = b_values;
960 OSSpinLockUnlock(&b_lock);
961 if (curr_time < next_time) {
962 mach_wait_until(next_time);
963 }
964 });
965 }
966
967 __private_extern__ ThrottleTypeB __CFCreateThrottleTypeB(uint64_t amount, uint64_t nanoseconds) {
968 struct mach_timebase_info info;
969 mach_timebase_info(&info);
970 uint64_t period = nanoseconds / info.numer * info.denom;
971
972 if (0 == amount || 0 == period) return NULL;
973
974 __block OSSpinLock b_lock = OS_SPINLOCK_INIT;
975 __block uint64_t b_sum = 0ULL;
976 __block uint16_t b_num_values = 8;
977 __block uint64_t *b_values = calloc(b_num_values, 2 * sizeof(uint64_t));
978 __block uint64_t *b_oldest = b_values;
979
980 return Block_copy(^(uint64_t amt){
981 OSSpinLockLock(&b_lock);
982 // unimplemented
983 OSSpinLockUnlock(&b_lock);
984 });
985 }
986
987 #endif
988 #endif
989
990 #pragma mark File Reading
991
992 #include <sys/stat.h>
993 #include <fcntl.h>
994 #include <errno.h>
995 #if DEPLOYMENT_TARGET_WINDOWS
996 #include <io.h>
997 #include <direct.h>
998 #define close _close
999 #define write _write
1000 #define read _read
1001 #define open _NS_open
1002 #define stat _NS_stat
1003 #define fstat _fstat
1004 #define statinfo _stat
1005
1006 #define mach_task_self() 0
1007
1008 #else
1009 #define statinfo stat
1010 #endif
1011
1012 static CFErrorRef _CFErrorWithFilePathCodeDomain(CFStringRef domain, CFIndex code, CFStringRef path) {
1013 CFStringRef key = CFSTR("NSFilePath");
1014 CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorSystemDefault, (const void **)&key, (const void **)&path, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1015 CFErrorRef result = CFErrorCreate(kCFAllocatorSystemDefault, domain, code, userInfo);
1016 CFRelease(userInfo);
1017 return result;
1018 }
1019
1020 // Caller is responsible for freeing memory. munmap() if map == true, else malloc().
1021 __private_extern__ Boolean _CFReadMappedFromFile(CFStringRef path, Boolean map, Boolean uncached, void **outBytes, CFIndex *outLength, CFErrorRef *errorPtr) {
1022 void *bytes = NULL;
1023 unsigned long length;
1024 char cpath[CFMaxPathSize];
1025 if (!CFStringGetFileSystemRepresentation(path, cpath, CFMaxPathSize)) {
1026 // TODO: real error codes
1027 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainCocoa, -1, path);
1028 return false;
1029 }
1030
1031 struct statinfo statBuf;
1032 int32_t fd = -1;
1033
1034 fd = open(cpath, O_RDONLY|CF_OPENFLGS, 0666);
1035 if (fd < 0) {
1036 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, errno, path);
1037 return false;
1038 }
1039 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
1040 if (uncached) (void)fcntl(fd, F_NOCACHE, 1); // Non-zero arg turns off caching; we ignore error as uncached is just a hint
1041 #endif
1042 if (fstat(fd, &statBuf) < 0) {
1043 int32_t savederrno = errno;
1044 close(fd);
1045 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path);
1046 return false;
1047 }
1048 if ((statBuf.st_mode & S_IFMT) != S_IFREG) {
1049 close(fd);
1050 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, EACCES, path);
1051 return false;
1052 }
1053 if (statBuf.st_size < 0LL) { // too small
1054 close(fd);
1055 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, ENOMEM, path);
1056 return false;
1057 }
1058 #if __LP64__
1059 #else
1060 if (statBuf.st_size > (1LL << 31)) { // refuse to do more than 2GB
1061 close(fd);
1062 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, EFBIG, path);
1063 return false;
1064 }
1065 #endif
1066
1067 if (0LL == statBuf.st_size) {
1068 bytes = malloc(8); // don't return constant string -- it's freed!
1069 length = 0;
1070 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
1071 } else if (map) {
1072 if((void *)-1 == (bytes = mmap(0, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0))) {
1073 int32_t savederrno = errno;
1074 close(fd);
1075 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path);
1076 return false;
1077 }
1078 length = (unsigned long)statBuf.st_size;
1079 } else {
1080 bytes = malloc(statBuf.st_size);
1081 if (bytes == NULL) {
1082 close(fd);
1083 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, ENOMEM, path);
1084 return false;
1085 }
1086 size_t numBytesRemaining = (size_t)statBuf.st_size;
1087 void *readLocation = bytes;
1088 while (numBytesRemaining > 0) {
1089 size_t numBytesRequested = (numBytesRemaining < (1LL << 31)) ? numBytesRemaining : ((1LL << 31) - 1); // This loop is basically a workaround for 4870206
1090 ssize_t numBytesRead = read(fd, readLocation, numBytesRequested);
1091 if (numBytesRead <= 0) {
1092 if (numBytesRead < 0) {
1093 int32_t savederrno = errno;
1094 free(bytes);
1095 close(fd);
1096 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, savederrno, path);
1097 bytes = NULL;
1098 return false;
1099 } else {
1100 // This is a bizarre case; 0 bytes read. Might indicate end-of-file?
1101 break;
1102 }
1103 } else {
1104 readLocation += numBytesRead;
1105 numBytesRemaining -= numBytesRead;
1106 }
1107 }
1108 length = (unsigned long)statBuf.st_size - numBytesRemaining;
1109 }
1110 #else
1111 } else {
1112 bytes = malloc(statBuf.st_size);
1113 DWORD numBytesRead;
1114 if (!ReadFile((HANDLE)_get_osfhandle(fd), bytes, statBuf.st_size, &numBytesRead, NULL)) {
1115 DWORD lastError = GetLastError();
1116 if (errorPtr) *errorPtr = _CFErrorWithFilePathCodeDomain(kCFErrorDomainPOSIX, lastError, path);
1117 free(bytes);
1118 close(fd);
1119 errno = lastError;
1120 bytes = NULL;
1121 return false;
1122 }
1123 length = numBytesRead;
1124 }
1125 #endif
1126 close(fd);
1127 *outBytes = bytes;
1128 *outLength = length;
1129 return true;
1130 }