2 * Copyright (c) 2000-2008, 2010-2013, 2015-2020 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * November 9, 2000 Allan Nathanson <ajn@apple.com>
34 #include <TargetConditionals.h>
35 #include "SCPreferencesInternal.h"
36 #include "SCHelper_client.h"
40 #include <sys/errno.h>
43 __SCPreferencesCommitChanges_helper(SCPreferencesRef prefs
)
45 CFDataRef data
= NULL
;
47 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
48 uint32_t status
= kSCStatusOK
;
49 CFDataRef reply
= NULL
;
51 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
53 status
= kSCStatusAccessError
;
57 if (prefsPrivate
->changed
) {
58 ok
= _SCSerialize(prefsPrivate
->prefs
, &data
, NULL
, NULL
);
60 status
= kSCStatusFailed
;
62 SC_log(LOG_NOTICE
, "_SCSerialize() failed");
63 SC_log(LOG_NOTICE
, " prefs = %s",
64 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
70 // have the helper "commit" the prefs
71 // status = kSCStatusOK;
73 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
74 SCHELPER_MSG_PREFS_COMMIT
,
78 if (data
!= NULL
) CFRelease(data
);
83 if (status
!= kSCStatusOK
) {
87 if (prefsPrivate
->changed
) {
88 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
89 prefsPrivate
->signature
= reply
;
91 if (reply
!= NULL
) CFRelease(reply
);
94 prefsPrivate
->changed
= FALSE
;
100 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
101 _SCHelperClose(&prefsPrivate
->helper_port
);
107 if (reply
!= NULL
) CFRelease(reply
);
114 writen(int ref
, const void *data
, size_t len
)
118 const void *p
= data
;
121 if ((n
= write(ref
, p
, left
)) == -1) {
122 if (errno
!= EINTR
) {
135 SCPreferencesCommitChanges(SCPreferencesRef prefs
)
139 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
145 /* sorry, you must provide a session */
146 _SCErrorSet(kSCStatusNoPrefsSession
);
151 * Determine if the we have exclusive access to the preferences
152 * and acquire the lock if necessary.
154 wasLocked
= prefsPrivate
->locked
;
156 if (!SCPreferencesLock(prefs
, TRUE
)) {
157 SC_log(LOG_INFO
, "SCPreferencesLock() failed");
162 if (prefsPrivate
->authorizationData
!= NULL
) {
163 ok
= __SCPreferencesCommitChanges_helper(prefs
);
165 prefsPrivate
->changed
= FALSE
;
171 * if necessary, apply changes
173 if (!prefsPrivate
->changed
) {
178 * check if the preferences should be removed
180 if (CFDictionaryGetCount(prefsPrivate
->prefs
) == 0) {
184 if ((prefsPrivate
->options
!= NULL
) &&
185 CFDictionaryGetValueIfPresent(prefsPrivate
->options
,
186 kSCPreferencesOptionRemoveWhenEmpty
,
187 (const void **)&val
) &&
188 isA_CFBoolean(val
) &&
189 CFBooleanGetValue(val
)) {
190 /* if we've been asked to remove empty .plists */
195 path
= prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
;
200 CFStringRef protectionClass
;
203 if (stat(prefsPrivate
->path
, &statBuf
) == -1) {
204 if (errno
== ENOENT
) {
205 memset(&statBuf
, 0, sizeof(statBuf
));
206 statBuf
.st_mode
= 0644;
207 statBuf
.st_uid
= geteuid();
208 statBuf
.st_gid
= getegid();
211 SC_log(LOG_INFO
, "stat() failed: %s", strerror(errno
));
216 /* create the (new) preferences file */
217 pathLen
= strlen(path
) + sizeof("-new");
218 thePath
= CFAllocatorAllocate(NULL
, pathLen
, 0);
219 snprintf(thePath
, pathLen
, "%s-new", path
);
221 if ((prefsPrivate
->options
!= NULL
) &&
222 CFDictionaryGetValueIfPresent(prefsPrivate
->options
,
223 kSCPreferencesOptionProtectionClass
,
224 (const void **)&protectionClass
)) {
228 if (!isA_CFString(protectionClass
) ||
229 (CFStringGetLength(protectionClass
) != 1) ||
230 ((str
= CFStringGetCStringPtr(protectionClass
, kCFStringEncodingASCII
)) == NULL
) ||
231 (str
[0] < 'A') || (str
[0] > 'F')
233 _SCErrorSet(kSCStatusInvalidArgument
);
237 pc
= str
[0] - 'A' + 1; // PROTECTION_CLASS_[ABCDEF]
238 fd
= open_dprotected_np(thePath
, O_WRONLY
|O_CREAT
, pc
, 0, statBuf
.st_mode
);
240 fd
= open(thePath
, O_WRONLY
|O_CREAT
, statBuf
.st_mode
);
245 SC_log(LOG_NOTICE
, "open() failed: %s", strerror(errno
));
246 CFAllocatorDeallocate(NULL
, thePath
);
250 /* preserve permissions */
251 (void) fchown(fd
, statBuf
.st_uid
, statBuf
.st_gid
);
252 (void) fchmod(fd
, statBuf
.st_mode
);
254 /* write the new preferences */
255 newPrefs
= CFPropertyListCreateData(NULL
,
258 kCFPropertyListBinaryFormat_v1_0
,
259 #else // TARGET_OS_IPHONE
260 kCFPropertyListXMLFormat_v1_0
,
261 #endif // TARGET_OS_IPHONE
265 _SCErrorSet(kSCStatusFailed
);
266 SC_log(LOG_INFO
, "CFPropertyListCreateData() failed");
267 SC_log(LOG_INFO
, " prefs = %s", path
);
268 CFAllocatorDeallocate(NULL
, thePath
);
272 if (writen(fd
, (const void *)CFDataGetBytePtr(newPrefs
), CFDataGetLength(newPrefs
)) == -1) {
274 SC_log(LOG_INFO
, "writen() failed: %s", strerror(errno
));
275 SC_log(LOG_INFO
, " path = %s", thePath
);
276 (void) unlink(thePath
);
277 CFAllocatorDeallocate(NULL
, thePath
);
283 /* new preferences have been written */
284 if (close(fd
) == -1) {
286 SC_log(LOG_INFO
, "close() failed: %s", strerror(errno
));
287 SC_log(LOG_INFO
, " path = %s", thePath
);
288 (void) unlink(thePath
);
289 CFAllocatorDeallocate(NULL
, thePath
);
295 /* rename new->old */
296 if (rename(thePath
, path
) == -1) {
298 SC_log(LOG_INFO
, "rename() failed: %s", strerror(errno
));
299 SC_log(LOG_INFO
, " path = %s --> %s", thePath
, path
);
300 CFAllocatorDeallocate(NULL
, thePath
);
303 CFAllocatorDeallocate(NULL
, thePath
);
305 if (prefsPrivate
->newPath
) {
306 /* prefs file saved in "new" directory */
307 (void) unlink(prefsPrivate
->path
);
308 (void) symlink(prefsPrivate
->newPath
, prefsPrivate
->path
);
309 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
310 prefsPrivate
->path
= path
;
311 prefsPrivate
->newPath
= NULL
;
314 /* grab the new signature */
315 if (stat(path
, &statBuf
) == -1) {
317 SC_log(LOG_INFO
, "stat() failed: %s", strerror(errno
));
318 SC_log(LOG_INFO
, " path = %s", thePath
);
322 /* remove the empty .plist */
325 /* init the new signature */
326 memset(&statBuf
, 0, sizeof(statBuf
));
329 /* update signature */
330 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
331 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
335 if (prefsPrivate
->changed
) {
336 SC_log(LOG_INFO
, "SCPreferences() commit: %s, size=%lld",
337 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
,
338 __SCPreferencesPrefsSize(prefs
));
340 SC_log(LOG_INFO
, "SCPreferences() commit: %s, w/no changes",
341 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
344 /* post notification */
345 ok
= SCDynamicStoreNotifyValue(NULL
, prefsPrivate
->sessionKeyCommit
);
347 SC_log(LOG_INFO
, "SCDynamicStoreNotifyValue() failed");
348 _SCErrorSet(kSCStatusFailed
);
352 prefsPrivate
->changed
= FALSE
;
359 status
= SCError(); // preserve status across unlock
360 (void) SCPreferencesUnlock(prefs
);