]> git.saurik.com Git - apple/cf.git/blob - CFUtilities.c
CF-1151.16.tar.gz
[apple/cf.git] / CFUtilities.c
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-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(&notificationName, "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 }