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