]> git.saurik.com Git - apple/cf.git/blob - CFXMLPreferencesDomain.c
CF-476.13.tar.gz
[apple/cf.git] / CFXMLPreferencesDomain.c
1 /*
2 * Copyright (c) 2008 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 /* CFXMLPreferencesDomain.c
24 Copyright 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Chris Parker
26 */
27
28 #include <CoreFoundation/CFPreferences.h>
29 #include <CoreFoundation/CFURLAccess.h>
30 #include <CoreFoundation/CFPropertyList.h>
31 #include <CoreFoundation/CFNumber.h>
32 #include <CoreFoundation/CFDate.h>
33 #include "CFInternal.h"
34 #include <time.h>
35 #if DEPLOYMENT_TARGET_MACOSX
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <sys/stat.h>
39 #include <mach/mach.h>
40 #include <mach/mach_syscalls.h>
41 #endif
42
43 Boolean __CFPreferencesShouldWriteXML(void);
44
45 typedef struct {
46 CFMutableDictionaryRef _domainDict; // Current value of the domain dictionary
47 CFMutableArrayRef _dirtyKeys; // The array of keys which must be synchronized
48 CFAbsoluteTime _lastReadTime; // The last time we synchronized with the disk
49 CFSpinLock_t _lock; // Lock for accessing fields in the domain
50 Boolean _isWorldReadable; // HACK - this is because we have no good way to propogate the kCFPreferencesAnyUser information from the upper level CFPreferences routines REW, 1/13/00
51 char _padding[3];
52 } _CFXMLPreferencesDomain;
53
54 static void *createXMLDomain(CFAllocatorRef allocator, CFTypeRef context);
55 static void freeXMLDomain(CFAllocatorRef allocator, CFTypeRef context, void *tDomain);
56 static CFTypeRef fetchXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key);
57 static void writeXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key, CFTypeRef value);
58 static Boolean synchronizeXMLDomain(CFTypeRef context, void *xmlDomain);
59 static void getXMLKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *xmlDomain, void **buf[], CFIndex *numKeyValuePairs);
60 static CFDictionaryRef copyXMLDomainDictionary(CFTypeRef context, void *domain);
61 static void setXMLDomainIsWorldReadable(CFTypeRef context, void *domain, Boolean isWorldReadable);
62
63 __private_extern__ const _CFPreferencesDomainCallBacks __kCFXMLPropertyListDomainCallBacks = {createXMLDomain, freeXMLDomain, fetchXMLValue, writeXMLValue, synchronizeXMLDomain, getXMLKeysAndValues, copyXMLDomainDictionary, setXMLDomainIsWorldReadable};
64
65 // Directly ripped from Foundation....
66 static void __CFMilliSleep(uint32_t msecs) {
67 #if defined(__svr4__) || defined(__hpux__)
68 sleep((msecs + 900) / 1000);
69 #elif DEPLOYMENT_TARGET_MACOSX
70 struct timespec input;
71 input.tv_sec = msecs / 1000;
72 input.tv_nsec = (msecs - input.tv_sec * 1000) * 1000000;
73 nanosleep(&input, NULL);
74 #else
75 #error Dont know how to define sleep for this platform
76 #endif
77 }
78
79 static CFSpinLock_t _propDictLock = CFSpinLockInit; // Annoying that we need this, but otherwise we have a multithreading risk
80
81 CF_INLINE CFDictionaryRef URLPropertyDictForPOSIXMode(SInt32 mode) {
82 static CFMutableDictionaryRef _propertyDict = NULL;
83 CFNumberRef num = CFNumberCreate(__CFPreferencesAllocator(), kCFNumberSInt32Type, &mode);
84 __CFSpinLock(&_propDictLock);
85 if (!_propertyDict) {
86 _propertyDict = CFDictionaryCreateMutable(__CFPreferencesAllocator(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
87 }
88 CFDictionarySetValue(_propertyDict, kCFURLFilePOSIXMode, num);
89 CFRelease(num);
90 return _propertyDict;
91 }
92
93 CF_INLINE void URLPropertyDictRelease(void) {
94 __CFSpinUnlock(&_propDictLock);
95 }
96
97 // Asssumes caller already knows the directory doesn't exist.
98 static Boolean _createDirectory(CFURLRef dirURL, Boolean worldReadable) {
99 CFAllocatorRef alloc = __CFPreferencesAllocator();
100 CFURLRef parentURL = CFURLCreateCopyDeletingLastPathComponent(alloc, dirURL);
101 CFBooleanRef val = (CFBooleanRef) CFURLCreatePropertyFromResource(alloc, parentURL, kCFURLFileExists, NULL);
102 Boolean parentExists = (val && CFBooleanGetValue(val));
103 SInt32 mode;
104 Boolean result;
105 if (val) CFRelease(val);
106 if (!parentExists) {
107 CFStringRef path = CFURLCopyPath(parentURL);
108 if (!CFEqual(path, CFSTR("/"))) {
109 _createDirectory(parentURL, worldReadable);
110 val = (CFBooleanRef) CFURLCreatePropertyFromResource(alloc, parentURL, kCFURLFileExists, NULL);
111 parentExists = (val && CFBooleanGetValue(val));
112 if (val) CFRelease(val);
113 }
114 CFRelease(path);
115 }
116 if (parentURL) CFRelease(parentURL);
117 if (!parentExists) return false;
118
119 #if DEPLOYMENT_TARGET_MACOSX
120 mode = worldReadable ? S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH : S_IRWXU;
121 #else
122 mode = 0666;
123 #endif
124
125 result = CFURLWriteDataAndPropertiesToResource(dirURL, (CFDataRef)dirURL, URLPropertyDictForPOSIXMode(mode), NULL);
126 URLPropertyDictRelease();
127 return result;
128 }
129
130
131 /* XML - context is the CFURL where the property list is stored on disk; domain is an _CFXMLPreferencesDomain */
132 static void *createXMLDomain(CFAllocatorRef allocator, CFTypeRef context) {
133 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain*) CFAllocatorAllocate(allocator, sizeof(_CFXMLPreferencesDomain), 0);
134 domain->_lastReadTime = 0.0;
135 domain->_domainDict = NULL;
136 domain->_dirtyKeys = CFArrayCreateMutable(allocator, 0, & kCFTypeArrayCallBacks);
137 const CFSpinLock_t lock = CFSpinLockInit;
138 domain->_lock = lock;
139 domain->_isWorldReadable = false;
140 return domain;
141 }
142
143 static void freeXMLDomain(CFAllocatorRef allocator, CFTypeRef context, void *tDomain) {
144 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)tDomain;
145 if (domain->_domainDict) CFRelease(domain->_domainDict);
146 if (domain->_dirtyKeys) CFRelease(domain->_dirtyKeys);
147 CFAllocatorDeallocate(allocator, domain);
148 }
149
150 // Assumes the domain has already been locked
151 static void _loadXMLDomainIfStale(CFURLRef url, _CFXMLPreferencesDomain *domain) {
152 CFAllocatorRef alloc = __CFPreferencesAllocator();
153 int idx;
154 if (domain->_domainDict) {
155 CFDateRef modDate;
156 CFAbsoluteTime modTime;
157 CFURLRef testURL = url;
158
159 if (CFDictionaryGetCount(domain->_domainDict) == 0) {
160 // domain never existed; check the parent directory, not the child
161 testURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR(".."), kCFURLPOSIXPathStyle, true, url);
162 }
163
164 modDate = (CFDateRef )CFURLCreatePropertyFromResource(alloc, testURL, kCFURLFileLastModificationTime, NULL);
165 modTime = modDate ? CFDateGetAbsoluteTime(modDate) : 0.0;
166
167 // free before possible return. we can test non-NULL of modDate but don't depend on contents after this.
168 if (testURL != url) CFRelease(testURL);
169 if (modDate) CFRelease(modDate);
170
171 if (modDate != NULL && modTime < domain->_lastReadTime) { // We're up-to-date
172 return;
173 }
174 }
175
176
177 // We're out-of-date; destroy domainDict and reload
178 if (domain->_domainDict) {
179 CFRelease(domain->_domainDict);
180 domain->_domainDict = NULL;
181 }
182
183 // We no longer lock on read; instead, we assume parse failures are because someone else is writing the file, and just try to parse again. If we fail 3 times in a row, we assume the file is corrupted. REW, 7/13/99
184
185 for (idx = 0; idx < 3; idx ++) {
186 CFDataRef data;
187 if (!CFURLCreateDataAndPropertiesFromResource(alloc, url, &data, NULL, NULL, NULL) || !data) {
188 // Either a file system error (so we can't read the file), or an empty (or perhaps non-existant) file
189 domain->_domainDict = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
190 break;
191 } else {
192 CFTypeRef pList = CFPropertyListCreateFromXMLData(alloc, data, kCFPropertyListImmutable, NULL);
193 CFRelease(data);
194 if (pList && CFGetTypeID(pList) == CFDictionaryGetTypeID()) {
195 domain->_domainDict = CFDictionaryCreateMutableCopy(alloc, 0, (CFDictionaryRef)pList);
196 CFRelease(pList);
197 break;
198 } else if (pList) {
199 CFRelease(pList);
200 }
201 // Assume the file is being written; sleep for a short time (to allow the write to complete) then re-read
202 __CFMilliSleep(150);
203 }
204 }
205 if (!domain->_domainDict) {
206 // Failed to ever load
207 domain->_domainDict = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
208 }
209 domain->_lastReadTime = CFAbsoluteTimeGetCurrent();
210 }
211
212 static CFTypeRef fetchXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key) {
213 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
214 CFTypeRef result;
215
216 // Never reload if we've looked at the file system within the last 5 seconds.
217 __CFSpinLock(&domain->_lock);
218 if (domain->_domainDict == NULL) _loadXMLDomainIfStale((CFURLRef )context, domain);
219 result = CFDictionaryGetValue(domain->_domainDict, key);
220 if (result) CFRetain(result);
221 __CFSpinUnlock(&domain->_lock);
222
223 return result;
224 }
225
226
227 #if DEPLOYMENT_TARGET_MACOSX
228 #include <sys/fcntl.h>
229
230 /* __CFWriteBytesToFileWithAtomicity is a "safe save" facility. Write the bytes using the specified mode on the file to the provided URL. If the atomic flag is true, try to do it in a fashion that will enable a safe save.
231 */
232 static Boolean __CFWriteBytesToFileWithAtomicity(CFURLRef url, const void *bytes, int length, SInt32 mode, Boolean atomic) {
233 int fd = -1;
234 char auxPath[CFMaxPathSize + 16];
235 char cpath[CFMaxPathSize];
236 uid_t owner = getuid();
237 gid_t group = getgid();
238 Boolean writingFileAsRoot = ((getuid() != geteuid()) && (geteuid() == 0));
239
240 if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)cpath, CFMaxPathSize)) {
241 return false;
242 }
243
244 if (-1 == mode || writingFileAsRoot) {
245 struct stat statBuf;
246 if (0 == stat(cpath, &statBuf)) {
247 mode = statBuf.st_mode;
248 owner = statBuf.st_uid;
249 group = statBuf.st_gid;
250 } else {
251 mode = 0664;
252 if (writingFileAsRoot && (0 == strncmp(cpath, "/Library/Preferences", 20))) {
253 owner = geteuid();
254 group = 80;
255 }
256 }
257 }
258
259 if (atomic) {
260 CFURLRef dir = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url);
261 CFURLRef tempFile = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, dir, CFSTR("cf#XXXXX"), false);
262 CFRelease(dir);
263 if (!CFURLGetFileSystemRepresentation(tempFile, true, (uint8_t *)auxPath, CFMaxPathSize)) {
264 CFRelease(tempFile);
265 return false;
266 }
267 CFRelease(tempFile);
268 fd = mkstemp(auxPath);
269 } else {
270 fd = open(cpath, O_WRONLY|O_CREAT|O_TRUNC, mode);
271 }
272
273 if (fd < 0) return false;
274
275 if (length && (write(fd, bytes, length) != length || fsync(fd) < 0)) {
276 int saveerr = thread_errno();
277 close(fd);
278 if (atomic)
279 unlink(auxPath);
280 thread_set_errno(saveerr);
281 return false;
282 }
283
284 close(fd);
285
286 if (atomic) {
287 // preserve the mode as passed in originally
288 chmod(auxPath, mode);
289
290 if (0 != rename(auxPath, cpath)) {
291 unlink(auxPath);
292 return false;
293 }
294
295 // If the file was renamed successfully and we wrote it as root we need to reset the owner & group as they were.
296 if (writingFileAsRoot) {
297 chown(cpath, owner, group);
298 }
299 }
300 return true;
301 }
302 #endif
303
304 // domain should already be locked.
305 static Boolean _writeXMLFile(CFURLRef url, CFMutableDictionaryRef dict, Boolean isWorldReadable, Boolean *tryAgain) {
306 Boolean success = false;
307 CFAllocatorRef alloc = __CFPreferencesAllocator();
308 *tryAgain = false;
309 if (CFDictionaryGetCount(dict) == 0) {
310 // Destroy the file
311 CFBooleanRef val = (CFBooleanRef) CFURLCreatePropertyFromResource(alloc, url, kCFURLFileExists, NULL);
312 if (val && CFBooleanGetValue(val)) {
313 success = CFURLDestroyResource(url, NULL);
314 } else {
315 success = true;
316 }
317 if (val) CFRelease(val);
318 } else {
319 CFPropertyListFormat desiredFormat = __CFPreferencesShouldWriteXML() ? kCFPropertyListXMLFormat_v1_0 : kCFPropertyListBinaryFormat_v1_0;
320 CFWriteStreamRef binStream = CFWriteStreamCreateWithAllocatedBuffers(alloc, alloc);
321 CFWriteStreamOpen(binStream);
322 CFPropertyListWriteToStream(dict, binStream, desiredFormat, NULL);
323 CFWriteStreamClose(binStream);
324 CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty(binStream, kCFStreamPropertyDataWritten);
325 CFRelease(binStream);
326 if (data) {
327 SInt32 mode;
328 #if DEPLOYMENT_TARGET_MACOSX
329 mode = isWorldReadable ? S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH : S_IRUSR|S_IWUSR;
330 #else
331 mode = 0666;
332 #endif
333 #if DEPLOYMENT_TARGET_MACOSX
334 { // Try quick atomic way first, then fallback to slower ways and error cases
335 CFStringRef scheme = CFURLCopyScheme(url);
336 if (!scheme) {
337 *tryAgain = false;
338 CFRelease(data);
339 return false;
340 } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
341 SInt32 length = CFDataGetLength(data);
342 const void *bytes = (0 == length) ? (const void *)"" : CFDataGetBytePtr(data);
343 Boolean atomicWriteSuccess = __CFWriteBytesToFileWithAtomicity(url, bytes, length, mode, true);
344 if (atomicWriteSuccess) {
345 CFRelease(scheme);
346 *tryAgain = false;
347 CFRelease(data);
348 return true;
349 }
350 if (!atomicWriteSuccess && thread_errno() == ENOSPC) {
351 CFRelease(scheme);
352 *tryAgain = false;
353 CFRelease(data);
354 return false;
355 }
356 }
357 CFRelease(scheme);
358 }
359 #endif
360 success = CFURLWriteDataAndPropertiesToResource(url, data, URLPropertyDictForPOSIXMode(mode), NULL);
361 URLPropertyDictRelease();
362 if (success) {
363 CFDataRef readData;
364 if (!CFURLCreateDataAndPropertiesFromResource(alloc, url, &readData, NULL, NULL, NULL) || !CFEqual(readData, data)) {
365 success = false;
366 *tryAgain = true;
367 }
368 if (readData) CFRelease(readData);
369 } else {
370 CFBooleanRef val = (CFBooleanRef) CFURLCreatePropertyFromResource(alloc, url, kCFURLFileExists, NULL);
371 if (!val || !CFBooleanGetValue(val)) {
372 CFURLRef tmpURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("."), kCFURLPOSIXPathStyle, true, url); // Just "." because url is not a directory URL
373 CFURLRef parentURL = tmpURL ? CFURLCopyAbsoluteURL(tmpURL) : NULL;
374 if (tmpURL) CFRelease(tmpURL);
375 if (val) CFRelease(val);
376 val = (CFBooleanRef) CFURLCreatePropertyFromResource(alloc, parentURL, kCFURLFileExists, NULL);
377 if ((!val || !CFBooleanGetValue(val)) && _createDirectory(parentURL, isWorldReadable)) {
378 // parent directory didn't exist; now it does; try again to write
379 success = CFURLWriteDataAndPropertiesToResource(url, data, URLPropertyDictForPOSIXMode(mode), NULL);
380 URLPropertyDictRelease();
381 if (success) {
382 CFDataRef rdData;
383 if (!CFURLCreateDataAndPropertiesFromResource(alloc, url, &rdData, NULL, NULL, NULL) || !CFEqual(rdData, data)) {
384 success = false;
385 *tryAgain = true;
386 }
387 if (rdData) CFRelease(rdData);
388 }
389
390 }
391 if (parentURL) CFRelease(parentURL);
392 }
393 if (val) CFRelease(val);
394 }
395 CFRelease(data);
396 } else {
397 // ??? This should never happen
398 CFLog(__kCFLogAssertion, CFSTR("Could not generate XML data for property list"));
399 success = false;
400 }
401 }
402 return success;
403 }
404
405 static void writeXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key, CFTypeRef value) {
406 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
407 const void *existing = NULL;
408
409 __CFSpinLock(&domain->_lock);
410 if (domain->_domainDict == NULL) {
411 _loadXMLDomainIfStale((CFURLRef )context, domain);
412 }
413
414 // check to see if the value is the same
415 // if (1) the key is present AND value is !NULL and equal to existing, do nothing, or
416 // if (2) the key is not present AND value is NULL, do nothing
417 // these things are no-ops, and should not dirty the domain
418 if (CFDictionaryGetValueIfPresent(domain->_domainDict, key, &existing)) {
419 if (NULL != value && (existing == value || CFEqual(existing, value))) {
420 __CFSpinUnlock(&domain->_lock);
421 return;
422 }
423 } else {
424 if (NULL == value) {
425 __CFSpinUnlock(&domain->_lock);
426 return;
427 }
428 }
429
430 // We must append first so key gets another retain (in case we're
431 // about to remove it from the dictionary, and that's the sole reference)
432 // This should be a set not an array.
433 if (!CFArrayContainsValue(domain->_dirtyKeys, CFRangeMake(0, CFArrayGetCount(domain->_dirtyKeys)), key)) {
434 CFArrayAppendValue(domain->_dirtyKeys, key);
435 }
436 if (value) {
437 // Must copy for two reasons - we don't want mutable objects in the cache, and we don't want objects allocated from a different allocator in the cache.
438 CFTypeRef newValue = CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), value, kCFPropertyListImmutable);
439 CFDictionarySetValue(domain->_domainDict, key, newValue);
440 CFRelease(newValue);
441 } else {
442 CFDictionaryRemoveValue(domain->_domainDict, key);
443 }
444 __CFSpinUnlock(&domain->_lock);
445 }
446
447 static void getXMLKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *xmlDomain, void **buf[], CFIndex *numKeyValuePairs) {
448 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
449 CFIndex count;
450 __CFSpinLock(&domain->_lock);
451 if (!domain->_domainDict) {
452 _loadXMLDomainIfStale((CFURLRef )context, domain);
453 }
454 count = CFDictionaryGetCount(domain->_domainDict);
455 if (buf) {
456 void **values;
457 if (count <= *numKeyValuePairs) {
458 values = *buf + count;
459 CFDictionaryGetKeysAndValues(domain->_domainDict, (const void **)*buf, (const void **)values);
460 } else if (alloc != kCFAllocatorNull) {
461 *buf = (void**) CFAllocatorReallocate(alloc, (*buf ? *buf : NULL), count * 2 * sizeof(void *), 0);
462 if (*buf) {
463 values = *buf + count;
464 CFDictionaryGetKeysAndValues(domain->_domainDict, (const void **)*buf, (const void **)values);
465 }
466 }
467 }
468 *numKeyValuePairs = count;
469 __CFSpinUnlock(&domain->_lock);
470 }
471
472 static CFDictionaryRef copyXMLDomainDictionary(CFTypeRef context, void *xmlDomain) {
473 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
474 CFDictionaryRef result;
475
476 __CFSpinLock(&domain->_lock);
477 if(!domain->_domainDict) {
478 _loadXMLDomainIfStale((CFURLRef)context, domain);
479 }
480
481 result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), domain->_domainDict, kCFPropertyListImmutable);
482
483 __CFSpinUnlock(&domain->_lock);
484 return result;
485 }
486
487
488 static void setXMLDomainIsWorldReadable(CFTypeRef context, void *domain, Boolean isWorldReadable) {
489 ((_CFXMLPreferencesDomain *)domain)->_isWorldReadable = isWorldReadable;
490 }
491
492 static Boolean synchronizeXMLDomain(CFTypeRef context, void *xmlDomain) {
493 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
494 CFMutableDictionaryRef cachedDict;
495 CFMutableArrayRef changedKeys;
496 SInt32 idx, count;
497 Boolean success, tryAgain;
498
499 __CFSpinLock(&domain->_lock);
500 cachedDict = domain->_domainDict;
501 changedKeys = domain->_dirtyKeys;
502 count = CFArrayGetCount(changedKeys);
503
504 if (count == 0) {
505 // no changes were made to this domain; just remove it from the cache to guarantee it will be taken from disk next access
506 if (cachedDict) {
507 CFRelease(cachedDict);
508 domain->_domainDict = NULL;
509 }
510 __CFSpinUnlock(&domain->_lock);
511 return true;
512 }
513
514 domain->_domainDict = NULL; // This forces a reload. Note that we now have a retain on cachedDict
515 do {
516 _loadXMLDomainIfStale((CFURLRef )context, domain);
517 // now cachedDict holds our changes; domain->_domainDict has the latest version from the disk
518 for (idx = 0; idx < count; idx ++) {
519 CFStringRef key = (CFStringRef) CFArrayGetValueAtIndex(changedKeys, idx);
520 CFTypeRef value = CFDictionaryGetValue(cachedDict, key);
521 if (value)
522 CFDictionarySetValue(domain->_domainDict, key, value);
523 else
524 CFDictionaryRemoveValue(domain->_domainDict, key);
525 }
526 success = _writeXMLFile((CFURLRef )context, domain->_domainDict, domain->_isWorldReadable, &tryAgain);
527 if (tryAgain) {
528 __CFMilliSleep((((uint32_t)__CFReadTSR() & 0xf) + 1) * 50);
529 }
530 } while (tryAgain);
531 CFRelease(cachedDict);
532 if (success) {
533 CFArrayRemoveAllValues(domain->_dirtyKeys);
534 }
535 domain->_lastReadTime = CFAbsoluteTimeGetCurrent();
536 __CFSpinUnlock(&domain->_lock);
537 return success;
538 }
539
540