2 * Copyright (c) 2015 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 * SOSChangeTracker.c - Implementation of a manifest caching change tracker that forwards changes to children
28 #include <Security/SecureObjectSync/SOSChangeTracker.h>
29 #include <Security/SecureObjectSync/SOSDigestVector.h>
30 #include <Security/SecureObjectSync/SOSEngine.h>
31 #include <Security/SecureObjectSync/SOSManifest.h>
32 #include <Security/SecureObjectSync/SOSInternal.h>
33 #include <utilities/SecCFError.h>
34 #include <utilities/SecCFWrappers.h>
36 CFStringRef
SOSChangeCopyDescription(SOSChangeRef change
) {
37 CFTypeRef object
= NULL
;
38 bool isAdd
= SOSChangeGetObject(change
, &object
);
39 // TODO: Print objects or digests
40 return (isData(object
)
41 ? isAdd
? CFSTR("a") : CFSTR("d")
42 : isAdd
? CFSTR("A") : CFSTR("D"));
45 CFDataRef
SOSChangeCopyDigest(SOSDataSourceRef dataSource
, SOSChangeRef change
, bool *isDel
, SOSObjectRef
*object
, CFErrorRef
*error
) {
46 CFDataRef digest
= NULL
;
47 if (isArray(change
)) {
48 if (CFArrayGetCount(change
) != 1) {
49 SecError(errSecDecode
, error
, CFSTR("change array count: %ld"), CFArrayGetCount(change
));
52 change
= CFArrayGetValueAtIndex(change
, 0);
58 // If the change is a CFData, this is the signal that it is a delete
60 digest
= (CFDataRef
)CFRetain(change
);
62 digest
= SOSObjectCopyDigest(dataSource
, (SOSObjectRef
)change
, error
);
63 *object
= (SOSObjectRef
)change
;
65 assert(digest
&& CFDataGetLength(digest
) == CCSHA1_OUTPUT_SIZE
);
69 CFStringRef
SOSChangesCopyDescription(CFArrayRef changes
) {
70 CFMutableStringRef desc
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("("));
72 if (changes
) CFArrayForEachC(changes
, change
) {
73 CFStringRef changeDesc
= SOSChangeCopyDescription(change
);
74 CFStringAppend(desc
, changeDesc
);
75 CFRetainSafe(changeDesc
);
77 CFStringAppend(desc
, CFSTR(")"));
82 /* SOSChangeTracker implementation. */
83 struct __OpaqueSOSChangeTracker
{
85 SOSManifestRef manifest
; // Optional: Only concrete cts have a manifest
86 CFMutableArrayRef changeChildren
; // Optional: cts can have children
87 CFMutableArrayRef manifestChildren
; // Optional: cts can have children
90 static CFStringRef
SOSChangeTrackerCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
91 SOSChangeTrackerRef ct
= (SOSChangeTrackerRef
)cf
;
92 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, formatOptions
, CFSTR("<ChangeTracker %@ children %ld/%ld>"),
93 ct
->manifest
? ct
->manifest
: (SOSManifestRef
)CFSTR("NonConcrete"),
94 CFArrayGetCount(ct
->changeChildren
), CFArrayGetCount(ct
->manifestChildren
));
98 static void SOSChangeTrackerDestroy(CFTypeRef cf
) {
99 SOSChangeTrackerRef ct
= (SOSChangeTrackerRef
)cf
;
100 CFReleaseSafe(ct
->manifest
);
101 CFReleaseSafe(ct
->changeChildren
);
102 CFReleaseSafe(ct
->manifestChildren
);
105 // Even though SOSChangeTracker instances are used as keys in dictionaries, they are treated as pointers when used as such
106 // which is fine since the engine ensures instances are singletons.
107 CFGiblisFor(SOSChangeTracker
);
109 SOSChangeTrackerRef
SOSChangeTrackerCreate(CFAllocatorRef allocator
, bool isConcrete
, CFArrayRef changeChildren
, CFErrorRef
*error
) {
110 SOSChangeTrackerRef ct
= NULL
;
111 ct
= CFTypeAllocate(SOSChangeTracker
, struct __OpaqueSOSChangeTracker
, allocator
);
112 if (ct
&& isConcrete
) {
113 ct
->manifest
= SOSManifestCreateWithData(NULL
, error
);
119 ct
->changeChildren
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, changeChildren
);
121 ct
->changeChildren
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
122 ct
->manifestChildren
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
128 // Change the concreteness of the current ct (a non concrete ct does not support SOSChangeTrackerCopyManifest().
129 void SOSChangeTrackerSetConcrete(SOSChangeTrackerRef ct
, bool isConcrete
) {
131 CFReleaseNull(ct
->manifest
);
132 else if (!ct
->manifest
) {
133 ct
->manifest
= SOSManifestCreateWithData(NULL
, NULL
);
137 // Add a child to the current ct
138 void SOSChangeTrackerRegisterChangeUpdate(SOSChangeTrackerRef ct
, SOSChangeTrackerUpdatesChanges child
) {
139 CFArrayAppendValue(ct
->changeChildren
, child
);
142 void SOSChangeTrackerRegisterManifestUpdate(SOSChangeTrackerRef ct
, SOSChangeTrackerUpdatesManifests child
) {
143 CFArrayAppendValue(ct
->manifestChildren
, child
);
146 void SOSChangeTrackerResetRegistration(SOSChangeTrackerRef ct
) {
147 CFArrayRemoveAllValues(ct
->changeChildren
);
148 CFArrayRemoveAllValues(ct
->manifestChildren
);
151 void SOSChangeTrackerSetManifest(SOSChangeTrackerRef ct
, SOSManifestRef manifest
) {
152 CFRetainAssign(ct
->manifest
, manifest
);
155 SOSManifestRef
SOSChangeTrackerCopyManifest(SOSChangeTrackerRef ct
, CFErrorRef
*error
) {
157 return (SOSManifestRef
)CFRetain(ct
->manifest
);
159 SOSErrorCreate(kSOSErrorNotConcreteError
, error
, NULL
, CFSTR("ChangeTracker is not concrete"));
163 static bool SOSChangeTrackerCreateManifestsWithChanges(SOSEngineRef engine
, CFArrayRef changes
, SOSManifestRef
*removals
, SOSManifestRef
*additions
, CFErrorRef
*error
) {
165 struct SOSDigestVector dvdels
= SOSDigestVectorInit
;
166 struct SOSDigestVector dvadds
= SOSDigestVectorInit
;
167 struct SOSDigestVector
*dv
;
169 CFArrayForEachC(changes
, change
) {
170 CFDataRef digest
, allocatedDigest
= NULL
;
171 if (isArray(change
)) {
172 assert(CFArrayGetCount(change
) == 1);
173 change
= CFArrayGetValueAtIndex(change
, 0);
179 if (isData(change
)) {
180 digest
= (CFDataRef
)change
;
182 CFErrorRef digestError
= NULL
;
183 digest
= allocatedDigest
= SOSObjectCopyDigest(SOSEngineGetDataSource(engine
), (SOSObjectRef
)change
, &digestError
);
185 secerror("change %@ SOSObjectCopyDigest: %@", change
, digestError
);
186 CFReleaseNull(digestError
);
191 if (CFDataGetLength(digest
) == 20) {
192 SOSDigestVectorAppend(dv
, CFDataGetBytePtr(digest
));
194 secerror("change %@ bad length digest: %@", change
, digest
);
196 CFReleaseNull(allocatedDigest
);
199 ok
= *removals
= SOSManifestCreateWithDigestVector(&dvdels
, error
);
201 ok
= *additions
= SOSManifestCreateWithDigestVector(&dvadds
, error
);
203 SOSDigestVectorFree(&dvadds
);
204 SOSDigestVectorFree(&dvdels
);
209 bool SOSChangeTrackerTrackChanges(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef
*error
) {
211 if (changes
&& CFArrayGetCount(changes
)) {
212 CFStringRef changesDesc
= SOSChangesCopyDescription(changes
);
213 secnotice("tracker", "%@ %s %s changes: %@", ct
, phase
== kSOSDataSourceTransactionWillCommit
? "will-commit" : phase
== kSOSDataSourceTransactionDidCommit
? "did-commit" : "did-rollback", source
== kSOSDataSourceSOSTransaction
? "sos" : "api", changesDesc
);
214 CFReleaseSafe(changesDesc
);
215 if (ct
->manifest
|| ct
->manifestChildren
) {
216 SOSManifestRef additions
= NULL
;
217 SOSManifestRef removals
= NULL
;
218 ok
&= SOSChangeTrackerCreateManifestsWithChanges(engine
, changes
, &removals
, &additions
, error
);
221 SOSManifestRef updatedManifest
= SOSManifestCreateWithPatch(ct
->manifest
, removals
, additions
, error
);
222 if (updatedManifest
){
223 CFTransferRetained(ct
->manifest
, updatedManifest
);
226 if (ct
->manifestChildren
) {
227 SOSChangeTrackerUpdatesManifests child
;
228 CFArrayForEachC(ct
->manifestChildren
, child
) {
229 ok
= ok
&& child(ct
, engine
, txn
, source
, phase
, removals
, additions
, error
);
233 CFReleaseSafe(removals
);
234 CFReleaseSafe(additions
);
235 // TODO: Potentially filter changes to eliminate any changes that were already in our manifest
236 // Backup Peers and the like would probably enjoy this so they don't have to do it themselves.
239 if (ct
->changeChildren
) {
240 SOSChangeTrackerUpdatesChanges child
;
241 CFArrayForEachC(ct
->changeChildren
, child
) {
242 ok
= ok
&& child(ct
, engine
, txn
, source
, phase
, changes
, error
);