2 * Copyright (c) 2012-2014 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@
26 * SOSPeer.c - Implementation of a secure object syncing peer
28 #include <Security/SecureObjectSync/SOSPeer.h>
30 #include <Security/SecureObjectSync/SOSCoder.h>
31 #include <Security/SecureObjectSync/SOSDigestVector.h>
32 #include <Security/SecureObjectSync/SOSInternal.h>
33 #include <Security/SecureObjectSync/SOSTransport.h>
34 #include <Security/SecureObjectSync/SOSViews.h>
35 #include <Security/SecureObjectSync/SOSChangeTracker.h>
36 #include <utilities/SecCFError.h>
37 #include <utilities/SecCFRelease.h>
38 #include <utilities/SecCFWrappers.h>
39 #include <utilities/SecDb.h>
40 #include <utilities/SecFileLocations.h>
41 #include <utilities/SecIOFormat.h>
42 #include <utilities/array_size.h>
43 #include <utilities/debugging.h>
44 #include <Security/SecureObjectSync/SOSBackupEvent.h>
45 #include <Security/SecItemBackup.h>
47 #include <securityd/SOSCloudCircleServer.h>
49 #include <CoreFoundation/CoreFoundation.h>
53 #include <AssertMacros.h>
55 // Backup Peer Support
56 #include <securityd/SecKeybagSupport.h>
61 // MARK: - SOSPeerPersistence code
63 static CFStringRef kSOSPeerSequenceNumberKey
= CFSTR("sequence-number");
64 static CFStringRef kSOSPeerGetObjectsKey
= CFSTR("get-objects");
65 static CFStringRef kSOSPeerReceivedUnknownConfirmedDigestKey
= CFSTR("received-unknown");
66 static CFStringRef kSOSPeerJoinRequestedKey
= CFSTR("join-requested");
67 static CFStringRef kSOSPeerSkipHelloKey
= CFSTR("skip-hello");
69 CFStringRef kSOSPeerDataLabel
= CFSTR("iCloud Peer Data Meta-data");
72 // MARK: SOSPeerState (dictionary keys)
75 // PeerState dictionary keys
76 static CFStringRef kSOSPeerSendObjectsKey
= CFSTR("send-objects"); // bool
77 static CFStringRef kSOSPeerMustSendMessageKey
= CFSTR("must-send"); // bool
78 static CFStringRef kSOSPeerPendingObjectsKey
= CFSTR("pending-objects"); // digest
79 static CFStringRef kSOSPeerUnwantedManifestKey
= CFSTR("unwanted-manifest"); // digest
80 static CFStringRef kSOSPeerConfirmedManifestKey
= CFSTR("confirmed-manifest"); //digest
81 static CFStringRef kSOSPeerProposedManifestKey
= CFSTR("pending-manifest"); // array of digests
82 static CFStringRef kSOSPeerLocalManifestKey
= CFSTR("local-manifest"); // array of digests
83 static CFStringRef kSOSPeerVersionKey
= CFSTR("vers"); // int
84 static CFStringRef kSOSPeerCoderKey
= CFSTR("coder"); // der encoded SOSCoder
87 // SOSPeerMeta keys that can also be used in peerstate...
89 static CFStringRef kSOSPeerPeerIDKey
= CFSTR("peer-id"); // string
90 static CFStringRef kSOSPeerViewsKey
= CFSTR("views"); // set (or array) of string
91 static CFStringRef kSOSPeerKeyBagKey
= CFSTR("keybag"); // data
94 Theory of syncing for both incoming and outgoing messages
96 A peerstate consists of:
97 (T, U, C, P, L, D->M, M->D, H->O, O->{(Op,Or)|Oa,(Op,Or)|(Om,Oa),(Op,Or)|Om,Oi|Oi|Ob|Oa,Ob|(Om,Oa),Ob|(Ol,Ou)|Oa,(Ol,Ou)|(Om,Oa)(Ol,Ou)})
98 T: to be sent (pendingObjects) manifest
99 U: unwanted objects manifest
100 C: confirmed manifest
102 D->M? digest or manifest to optional manifest function
103 M->D manifest to digest of manifest function
104 H->O? hash (manifest entry) to optional object function (the datasource)
105 O->{ Mapping from incoming O objects to one of:
106 (Op,Or): Op = Peers object, Or is replaced local object.
107 Oa,(Op,Or): Oa = appeared local object, Or = Oa, see above for (Op, Or)
108 (Om,Oa),(Op,Or): Om missing local object, was apparently Oa instead see above for (Oa, Op, Or)
109 Om,Oi: Om missing local object, inserted Oi (nothing replaced), but Om still disapeared from manifest
110 Oi Oi inserted object from peer (nothing replaced)
111 Ob: Ob both remote and local object are identical, nothing changed
112 Oa,Ob: Oa = appeared local object equal to Oa = Ob. Equivalent to single Oi
113 (Om,Oa),Ob: Om missing local object, must be O->H->Ob, Oa found in place, Ob != Oa, Oa magically appeared unrelated to O->H->Ob
114 (Ol,Ou): Ol local object wins from peers Ou older object => append Ou to U
115 Oa,(Ol,Ou): Oa appeared as a local object and Oa = Ol above
116 (Om,Oa),(Ol,Ou): Om removed and Oa replaced Om and Oa = Ol as above
118 A message consists of
125 To send a message we simply compute:
128 E: L \ C # subsetted if not all of O is sent
129 O: H->O?->O # as size permits
130 and change P to (C \ M:) union E: union O->H->O:
132 To receive a message we compute
133 Op,Oi,Ob,Oo and Om,Oa,Ol
134 C: (D->M->B \ M) union E union O->H->O
136 T: T \ (Om union Or union A union Oa,Ob.last union (Om,Oa),Ob.last ) union (M intersect L)
144 static SOSPeerMetaRef
SOSPeerMetaCreate(CFStringRef peerID
) {
145 return CFRetain(peerID
);
148 static SOSPeerMetaRef
SOSPeerMetaCreateWithViews(CFStringRef peerID
, CFSetRef views
) {
149 const void *keys
[] = { kSOSPeerPeerIDKey
, kSOSPeerViewsKey
};
150 const void *values
[] = { peerID
, views
};
151 return CFDictionaryCreate(kCFAllocatorDefault
, keys
, values
, array_size(keys
), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
154 static SOSPeerMetaRef
SOSPeerMetaCreateWithViewsAndKeyBag(CFStringRef peerID
, CFSetRef views
, CFDataRef keybag
) {
155 const void *keys
[] = { kSOSPeerPeerIDKey
, kSOSPeerViewsKey
, kSOSPeerKeyBagKey
};
156 const void *values
[] = { peerID
, views
, keybag
};
157 return CFDictionaryCreate(kCFAllocatorDefault
, keys
, values
, array_size(keys
), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
160 SOSPeerMetaRef
SOSPeerMetaCreateWithComponents(CFStringRef peerID
, CFSetRef views
, CFDataRef keybag
) {
161 if (!isString(peerID
))
165 return SOSPeerMetaCreateWithViewsAndKeyBag(peerID
, views
, keybag
);
167 return SOSPeerMetaCreateWithViews(peerID
, views
);
169 return SOSPeerMetaCreate(peerID
);
172 SOSPeerMetaRef
SOSPeerMetaCreateWithState(CFStringRef peerID
, CFDictionaryRef state
) {
173 return SOSPeerMetaCreateWithComponents(peerID
, CFDictionaryGetValue(state
, kSOSPeerViewsKey
), CFDictionaryGetValue(state
, kSOSPeerKeyBagKey
));
176 CFStringRef
SOSPeerMetaGetComponents(SOSPeerMetaRef peerMeta
, CFSetRef
*views
, CFDataRef
*keybag
, CFErrorRef
*error
) {
177 if (isDictionary(peerMeta
)) {
178 CFDictionaryRef meta
= (CFDictionaryRef
)peerMeta
;
179 CFStringRef peerID
= asString(CFDictionaryGetValue(meta
, kSOSPeerPeerIDKey
), error
);
180 CFSetRef vns
= asSet(CFDictionaryGetValue(meta
, kSOSPeerViewsKey
), error
);
181 if (vns
&& asDataOptional(CFDictionaryGetValue(meta
, kSOSPeerKeyBagKey
), keybag
, error
)) {
189 // Hack so tests can pass simple peerIDs
190 *views
= SOSViewsGetV0ViewSet();
192 return asString(peerMeta
, error
);
196 CFTypeRef
SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFTypeRef peerOrState
, CFSetRef views
, CFDataRef keyBag
) {
198 if (peerOrState
&& CFGetTypeID(peerOrState
) == SOSPeerGetTypeID()) {
199 // Inflated peer, update its views and move on
200 SOSPeerRef peer
= (SOSPeerRef
)peerOrState
;
201 SOSPeerSetViewNameSet(peer
, views
);
202 SOSPeerSetKeyBag(peer
, keyBag
);
203 return CFRetainSafe(peer
);
204 } else if (peerOrState
&& CFGetTypeID(peerOrState
) == CFDictionaryGetTypeID()) {
205 CFMutableDictionaryRef state
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, peerOrState
);
206 // Deserialized peer, just updated the serialized state with the new views
207 CFDictionarySetValue(state
, kSOSPeerViewsKey
, views
);
209 CFDictionarySetValue(state
, kSOSPeerKeyBagKey
, keyBag
);
211 CFDictionaryRemoveValue(state
, kSOSPeerKeyBagKey
);
214 // New peer, just create a state object.
216 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, kSOSPeerViewsKey
, views
, kSOSPeerKeyBagKey
, keyBag
, NULL
);
218 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, kSOSPeerViewsKey
, views
, NULL
);
222 CFTypeRef
SOSPeerOrStateSetViewsAndCopyState(CFTypeRef peerOrState
, CFSetRef views
) {
224 if (peerOrState
&& CFGetTypeID(peerOrState
) == SOSPeerGetTypeID()) {
225 // Inflated peer, update its views and deflate it
226 SOSPeerRef peer
= (SOSPeerRef
)peerOrState
;
227 SOSPeerSetViewNameSet(peer
, views
);
228 return SOSPeerCopyState(peer
, NULL
);
229 } else if (peerOrState
&& CFGetTypeID(peerOrState
) == CFDictionaryGetTypeID()) {
230 // We have a deflated peer. Update its views and keep it deflated
231 CFMutableDictionaryRef state
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, peerOrState
);
232 CFDictionarySetValue(state
, kSOSPeerViewsKey
, views
);
239 bool SOSPeerMapEntryIsBackup(const void *mapEntry
) {
240 if (!mapEntry
) return false;
241 if (CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) {
242 return SOSPeerGetKeyBag((SOSPeerRef
)mapEntry
);
244 return CFDictionaryContainsKey(mapEntry
, kSOSPeerKeyBagKey
);
249 // MARK: - SOSManifest
253 kSOSPeerMaxManifestWindowDepth
= 4
256 static CFStringRef
SOSManifestCreateOptionalDescriptionWithLabel(SOSManifestRef manifest
, CFStringRef label
) {
257 if (!manifest
) return CFSTR(" - ");
258 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR(" %@%@"), label
, manifest
);
261 static CFStringRef
SOSManifestArrayCreateOptionalDescriptionWithLabel(CFArrayRef manifests
, CFStringRef label
) {
262 CFIndex count
= manifests
? CFArrayGetCount(manifests
) : 0;
263 if (count
== 0) return CFSTR(" - ");
264 SOSManifestRef manifest
= (SOSManifestRef
)CFArrayGetValueAtIndex(manifests
, 0);
265 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR(" %@[%" PRIdCFIndex
"]%@"), label
, count
, manifest
);
268 static void SOSManifestArraySetManifest(CFMutableArrayRef
*manifests
, SOSManifestRef manifest
) {
271 CFArrayRemoveAllValues(*manifests
);
273 *manifests
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
274 CFArrayAppendValue(*manifests
, manifest
);
276 CFReleaseNull(*manifests
);
280 static void SOSManifestMutableArrayAppendManifest(CFMutableArrayRef manifests
, SOSManifestRef manifest
) {
282 CFIndex count
= CFArrayGetCount(manifests
);
283 CFIndex ixOfManifest
= CFArrayGetFirstIndexOfValue(manifests
, CFRangeMake(0, count
), manifest
);
284 if (ixOfManifest
!= 0) {
285 // If the manifest isn't at the front of the array move it there.
286 // If it's not in the array, remove enough entires from the end to
287 // make room to put it in the front.
288 if (ixOfManifest
!= kCFNotFound
) {
289 CFArrayRemoveValueAtIndex(manifests
, ixOfManifest
);
291 while (count
>= kSOSPeerMaxManifestWindowDepth
)
292 CFArrayRemoveValueAtIndex(manifests
, --count
);
295 CFArrayInsertValueAtIndex(manifests
, 0, manifest
);
298 // pending == NULL => nothing clear history
299 CFArrayRemoveAllValues(manifests
);
303 static void SOSManifestArrayAppendManifest(CFMutableArrayRef
*manifests
, SOSManifestRef manifest
) {
305 SOSManifestMutableArrayAppendManifest(*manifests
, manifest
);
307 SOSManifestArraySetManifest(manifests
, manifest
);
314 struct __OpaqueSOSPeer
{
322 uint64_t sequenceNumber
;
323 bool mustSendMessage
;
326 SOSManifestRef pendingObjects
;
327 SOSManifestRef unwantedManifest
;
328 SOSManifestRef confirmedManifest
;
329 CFMutableArrayRef proposedManifests
;
330 CFMutableArrayRef localManifests
;
332 // Only backup peers have these:
337 CFGiblisWithCompareFor(SOSPeer
)
339 static CFStringRef
SOSPeerCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
340 SOSPeerRef peer
= (SOSPeerRef
)cf
;
342 CFStringRef po
= SOSManifestCreateOptionalDescriptionWithLabel(peer
->pendingObjects
, CFSTR("O"));
343 CFStringRef uo
= SOSManifestCreateOptionalDescriptionWithLabel(peer
->unwantedManifest
, CFSTR("U"));
344 CFStringRef co
= SOSManifestCreateOptionalDescriptionWithLabel(peer
->confirmedManifest
, CFSTR("C"));
345 CFStringRef pe
= SOSManifestArrayCreateOptionalDescriptionWithLabel(peer
->proposedManifests
, CFSTR("P"));
346 CFStringRef lo
= SOSManifestArrayCreateOptionalDescriptionWithLabel(peer
->localManifests
, CFSTR("L"));
347 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<%@ %s%s%@%@%@%@%@>"),
349 SOSPeerMustSendMessage(peer
) ? "F" : "f",
350 SOSPeerSendObjects(peer
) ? "S" : "s",
361 return CFSTR("NULL");
364 static Boolean
SOSPeerCompare(CFTypeRef cfA
, CFTypeRef cfB
)
366 SOSPeerRef peerA
= (SOSPeerRef
)cfA
, peerB
= (SOSPeerRef
)cfB
;
367 // Use mainly to see if peerB is actually this device (peerA)
368 return CFStringCompare(SOSPeerGetID(peerA
), SOSPeerGetID(peerB
), 0) == kCFCompareEqualTo
;
371 // Coder and coderData caching.
372 // A Peer has either a coderData or a coder. Upon serialization the
373 // coder will be turned into coderData but the coder will stay instantiated
374 // unless the peer is released.
375 static void SOSPeerSetCoderData(SOSPeerRef peer
, CFDataRef coderData
){
377 SOSCoderDispose(peer
->coder
);
380 CFRetainAssign(peer
->coderData
, coderData
);
383 static bool SOSPeerCopyCoderData(SOSPeerRef peer
, CFDataRef
*coderData
, CFErrorRef
*error
) {
384 // TODO: We can optionally call SOSPeerSetCoderData here to clear the coder whenever its encoded,
385 // if we assume that coders are written out to disk more often than they are used.
389 CFErrorRef localError
= NULL
;
390 ok
= *coderData
= SOSCoderCopyDER(peer
->coder
, &localError
);
392 secerror("failed to der encode coder for peer %@, dropping it: %@", peer
->peer_id
, localError
);
393 SOSCoderDispose(peer
->coder
);
395 CFErrorPropagate(localError
, error
);
399 // Alternate always delete in memory coder after der encoding it.
400 CFAssignRetained(peer
->coderData
, SOSCoderCopyDER(peer
->coder
, error
));
401 ok
= peer
->coderData
;
402 SOSCoderDispose(peer
->coder
);
406 *coderData
= CFRetainSafe(peer
->coderData
);
410 SOSCoderRef
SOSPeerGetCoder(SOSPeerRef peer
, CFErrorRef
*error
) {
411 if (peer
->coderData
) {
412 peer
->coder
= SOSCoderCreateFromData(peer
->coderData
, error
);
413 CFReleaseNull(peer
->coderData
);
414 } else if (!peer
->coder
) {
415 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No coderData nor coder for peer: %@"), peer
->peer_id
);
420 bool SOSPeerEnsureCoder(SOSPeerRef peer
, SOSFullPeerInfoRef myPeerInfo
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
421 if (!SOSPeerGetCoder(peer
, NULL
)) {
422 secinfo("peer", "New coder for id %@.", peer
->peer_id
);
423 CFErrorRef localError
= NULL
;
424 if(SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(myPeerInfo
), peerInfo
))
425 peer
->coder
= SOSCoderCreate(peerInfo
, myPeerInfo
, kCFBooleanTrue
, &localError
);
427 peer
->coder
= SOSCoderCreate(peerInfo
, myPeerInfo
, kCFBooleanFalse
, &localError
);
430 secerror("Failed to create coder for %@: %@", peer
->peer_id
, localError
);
431 CFErrorPropagate(localError
, error
);
438 static bool SOSPeerGetPersistedBoolean(CFDictionaryRef persisted
, CFStringRef key
) {
439 CFBooleanRef boolean
= CFDictionaryGetValue(persisted
, key
);
440 return boolean
&& CFBooleanGetValue(boolean
);
443 static CFDataRef
SOSPeerGetPersistedData(CFDictionaryRef persisted
, CFStringRef key
) {
444 return asData(CFDictionaryGetValue(persisted
, key
), NULL
);
447 static int64_t SOSPeerGetPersistedInt64(CFDictionaryRef persisted
, CFStringRef key
) {
449 CFNumberRef number
= CFDictionaryGetValue(persisted
, key
);
451 CFNumberGetValue(number
, kCFNumberSInt64Type
, &integer
);
456 static void SOSPeerGetOptionalPersistedCFIndex(CFDictionaryRef persisted
, CFStringRef key
, CFIndex
*value
) {
457 CFNumberRef number
= CFDictionaryGetValue(persisted
, key
);
459 CFNumberGetValue(number
, kCFNumberCFIndexType
, value
);
463 static CFSetRef
SOSPeerGetPersistedViewNameSet(SOSPeerRef peer
, CFDictionaryRef persisted
, CFStringRef key
) {
464 CFSetRef vns
= CFDictionaryGetValue(persisted
, key
);
466 // Engine state in db contained a v0 peer, thus it must be in the V0ViewSet.
467 vns
= SOSViewsGetV0ViewSet();
468 secnotice("peer", "%@ had no views, inferring: %@", peer
->peer_id
, vns
);
474 // MARK: Backup Peers
477 void SOSBackupPeerPostNotification(const char *reason
) {
478 // Let sbd know when a notable event occurs
480 // - Backup bag change
481 secnotice("backup", "posting notification to CloudServices: %s", reason
?reason
:"");
482 notify_post(kSecItemBackupNotification
);
485 static bool SOSPeerDoWithJournalPath(SOSPeerRef peer
, CFErrorRef
*error
, void(^with
)(const char *journalPath
)) {
486 // TODO: Probably switch to using CFURL to construct the path.
488 char strBuffer
[PATH_MAX
+ 1];
489 size_t userTempLen
= confstr(_CS_DARWIN_USER_TEMP_DIR
, strBuffer
, sizeof(strBuffer
));
490 if (userTempLen
== 0) {
491 ok
= SecCheckErrno(-1, error
, CFSTR("confstr on _CS_DARWIN_USER_TEMP_DIR returned an error."));
493 CFStringRef journalName
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%s/SOSBackup-%@"), strBuffer
, SOSPeerGetID(peer
));
494 CFStringPerformWithCString(journalName
, with
);
495 CFReleaseSafe(journalName
);
500 static FILE *fopen_journal(const char *journalPath
, const char *mode
, CFErrorRef
*error
) {
501 FILE *file
= fopen(journalPath
, mode
);
502 SecCheckErrno(!file
, error
, CFSTR("fopen %s,%s"), journalPath
, mode
);
506 #include <sys/stat.h>
509 static off_t
getFileSize(int fd
) {
510 return lseek(fd
, 0, SEEK_END
);
514 int SOSPeerHandoffFD(SOSPeerRef peer
, CFErrorRef
*error
) {
516 SOSPeerDoWithJournalPath(peer
, error
, ^(const char *journalName
) {
517 fd
= open(journalName
, O_RDONLY
| O_CLOEXEC
);
518 if (SecCheckErrno(fd
< 0, error
, CFSTR("open %s"), journalName
)) {
519 if (!SecCheckErrno(unlink(journalName
), error
, CFSTR("unlink %s"), journalName
)) {
523 secdebug("backup", "Handing off file %s with fd %d of size %llu", journalName
, fd
, getFileSize(fd
));
526 secdebug("backup", "Handing off file %s failed, %@", journalName
, error
?*error
:NULL
);
532 static CFDataRef
SOSPeerCopyAKSKeyBag(SOSPeerRef peer
, CFErrorRef
*error
) {
533 if (CFEqual(peer
->peer_id
, kSOSViewKeychainV0_tomb
)) {
534 return CFRetainSafe(peer
->_keyBag
);
536 CFDataRef aksKeybag
= NULL
;
537 SOSBackupSliceKeyBagRef backupSliceKeyBag
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, peer
->_keyBag
, error
);
538 if (backupSliceKeyBag
) {
539 aksKeybag
= SOSBSKBCopyAKSBag(backupSliceKeyBag
, error
);
540 CFRelease(backupSliceKeyBag
);
546 bool SOSPeerAppendToJournal(SOSPeerRef peer
, CFErrorRef
*error
, void(^with
)(FILE *journalFile
, keybag_handle_t kbhandle
)) {
547 __block
bool ok
= true;
548 // We only need a keybag if we are writing ADDs. Since we don't know at this layer
549 // what operations we may be doing, open keybag if we have one, otherwise don't
550 ok
&= SOSPeerDoWithJournalPath(peer
, error
, ^(const char *fname
) {
551 FILE *file
= fopen_journal(fname
, "a", error
);
553 keybag_handle_t kbhandle
= bad_keybag_handle
;
554 CFDataRef keybag
= SOSPeerCopyAKSKeyBag(peer
, error
);
556 if (ok
&& (ok
= ks_open_keybag(keybag
, NULL
, &kbhandle
, error
))) {
557 with(file
, kbhandle
);
558 if (kbhandle
!= bad_keybag_handle
)
559 ok
&= ks_close_keybag(kbhandle
, error
);
561 CFReleaseSafe(keybag
);
568 static bool SOSPeerTruncateJournal(SOSPeerRef peer
, CFErrorRef
*error
, void(^with
)(FILE *journalFile
)) {
569 __block
bool ok
= true;
570 ok
&= SOSPeerDoWithJournalPath(peer
, error
, ^(const char *fname
) {
571 FILE *file
= fopen_journal(fname
, "w", error
);
580 bool SOSPeerSetState(SOSPeerRef p
, SOSEngineRef engine
, CFDictionaryRef state
, CFErrorRef
*error
) {
583 SOSPeerGetOptionalPersistedCFIndex(state
, kSOSPeerVersionKey
, &p
->version
);
585 p
->sequenceNumber
= SOSPeerGetPersistedInt64(state
, kSOSPeerSequenceNumberKey
);
586 p
->mustSendMessage
= SOSPeerGetPersistedBoolean(state
, kSOSPeerMustSendMessageKey
);
587 p
->sendObjects
= SOSPeerGetPersistedBoolean(state
, kSOSPeerSendObjectsKey
);
588 CFRetainAssign(p
->views
, SOSPeerGetPersistedViewNameSet(p
, state
, kSOSPeerViewsKey
));
589 SOSPeerSetKeyBag(p
, SOSPeerGetPersistedData(state
, kSOSPeerKeyBagKey
));
590 // Don't rollback coder in memory if a transaction is rolled back, since this
591 // might lead to reuse of an IV.
593 SOSPeerSetCoderData(p
, SOSPeerGetPersistedData(state
, kSOSPeerCoderKey
));
594 CFAssignRetained(p
->pendingObjects
, SOSEngineCopyPersistedManifest(engine
, state
, kSOSPeerPendingObjectsKey
));
595 CFAssignRetained(p
->unwantedManifest
, SOSEngineCopyPersistedManifest(engine
, state
, kSOSPeerUnwantedManifestKey
));
596 CFAssignRetained(p
->confirmedManifest
, SOSEngineCopyPersistedManifest(engine
, state
, kSOSPeerConfirmedManifestKey
));
597 CFAssignRetained(p
->proposedManifests
, SOSEngineCopyPersistedManifestArray(engine
, state
, kSOSPeerProposedManifestKey
, error
));
598 ok
&= p
->proposedManifests
!= NULL
;
599 CFAssignRetained(p
->localManifests
, SOSEngineCopyPersistedManifestArray(engine
, state
, kSOSPeerLocalManifestKey
, error
));
600 ok
&= p
->localManifests
!= NULL
;
605 static SOSPeerRef
SOSPeerCreate_Internal(SOSEngineRef engine
, CFDictionaryRef state
, CFStringRef theirPeerID
, CFIndex version
, CFErrorRef
*error
) {
606 SOSPeerRef p
= CFTypeAllocate(SOSPeer
, struct __OpaqueSOSPeer
, kCFAllocatorDefault
);
607 p
->peer_id
= CFRetainSafe(theirPeerID
);
608 p
->version
= version
;
609 CFDictionaryRef empty
= NULL
;
611 empty
= CFDictionaryCreate(kCFAllocatorDefault
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
614 if (!SOSPeerSetState(p
, engine
, state
, error
)) {
617 CFReleaseNull(empty
);
621 static bool SOSPeerPersistOptionalCoder(SOSPeerRef peer
, CFMutableDictionaryRef persist
, CFStringRef key
, CFErrorRef
*error
) {
622 CFDataRef coderData
= NULL
;
623 bool ok
= SOSPeerCopyCoderData(peer
, &coderData
, error
);
625 CFDictionarySetValue(persist
, key
, coderData
);
626 CFReleaseSafe(coderData
);
631 static void SOSPeerPersistBool(CFMutableDictionaryRef persist
, CFStringRef key
, bool value
) {
632 CFDictionarySetValue(persist
, key
, value
? kCFBooleanTrue
: kCFBooleanFalse
);
635 static void SOSPeerPersistInt64(CFMutableDictionaryRef persist
, CFStringRef key
, int64_t value
) {
636 CFNumberRef number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt64Type
, &value
);
637 CFDictionarySetValue(persist
, key
, number
);
638 CFReleaseSafe(number
);
641 static void SOSPeerPersistCFIndex(CFMutableDictionaryRef persist
, CFStringRef key
, CFIndex value
) {
642 CFNumberRef number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &value
);
643 CFDictionarySetValue(persist
, key
, number
);
644 CFReleaseSafe(number
);
647 static bool SOSPeerPersistOptionalManifest(CFMutableDictionaryRef persist
, CFStringRef key
, SOSManifestRef manifest
, CFErrorRef
*error
) {
650 CFDataRef digest
= SOSManifestGetDigest(manifest
, error
);
653 CFDictionarySetValue(persist
, key
, digest
);
657 static bool SSOSPeerPersistManifestArray(CFMutableDictionaryRef persist
, CFStringRef key
, CFArrayRef manifests
, CFErrorRef
*error
) {
658 CFMutableArrayRef digests
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
659 SOSManifestRef manifest
;
660 if (manifests
) CFArrayForEachC(manifests
, manifest
) {
661 CFDataRef digest
= SOSManifestGetDigest(manifest
, error
);
663 CFReleaseNull(digests
);
665 CFArrayAppendValue(digests
, digest
);
669 CFDictionarySetValue(persist
, key
, digests
);
675 static void SOSPeerPersistOptionalValue(CFMutableDictionaryRef persist
, CFStringRef key
, CFTypeRef value
) {
677 CFDictionarySetValue(persist
, key
, value
);
680 CFDictionaryRef
SOSPeerCopyState(SOSPeerRef peer
, CFErrorRef
*error
) {
681 CFMutableDictionaryRef state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
682 SOSPeerPersistInt64(state
, kSOSPeerSequenceNumberKey
, peer
->sequenceNumber
);
684 SOSPeerPersistCFIndex(state
, kSOSPeerVersionKey
, peer
->version
);
687 SOSPeerPersistBool(state
, kSOSPeerMustSendMessageKey
, peer
->mustSendMessage
);
688 SOSPeerPersistBool(state
, kSOSPeerSendObjectsKey
, peer
->sendObjects
);
689 SOSPeerPersistOptionalValue(state
, kSOSPeerViewsKey
, peer
->views
);
691 CFDataRef keybag
= SOSPeerGetKeyBag(peer
);
692 if (keybag
&& !CFEqual(peer
->peer_id
, kSOSViewKeychainV0_tomb
))
693 SOSPeerPersistOptionalValue(state
, kSOSPeerKeyBagKey
, keybag
);
695 if (!SOSPeerPersistOptionalCoder(peer
, state
, kSOSPeerCoderKey
, error
)
696 || !SOSPeerPersistOptionalManifest(state
, kSOSPeerPendingObjectsKey
, peer
->pendingObjects
, error
)
697 || !SOSPeerPersistOptionalManifest(state
, kSOSPeerUnwantedManifestKey
, peer
->unwantedManifest
, error
)
698 || !SOSPeerPersistOptionalManifest(state
, kSOSPeerConfirmedManifestKey
, peer
->confirmedManifest
, error
)
699 || !SSOSPeerPersistManifestArray(state
, kSOSPeerProposedManifestKey
, peer
->proposedManifests
, error
)
700 || !SSOSPeerPersistManifestArray(state
, kSOSPeerLocalManifestKey
, peer
->localManifests
, error
)) {
701 CFReleaseNull(state
);
706 SOSPeerRef
SOSPeerCreateWithState(SOSEngineRef engine
, CFStringRef peer_id
, CFDictionaryRef state
, CFErrorRef
*error
) {
707 return SOSPeerCreate_Internal(engine
, state
, peer_id
, 0, error
);
710 static void SOSPeerDestroy(CFTypeRef cf
) {
711 SOSPeerRef peer
= (SOSPeerRef
)cf
;
712 CFReleaseNull(peer
->peer_id
);
713 CFReleaseNull(peer
->views
);
714 SOSPeerSetCoderData(peer
, NULL
);
715 CFReleaseNull(peer
->pendingObjects
);
716 CFReleaseNull(peer
->unwantedManifest
);
717 CFReleaseNull(peer
->confirmedManifest
);
718 CFReleaseNull(peer
->proposedManifests
);
719 CFReleaseNull(peer
->localManifests
);
722 bool SOSPeerDidConnect(SOSPeerRef peer
) {
723 SOSPeerSetMustSendMessage(peer
, true);
724 SOSPeerSetProposedManifest(peer
, SOSPeerGetConfirmedManifest(peer
));
725 // TODO: Return false if nothing changed.
731 CFIndex
SOSPeerGetVersion(SOSPeerRef peer
) {
732 return peer
->version
;
735 CFStringRef
SOSPeerGetID(SOSPeerRef peer
) {
736 return peer
->peer_id
;
739 CFSetRef
SOSPeerGetViewNameSet(SOSPeerRef peer
) {
743 void SOSPeerSetViewNameSet(SOSPeerRef peer
, CFSetRef views
) {
744 CFRetainAssign(peer
->views
, views
);
747 CFDataRef
SOSPeerGetKeyBag(SOSPeerRef peer
) {
748 return peer
->_keyBag
;
751 static bool SOSPeerUnlinkBackupJournal(SOSPeerRef peer
, CFErrorRef
*error
) {
752 __block
bool ok
= true;
753 ok
&= SOSPeerDoWithJournalPath(peer
, error
, ^(const char *journalName
) {
754 secnotice("backup", "%@ unlinking journal file %s", peer
, journalName
);
755 ok
&= SecCheckErrno(unlink(journalName
), error
, CFSTR("unlink %s"), journalName
);
760 static bool SOSPeerWriteReset(SOSPeerRef peer
, CFErrorRef
*error
) {
761 __block
bool ok
= true;
762 __block CFErrorRef localError
= NULL
;
763 ok
&= SOSPeerTruncateJournal(peer
, &localError
, ^(FILE *journalFile
) {
764 ok
= SOSBackupEventWriteReset(journalFile
, peer
->_keyBag
, &localError
);
765 if (ok
&& !peer
->_keyBag
)
766 ok
= SOSBackupEventWriteCompleteMarker(journalFile
, 999, &localError
);
769 secwarning("%@ failed to write reset to backup journal: %@", peer
->peer_id
, localError
);
770 CFErrorPropagate(localError
, error
);
773 // Forget we ever wrote anything to the journal.
774 SOSPeerSetConfirmedManifest(peer
, NULL
);
775 SOSPeerSetProposedManifest(peer
, NULL
);
777 SOSPeerSetMustSendMessage(peer
, !ok
);
781 void SOSPeerKeyBagDidChange(SOSPeerRef peer
) {
782 // If !keyBag unlink the file, instead of writing a reset.
783 // CloudServices does not want to hear about empty keybags
784 SOSPeerSetSendObjects(peer
, false);
785 if (!peer
->_keyBag
) {
786 SOSPeerUnlinkBackupJournal(peer
, NULL
);
788 // Attempt to write a reset (ignoring failures since it will
789 // be pended stickily if it fails).
790 SOSPeerWriteReset(peer
, NULL
);
791 SOSCCSyncWithAllPeers();
795 void SOSPeerSetKeyBag(SOSPeerRef peer
, CFDataRef keyBag
) {
796 if (CFEqualSafe(keyBag
, peer
->_keyBag
)) return;
797 bool hadKeybag
= peer
->_keyBag
;
799 secwarning("%@ keybag for backup unset", SOSPeerGetID(peer
));
801 secnotice("backup", "%@ backup bag: %@", SOSPeerGetID(peer
), keyBag
);
803 CFRetainAssign(peer
->_keyBag
, keyBag
);
804 // Don't call SOSPeerKeybagDidChange for the inital edge from NULL -> having a keybag.
806 SOSPeerKeyBagDidChange(peer
);
810 bool SOSPeerWritePendingReset(SOSPeerRef peer
, CFErrorRef
*error
) {
811 return !SOSPeerMustSendMessage(peer
) || SOSPeerWriteReset(peer
, error
);
814 uint64_t SOSPeerNextSequenceNumber(SOSPeerRef peer
) {
815 return ++peer
->sequenceNumber
;
818 uint64_t SOSPeerGetMessageVersion(SOSPeerRef peer
) {
819 return SOSPeerGetVersion(peer
);
822 bool SOSPeerMustSendMessage(SOSPeerRef peer
) {
823 return peer
->mustSendMessage
;
826 void SOSPeerSetMustSendMessage(SOSPeerRef peer
, bool sendMessage
) {
827 peer
->mustSendMessage
= sendMessage
;
830 bool SOSPeerSendObjects(SOSPeerRef peer
) {
831 return peer
->sendObjects
;
834 void SOSPeerSetSendObjects(SOSPeerRef peer
, bool sendObjects
) {
835 peer
->sendObjects
= sendObjects
;
840 SOSManifestRef
SOSPeerGetProposedManifest(SOSPeerRef peer
) {
841 if (peer
->proposedManifests
&& CFArrayGetCount(peer
->proposedManifests
))
842 return (SOSManifestRef
)CFArrayGetValueAtIndex(peer
->proposedManifests
, 0);
846 SOSManifestRef
SOSPeerGetConfirmedManifest(SOSPeerRef peer
) {
847 return peer
->confirmedManifest
;
850 void SOSPeerSetConfirmedManifest(SOSPeerRef peer
, SOSManifestRef confirmed
) {
851 CFRetainAssign(peer
->confirmedManifest
, confirmed
);
853 // TODO: Clear only expired pending and local manifests from the array - this clears them all
854 // To do so we'd have to track the messageIds we sent to our peer and when we proposed a particular manifest.
855 // Then we simply remove the entries from messages older than the one we are confirming now
856 //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerProposedManifestKey));
857 //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerLocalManifestKey));
860 void SOSPeerAddProposedManifest(SOSPeerRef peer
, SOSManifestRef proposed
) {
861 SOSManifestArrayAppendManifest(&peer
->proposedManifests
, proposed
);
864 void SOSPeerSetProposedManifest(SOSPeerRef peer
, SOSManifestRef proposed
) {
865 SOSManifestArraySetManifest(&peer
->proposedManifests
, proposed
);
868 void SOSPeerAddLocalManifest(SOSPeerRef peer
, SOSManifestRef local
) {
869 SOSManifestArrayAppendManifest(&peer
->localManifests
, local
);
872 SOSManifestRef
SOSPeerGetPendingObjects(SOSPeerRef peer
) {
873 return peer
->pendingObjects
;
876 void SOSPeerSetPendingObjects(SOSPeerRef peer
, SOSManifestRef pendingObjects
) {
877 CFRetainAssign(peer
->pendingObjects
, pendingObjects
);
880 SOSManifestRef
SOSPeerGetUnwantedManifest(SOSPeerRef peer
) {
881 return peer
->unwantedManifest
;
884 void SOSPeerSetUnwantedManifest(SOSPeerRef peer
, SOSManifestRef unwantedManifest
) {
885 CFRetainAssign(peer
->unwantedManifest
, unwantedManifest
);
888 SOSManifestRef
SOSPeerCopyManifestForDigest(SOSPeerRef peer
, CFDataRef digest
) {
889 if (!digest
) return NULL
;
890 SOSManifestRef manifest
;
891 if (peer
->proposedManifests
) CFArrayForEachC(peer
->proposedManifests
, manifest
) {
892 if (CFEqual(digest
, SOSManifestGetDigest(manifest
, NULL
)))
893 return CFRetainSafe(manifest
);
895 if (peer
->localManifests
) CFArrayForEachC(peer
->localManifests
, manifest
) {
896 if (CFEqual(digest
, SOSManifestGetDigest(manifest
, NULL
)))
897 return CFRetainSafe(manifest
);
899 if (peer
->confirmedManifest
&& CFEqual(digest
, SOSManifestGetDigest(peer
->confirmedManifest
, NULL
)))
900 return CFRetainSafe(peer
->confirmedManifest
);
905 static void SOSMarkManifestInUse(struct SOSDigestVector
*mdInUse
, SOSManifestRef manifest
) {
906 CFDataRef digest
= SOSManifestGetDigest(manifest
, NULL
);
908 SOSDigestVectorAppend(mdInUse
, CFDataGetBytePtr(digest
));
911 static void SOSMarkManifestsInUse(struct SOSDigestVector
*mdInUse
, CFArrayRef manifests
) {
912 if (!isArray(manifests
)) return;
913 SOSManifestRef manifest
= NULL
;
914 CFArrayForEachC(manifests
, manifest
) {
915 SOSMarkManifestInUse(mdInUse
, manifest
);
919 // Add all digests we are using to mdInUse
920 void SOSPeerMarkDigestsInUse(SOSPeerRef peer
, struct SOSDigestVector
*mdInUse
) {
921 SOSMarkManifestInUse(mdInUse
, peer
->pendingObjects
);
922 SOSMarkManifestInUse(mdInUse
, peer
->unwantedManifest
);
923 SOSMarkManifestInUse(mdInUse
, peer
->confirmedManifest
);
924 SOSMarkManifestsInUse(mdInUse
, peer
->localManifests
);
925 SOSMarkManifestsInUse(mdInUse
, peer
->proposedManifests
);
928 static void SOSAddManifestInUse(CFMutableDictionaryRef mfc
, SOSManifestRef manifest
) {
929 CFDataRef digest
= SOSManifestGetDigest(manifest
, NULL
);
930 CFDataRef data
= SOSManifestGetData(manifest
);
932 CFDictionarySetValue(mfc
, digest
, data
);
935 static void SOSAddManifestsInUse(CFMutableDictionaryRef mfc
, CFArrayRef manifests
) {
936 if (!isArray(manifests
)) return;
937 SOSManifestRef manifest
= NULL
;
938 CFArrayForEachC(manifests
, manifest
) {
939 SOSAddManifestInUse(mfc
, manifest
);
943 void SOSPeerAddManifestsInUse(SOSPeerRef peer
, CFMutableDictionaryRef mfc
) {
944 SOSAddManifestInUse(mfc
, peer
->pendingObjects
);
945 SOSAddManifestInUse(mfc
, peer
->unwantedManifest
);
946 SOSAddManifestInUse(mfc
, peer
->confirmedManifest
);
947 SOSAddManifestsInUse(mfc
, peer
->localManifests
);
948 SOSAddManifestsInUse(mfc
, peer
->proposedManifests
);
954 // additionsFromRemote
955 // original intent was that digests only got added to pendingObjects. We only know for sure if it is something added locally via api call
958 bool SOSPeerDidReceiveRemovalsAndAdditions(SOSPeerRef peer
, SOSManifestRef absentFromRemote
, SOSManifestRef additionsFromRemote
, SOSManifestRef unwantedFromRemote
,
959 SOSManifestRef local
, CFErrorRef
*error
) {
960 // We assume that incoming manifests are all sorted, and absentFromRemote is disjoint from additionsFromRemote
962 SOSManifestRef remoteMissing
= NULL
, sharedRemovals
= NULL
, sharedAdditions
= NULL
;
964 // TODO: Simplify -- a lot.
965 ok
= ok
&& (remoteMissing
= SOSManifestCreateIntersection(absentFromRemote
, local
, error
)); // remoteMissing = absentFromRemote <Intersected> local
966 ok
= ok
&& (sharedRemovals
= SOSManifestCreateComplement(remoteMissing
, absentFromRemote
, error
)); // sharedRemovals = absentFromRemote - remoteMissing
967 ok
= ok
&& (sharedAdditions
= SOSManifestCreateIntersection(additionsFromRemote
, local
, error
)); // sharedAdditions = additionsFromRemote <Intersected> local
968 //ok = ok && (remoteAdditions = SOSManifestCreateComplement(sharedAdditions, additionsFromRemote, error)); // remoteAdditions = additionsFromRemote - sharedAdditions
970 // remoteMissing are things we have that remote has asked for => add to pendingObjects
971 // sharedRemovals are things we don't have that remote has asked for => remove from pendingDeletes
972 // sharedAdditions are things we have that remote has too => remove from pendingObjects
973 // remoteAdditions are things that remote said they have that we don't and we should probably ask for => add to pendingDeletes?
974 // unwantedFromRemote are things we received from remote for which we already have a newer object according to the conflict resolver.
975 secnotice("peer", "%@ RM:%@ SR:%@ SA:%@ UR:%@", peer
, remoteMissing
, sharedRemovals
, sharedAdditions
, unwantedFromRemote
);
977 SOSManifestRef pendingObjectsManifest
= SOSManifestCreateWithPatch(peer
->pendingObjects
, sharedAdditions
, remoteMissing
, error
);
978 SOSManifestRef unwantedManifest
= SOSManifestCreateWithPatch(peer
->unwantedManifest
, sharedRemovals
, unwantedFromRemote
, error
);
979 CFAssignRetained(peer
->pendingObjects
, pendingObjectsManifest
); // PO = PO - sharedAdditions + remoteMissing
980 CFAssignRetained(peer
->unwantedManifest
, unwantedManifest
); // U = U - sharedRemovals + unwantedFromRemote
982 CFReleaseSafe(remoteMissing
);
983 CFReleaseSafe(sharedRemovals
);
984 CFReleaseSafe(sharedAdditions
);
986 secnotice("peer", "%@ C:%@ U:%@ O:%@", peer
, SOSPeerGetConfirmedManifest(peer
), SOSPeerGetUnwantedManifest(peer
), SOSPeerGetPendingObjects(peer
));
991 // Called for a normal syncing peer. Only updates pendingObjects currently.
992 bool SOSPeerDataSourceWillCommit(SOSPeerRef peer
, SOSDataSourceTransactionSource source
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
993 bool isAPITransaction
= source
== kSOSDataSourceAPITransaction
;
994 SOSManifestRef unconfirmedAdditions
= NULL
;
995 if (isAPITransaction
&& SOSManifestGetCount(additions
)) {
996 // Remove confirmed from additions, leaving us with additions to the local db that the remote peer doesn't have yet
997 unconfirmedAdditions
= SOSManifestCreateComplement(SOSPeerGetConfirmedManifest(peer
), additions
, error
);
1000 if (SOSManifestGetCount(removals
) || SOSManifestGetCount(unconfirmedAdditions
)) {
1001 SOSManifestRef pendingObjectsManifest
= SOSManifestCreateWithPatch(peer
->pendingObjects
, removals
, unconfirmedAdditions
, error
);
1004 // TODO: Only do this if debugScope "peer", notice is enabled.
1005 // if (!SecIsScopeActive(kSecLevelNotice, "peer"))
1007 // pended == UA unless the db is renotifying of an addition for something we already have
1008 SOSManifestRef unpended
= NULL
, pended
= NULL
;
1009 SOSManifestDiff(peer
->pendingObjects
, pendingObjectsManifest
, &unpended
, &pended
, error
);
1010 secinfo("peer", "%@: willCommit R:%@ A:%@ UA:%@ %s O%s%@%s%@",
1011 SOSPeerGetID(peer
), removals
, additions
, unconfirmedAdditions
,
1012 (isAPITransaction
? "api": "sos"),
1013 (SOSManifestGetCount(unpended
) ? "-" : ""),
1014 (SOSManifestGetCount(unpended
) ? (CFStringRef
)unpended
: CFSTR("")),
1015 (SOSManifestGetCount(pended
) ? "+" : SOSManifestGetCount(unpended
) ? "" : "="),
1016 (SOSManifestGetCount(pended
) ? (CFStringRef
)pended
: CFSTR("")));
1017 CFReleaseSafe(unpended
);
1018 CFReleaseSafe(pended
);
1020 //#endif /* DEBUG */
1021 CFAssignRetained(peer
->pendingObjects
, pendingObjectsManifest
);
1023 CFReleaseSafe(unconfirmedAdditions
);
1028 bool SOSPeerWriteAddEvent(FILE *journalFile
, keybag_handle_t kbhandle
, SOSDataSourceRef dataSource
, SOSObjectRef object
, CFErrorRef
*error
) {
1029 CFDictionaryRef backup_item
= NULL
;
1030 bool ok
= ((backup_item
= SOSObjectCopyBackup(dataSource
, object
, kbhandle
, error
))
1031 && SOSBackupEventWriteAdd(journalFile
, backup_item
, error
));
1032 CFReleaseSafe(backup_item
);
1036 // Called for a backup peer, should stream objects in changes right away
1037 bool SOSPeerDataSourceWillChange(SOSPeerRef peer
, SOSDataSourceRef dataSource
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef
*error
) {
1038 __block
bool ok
= true;
1039 ok
&= SOSPeerWritePendingReset(peer
, error
) && SOSPeerAppendToJournal(peer
, error
, ^(FILE *journalFile
, keybag_handle_t kbhandle
) {
1040 struct SOSDigestVector dvdel
= SOSDigestVectorInit
;
1041 struct SOSDigestVector dvadd
= SOSDigestVectorInit
;
1042 SOSChangeRef change
;
1043 CFArrayForEachC(changes
, change
) {
1044 bool isDelete
= false;
1045 CFErrorRef localError
= NULL
;
1046 CFDataRef digest
= NULL
;
1047 SOSObjectRef object
= NULL
;
1048 bool ok
= digest
= SOSChangeCopyDigest(dataSource
, change
, &isDelete
, &object
, &localError
);
1050 ok
&= SOSBackupEventWriteDelete(journalFile
, digest
, &localError
);
1051 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(digest
));
1053 ok
&= SOSPeerWriteAddEvent(journalFile
, kbhandle
, dataSource
, object
, &localError
);
1054 SOSDigestVectorAppend(&dvadd
, CFDataGetBytePtr(digest
));
1057 secerror("bad change %@: %@", change
, localError
);
1059 CFReleaseSafe(digest
);
1060 CFReleaseSafe(localError
);
1064 // Update our proposed manifest since we just wrote stuff
1065 struct SOSDigestVector dvresult
= SOSDigestVectorInit
;
1066 SOSDigestVectorSort(&dvdel
);
1067 SOSDigestVectorSort(&dvadd
);
1068 if ((ok
= SOSDigestVectorPatchSorted(SOSManifestGetDigestVector(SOSPeerGetProposedManifest(peer
)), &dvdel
,
1069 &dvadd
, &dvresult
, error
))) {
1070 SOSManifestRef proposed
;
1071 ok
= proposed
= SOSManifestCreateWithDigestVector(&dvresult
, error
);
1072 SOSPeerSetProposedManifest(peer
, proposed
);
1073 CFReleaseSafe(proposed
);
1075 SOSDigestVectorFree(&dvresult
);
1077 SOSDigestVectorFree(&dvdel
);
1078 SOSDigestVectorFree(&dvadd
);
1080 // Only Write marker if we are actually in sync now (local == propopsed).
1081 if (SOSPeerSendObjects(peer
))
1082 SOSBackupEventWriteCompleteMarker(journalFile
, 799, error
);
1086 // We were unable to stream everything out neatly
1087 SOSCCSyncWithAllPeers();