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