]> git.saurik.com Git - apple/cf.git/blame - Preferences.subproj/CFXMLPreferencesDomain.c
CF-368.28.tar.gz
[apple/cf.git] / Preferences.subproj / CFXMLPreferencesDomain.c
CommitLineData
d8925383
A
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
43Boolean __CFPreferencesShouldWriteXML(void);
44
45typedef 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
54static void *createXMLDomain(CFAllocatorRef allocator, CFTypeRef context);
55static void freeXMLDomain(CFAllocatorRef allocator, CFTypeRef context, void *tDomain);
56static CFTypeRef fetchXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key);
57static void writeXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key, CFTypeRef value);
58static Boolean synchronizeXMLDomain(CFTypeRef context, void *xmlDomain);
59static void getXMLKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *xmlDomain, void **buf[], CFIndex *numKeyValuePairs);
60static CFDictionaryRef copyXMLDomainDictionary(CFTypeRef context, void *domain);
61static 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....
66static 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
81static CFSpinLock_t _propDictLock = 0; // Annoying that we need this, but otherwise we have a multithreading risk
82
83CF_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
95CF_INLINE void URLPropertyDictRelease(void) {
96 __CFSpinUnlock(&_propDictLock);
97}
98
99// Asssumes caller already knows the directory doesn't exist.
100static 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 */
130static 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
140static 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
148static 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
209static 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.
233static 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 */
288static 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];
4c91a73d
A
292 uid_t owner = getuid();
293 gid_t group = getgid();
294 Boolean writingFileAsRoot = ((getuid() != geteuid()) && (geteuid() == 0));
d8925383
A
295
296 if (!CFURLGetFileSystemRepresentation(url, true, cpath, CFMaxPathSize)) {
297 return false;
298 }
4c91a73d
A
299
300 if (-1 == mode || writingFileAsRoot) {
d8925383 301 struct stat statBuf;
4c91a73d
A
302 if (0 == stat(cpath, &statBuf)) {
303 mode = statBuf.st_mode;
304 owner = statBuf.st_uid;
305 group = statBuf.st_gid;
306 } else {
307 mode = 0664;
308 if (writingFileAsRoot && (0 == strncmp(cpath, "/Library/Preferences", 20))) {
309 owner = geteuid();
310 group = 80;
311 }
312 }
d8925383 313 }
4c91a73d 314
d8925383
A
315 if (atomic) {
316 CFURLRef dir = CFURLCreateCopyDeletingLastPathComponent(NULL, url);
317 CFURLRef tempFile = CFURLCreateCopyAppendingPathComponent(NULL, dir, CFSTR("cf#XXXXX"), false);
318 CFRelease(dir);
319 if (!CFURLGetFileSystemRepresentation(tempFile, true, auxPath, CFMaxPathSize)) {
320 CFRelease(tempFile);
321 return false;
322 }
323 CFRelease(tempFile);
324 fd = mkstemp(auxPath);
325 } else {
326 fd = open(cpath, O_WRONLY|O_CREAT|O_TRUNC, mode);
327 }
4c91a73d 328
d8925383 329 if (fd < 0) return false;
4c91a73d 330
d8925383
A
331 if (length && (write(fd, bytes, length) != length || fsync(fd) < 0)) {
332 int saveerr = thread_errno();
333 close(fd);
334 if (atomic)
335 unlink(auxPath);
336 thread_set_errno(saveerr);
337 return false;
338 }
4c91a73d 339
d8925383 340 close(fd);
4c91a73d 341
d8925383
A
342 if (atomic) {
343 // preserve the mode as passed in originally
344 chmod(auxPath, mode);
345
346 if (0 != rename(auxPath, cpath)) {
347 unlink(auxPath);
348 return false;
349 }
4c91a73d
A
350
351 // If the file was renamed successfully and we wrote it as root we need to reset the owner & group as they were.
352 if (writingFileAsRoot) {
353 chown(cpath, owner, group);
354 }
d8925383
A
355 }
356 return true;
357}
358#endif
359
360// domain should already be locked.
361static Boolean _writeXMLFile(CFURLRef url, CFMutableDictionaryRef dict, Boolean isWorldReadable, Boolean *tryAgain) {
362 Boolean success = false;
363 CFAllocatorRef alloc = __CFPreferencesAllocator();
364 *tryAgain = false;
365 if (CFDictionaryGetCount(dict) == 0) {
366 // Destroy the file
367 CFBooleanRef val = CFURLCreatePropertyFromResource(alloc, url, kCFURLFileExists, NULL);
368 if (val && CFBooleanGetValue(val)) {
369 success = CFURLDestroyResource(url, NULL);
370 } else {
371 success = true;
372 }
373 if (val) CFRelease(val);
374 } else {
375 CFPropertyListFormat desiredFormat = __CFPreferencesShouldWriteXML() ? kCFPropertyListXMLFormat_v1_0 : kCFPropertyListBinaryFormat_v1_0;
376 CFWriteStreamRef binStream = CFWriteStreamCreateWithAllocatedBuffers(alloc, alloc);
377 CFWriteStreamOpen(binStream);
378 CFPropertyListWriteToStream(dict, binStream, desiredFormat, NULL);
379 CFWriteStreamClose(binStream);
380 CFDataRef data = CFWriteStreamCopyProperty(binStream, kCFStreamPropertyDataWritten);
381 CFRelease(binStream);
382 if (data) {
383 SInt32 mode;
384 mode = isWorldReadable ? S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH : S_IRUSR|S_IWUSR;
385#if 1 && defined(__MACH__)
386 { // Try quick atomic way first, then fallback to slower ways and error cases
387 CFStringRef scheme = CFURLCopyScheme(url);
388 if (!scheme) {
389 *tryAgain = false;
390 CFRelease(data);
391 return false;
392 } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
393 SInt32 length = CFDataGetLength(data);
394 const void *bytes = (0 == length) ? (const void *)"" : CFDataGetBytePtr(data);
395 Boolean atomicWriteSuccess = __CFWriteBytesToFileWithAtomicity(url, bytes, length, mode, true);
396 if (atomicWriteSuccess) {
397 CFRelease(scheme);
398 *tryAgain = false;
399 CFRelease(data);
400 return true;
401 }
402 if (!atomicWriteSuccess && thread_errno() == ENOSPC) {
403 CFRelease(scheme);
404 *tryAgain = false;
405 CFRelease(data);
406 return false;
407 }
408 }
409 CFRelease(scheme);
410 }
411#endif
412 success = CFURLWriteDataAndPropertiesToResource(url, data, URLPropertyDictForPOSIXMode(mode), NULL);
413 URLPropertyDictRelease();
414 if (success) {
415 CFDataRef readData;
416 if (!CFURLCreateDataAndPropertiesFromResource(alloc, url, &readData, NULL, NULL, NULL) || !CFEqual(readData, data)) {
417 success = false;
418 *tryAgain = true;
419 }
420 if (readData) CFRelease(readData);
421 } else {
422 CFBooleanRef val = CFURLCreatePropertyFromResource(alloc, url, kCFURLFileExists, NULL);
423 if (!val || !CFBooleanGetValue(val)) {
424 CFURLRef tmpURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("."), kCFURLPOSIXPathStyle, true, url); // Just "." because url is not a directory URL
425 CFURLRef parentURL = tmpURL ? CFURLCopyAbsoluteURL(tmpURL) : NULL;
426 if (tmpURL) CFRelease(tmpURL);
427 if (val) CFRelease(val);
428 val = CFURLCreatePropertyFromResource(alloc, parentURL, kCFURLFileExists, NULL);
429 if ((!val || !CFBooleanGetValue(val)) && _createDirectory(parentURL, isWorldReadable)) {
430 // parent directory didn't exist; now it does; try again to write
431 success = CFURLWriteDataAndPropertiesToResource(url, data, URLPropertyDictForPOSIXMode(mode), NULL);
432 URLPropertyDictRelease();
433 if (success) {
434 CFDataRef rdData;
435 if (!CFURLCreateDataAndPropertiesFromResource(alloc, url, &rdData, NULL, NULL, NULL) || !CFEqual(rdData, data)) {
436 success = false;
437 *tryAgain = true;
438 }
439 if (rdData) CFRelease(rdData);
440 }
441
442 }
443 if (parentURL) CFRelease(parentURL);
444 }
445 if (val) CFRelease(val);
446 }
447 CFRelease(data);
448 } else {
449 // ??? This should never happen
450 CFLog(__kCFLogAssertion, CFSTR("Could not generate XML data for property list"));
451 success = false;
452 }
453 }
454 return success;
455}
456
457static void writeXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key, CFTypeRef value) {
458 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
459 const void *existing = NULL;
460
461 __CFSpinLock(&domain->_lock);
462 if (domain->_domainDict == NULL) {
463 _loadXMLDomainIfStale((CFURLRef )context, domain);
464 }
465
466 // check to see if the value is the same
467 // if (1) the key is present AND value is !NULL and equal to existing, do nothing, or
468 // if (2) the key is not present AND value is NULL, do nothing
469 // these things are no-ops, and should not dirty the domain
470 if (CFDictionaryGetValueIfPresent(domain->_domainDict, key, &existing)) {
471 if (NULL != value && (existing == value || CFEqual(existing, value))) {
472 __CFSpinUnlock(&domain->_lock);
473 return;
474 }
475 } else {
476 if (NULL == value) {
477 __CFSpinUnlock(&domain->_lock);
478 return;
479 }
480 }
481
482 // We must append first so key gets another retain (in case we're
483 // about to remove it from the dictionary, and that's the sole reference)
484 // This should be a set not an array.
485 if (!CFArrayContainsValue(domain->_dirtyKeys, CFRangeMake(0, CFArrayGetCount(domain->_dirtyKeys)), key)) {
486 CFArrayAppendValue(domain->_dirtyKeys, key);
487 }
488 if (value) {
489 // 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.
490 CFTypeRef newValue = CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), value, kCFPropertyListImmutable);
491 CFDictionarySetValue(domain->_domainDict, key, newValue);
492 CFRelease(newValue);
493 } else {
494 CFDictionaryRemoveValue(domain->_domainDict, key);
495 }
496 __CFSpinUnlock(&domain->_lock);
497}
498
499static void getXMLKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *xmlDomain, void **buf[], CFIndex *numKeyValuePairs) {
500 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
501 CFIndex count;
502 __CFSpinLock(&domain->_lock);
503 if (!domain->_domainDict) {
504 _loadXMLDomainIfStale((CFURLRef )context, domain);
505 }
506 count = CFDictionaryGetCount(domain->_domainDict);
507 if (buf) {
508 void **values;
509 if (count <= *numKeyValuePairs) {
510 values = *buf + count;
511 CFDictionaryGetKeysAndValues(domain->_domainDict, (const void **)*buf, (const void **)values);
512 } else if (alloc != kCFAllocatorNull) {
513 *buf = CFAllocatorReallocate(alloc, (*buf ? *buf : NULL), count * 2 * sizeof(void *), 0);
514 if (*buf) {
515 values = *buf + count;
516 CFDictionaryGetKeysAndValues(domain->_domainDict, (const void **)*buf, (const void **)values);
517 }
518 }
519 }
520 *numKeyValuePairs = count;
521 __CFSpinUnlock(&domain->_lock);
522}
523
524static CFDictionaryRef copyXMLDomainDictionary(CFTypeRef context, void *xmlDomain) {
525 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
526 CFDictionaryRef result;
527
528 __CFSpinLock(&domain->_lock);
529 if(!domain->_domainDict) {
530 _loadXMLDomainIfStale((CFURLRef)context, domain);
531 }
532
533 result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), domain->_domainDict, kCFPropertyListImmutable);
534
535 __CFSpinUnlock(&domain->_lock);
536 return result;
537}
538
539
540static void setXMLDomainIsWorldReadable(CFTypeRef context, void *domain, Boolean isWorldReadable) {
541 ((_CFXMLPreferencesDomain *)domain)->_isWorldReadable = isWorldReadable;
542}
543
544static Boolean synchronizeXMLDomain(CFTypeRef context, void *xmlDomain) {
545 _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain;
546 CFMutableDictionaryRef cachedDict;
547 CFMutableArrayRef changedKeys;
548 SInt32 idx, count;
549 Boolean success, tryAgain;
550
551 __CFSpinLock(&domain->_lock);
552 cachedDict = domain->_domainDict;
553 changedKeys = domain->_dirtyKeys;
554 count = CFArrayGetCount(changedKeys);
555
556 if (count == 0) {
557 // no changes were made to this domain; just remove it from the cache to guarantee it will be taken from disk next access
558 if (cachedDict) {
559 CFRelease(cachedDict);
560 domain->_domainDict = NULL;
561 }
562 __CFSpinUnlock(&domain->_lock);
563 return true;
564 }
565
566 domain->_domainDict = NULL; // This forces a reload. Note that we now have a retain on cachedDict
567 do {
568 _loadXMLDomainIfStale((CFURLRef )context, domain);
569 // now cachedDict holds our changes; domain->_domainDict has the latest version from the disk
570 for (idx = 0; idx < count; idx ++) {
571 CFStringRef key = CFArrayGetValueAtIndex(changedKeys, idx);
572 CFTypeRef value = CFDictionaryGetValue(cachedDict, key);
573 if (value)
574 CFDictionarySetValue(domain->_domainDict, key, value);
575 else
576 CFDictionaryRemoveValue(domain->_domainDict, key);
577 }
578 success = _writeXMLFile((CFURLRef )context, domain->_domainDict, domain->_isWorldReadable, &tryAgain);
579 if (tryAgain) {
580 __CFMilliSleep(((__CFReadTSR() & 0xf) + 1) * 50);
581 }
582 } while (tryAgain);
583 CFRelease(cachedDict);
584 if (success) {
585 CFArrayRemoveAllValues(domain->_dirtyKeys);
586 }
587 domain->_lastReadTime = CFAbsoluteTimeGetCurrent();
588 __CFSpinUnlock(&domain->_lock);
589 return success;
590}
591
592#endif /* !defined(__WIN32__) */
593