]> git.saurik.com Git - apple/security.git/blame - OSX/sec/SOSCircle/SecureObjectSync/SOSPeer.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSPeer.c
CommitLineData
5c19dc3a
A
1/*
2 * Copyright (c) 2012-2014 Apple 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
24
25/*
26 * SOSPeer.c - Implementation of a secure object syncing peer
27 */
28#include <Security/SecureObjectSync/SOSPeer.h>
29
5c19dc3a
A
30#include <Security/SecureObjectSync/SOSDigestVector.h>
31#include <Security/SecureObjectSync/SOSInternal.h>
32#include <Security/SecureObjectSync/SOSTransport.h>
33#include <Security/SecureObjectSync/SOSViews.h>
34#include <Security/SecureObjectSync/SOSChangeTracker.h>
35#include <utilities/SecCFError.h>
36#include <utilities/SecCFRelease.h>
37#include <utilities/SecCFWrappers.h>
38#include <utilities/SecDb.h>
39#include <utilities/SecFileLocations.h>
40#include <utilities/SecIOFormat.h>
41#include <utilities/array_size.h>
42#include <utilities/debugging.h>
43#include <Security/SecureObjectSync/SOSBackupEvent.h>
44#include <Security/SecItemBackup.h>
45
46#include <securityd/SOSCloudCircleServer.h>
47
48#include <CoreFoundation/CoreFoundation.h>
49
50#include <stdlib.h>
51
52#include <AssertMacros.h>
53
54// Backup Peer Support
55#include <securityd/SecKeybagSupport.h>
56#include <notify.h>
57
58
59//
60// MARK: - SOSPeerPersistence code
61//
62static CFStringRef kSOSPeerSequenceNumberKey = CFSTR("sequence-number");
5c19dc3a
A
63
64CFStringRef kSOSPeerDataLabel = CFSTR("iCloud Peer Data Meta-data");
65
66//
67// MARK: SOSPeerState (dictionary keys)
68//
69
70// PeerState dictionary keys
71static CFStringRef kSOSPeerSendObjectsKey = CFSTR("send-objects"); // bool
72static CFStringRef kSOSPeerMustSendMessageKey = CFSTR("must-send"); // bool
73static CFStringRef kSOSPeerPendingObjectsKey = CFSTR("pending-objects"); // digest
74static CFStringRef kSOSPeerUnwantedManifestKey = CFSTR("unwanted-manifest"); // digest
75static CFStringRef kSOSPeerConfirmedManifestKey = CFSTR("confirmed-manifest"); //digest
76static CFStringRef kSOSPeerProposedManifestKey = CFSTR("pending-manifest"); // array of digests
77static CFStringRef kSOSPeerLocalManifestKey = CFSTR("local-manifest"); // array of digests
78static CFStringRef kSOSPeerVersionKey = CFSTR("vers"); // int
5c19dc3a
A
79
80//
81// SOSPeerMeta keys that can also be used in peerstate...
82//
83static CFStringRef kSOSPeerPeerIDKey = CFSTR("peer-id"); // string
84static CFStringRef kSOSPeerViewsKey = CFSTR("views"); // set (or array) of string
85static CFStringRef kSOSPeerKeyBagKey = CFSTR("keybag"); // data
86
87/*
88 Theory of syncing for both incoming and outgoing messages
89
90 A peerstate consists of:
91 (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)})
92 T: to be sent (pendingObjects) manifest
93 U: unwanted objects manifest
94 C: confirmed manifest
95 P: proposed manifest
96 D->M? digest or manifest to optional manifest function
97 M->D manifest to digest of manifest function
98 H->O? hash (manifest entry) to optional object function (the datasource)
99 O->{ Mapping from incoming O objects to one of:
100 (Op,Or): Op = Peers object, Or is replaced local object.
101 Oa,(Op,Or): Oa = appeared local object, Or = Oa, see above for (Op, Or)
102 (Om,Oa),(Op,Or): Om missing local object, was apparently Oa instead see above for (Oa, Op, Or)
103 Om,Oi: Om missing local object, inserted Oi (nothing replaced), but Om still disapeared from manifest
104 Oi Oi inserted object from peer (nothing replaced)
105 Ob: Ob both remote and local object are identical, nothing changed
106 Oa,Ob: Oa = appeared local object equal to Oa = Ob. Equivalent to single Oi
107 (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
108 (Ol,Ou): Ol local object wins from peers Ou older object => append Ou to U
109 Oa,(Ol,Ou): Oa appeared as a local object and Oa = Ol above
110 (Om,Oa),(Ol,Ou): Om removed and Oa replaced Om and Oa = Ol as above
111 }
112 A message consists of
113 (B,M,E,O)
114 B: base digest
115 M: missing manifest
116 E: extra manifest
117 O: objects
118
119 To send a message we simply compute:
120 B: D->M->C
121 M: C \ L
122 E: L \ C # subsetted if not all of O is sent
123 O: H->O?->O # as size permits
124 and change P to (C \ M:) union E: union O->H->O:
125
126 To receive a message we compute
127 Op,Oi,Ob,Oo and Om,Oa,Ol
128 C: (D->M->B \ M) union E union O->H->O
129 A = E \ (O->H->O)
130 T: T \ (Om union Or union A union Oa,Ob.last union (Om,Oa),Ob.last ) union (M intersect L)
131 */
132
133
134//
135// MARK: SOSPeerMeta
136//
137
138static SOSPeerMetaRef SOSPeerMetaCreate(CFStringRef peerID) {
139 return CFRetain(peerID);
140}
141
142static SOSPeerMetaRef SOSPeerMetaCreateWithViews(CFStringRef peerID, CFSetRef views) {
143 const void *keys[] = { kSOSPeerPeerIDKey, kSOSPeerViewsKey };
144 const void *values[] = { peerID, views };
145 return CFDictionaryCreate(kCFAllocatorDefault, keys, values, array_size(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
146}
147
148static SOSPeerMetaRef SOSPeerMetaCreateWithViewsAndKeyBag(CFStringRef peerID, CFSetRef views, CFDataRef keybag) {
149 const void *keys[] = { kSOSPeerPeerIDKey, kSOSPeerViewsKey, kSOSPeerKeyBagKey };
150 const void *values[] = { peerID, views, keybag };
151 return CFDictionaryCreate(kCFAllocatorDefault, keys, values, array_size(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
152}
153
154SOSPeerMetaRef SOSPeerMetaCreateWithComponents(CFStringRef peerID, CFSetRef views, CFDataRef keybag) {
155 if (!isString(peerID))
156 return NULL;
157 if (views) {
158 if (keybag)
159 return SOSPeerMetaCreateWithViewsAndKeyBag(peerID, views, keybag);
160 else
161 return SOSPeerMetaCreateWithViews(peerID, views);
162 } else
163 return SOSPeerMetaCreate(peerID);
164}
165
166SOSPeerMetaRef SOSPeerMetaCreateWithState(CFStringRef peerID, CFDictionaryRef state) {
167 return SOSPeerMetaCreateWithComponents(peerID, CFDictionaryGetValue(state, kSOSPeerViewsKey), CFDictionaryGetValue(state, kSOSPeerKeyBagKey));
168}
169
170CFStringRef SOSPeerMetaGetComponents(SOSPeerMetaRef peerMeta, CFSetRef *views, CFDataRef *keybag, CFErrorRef *error) {
171 if (isDictionary(peerMeta)) {
172 CFDictionaryRef meta = (CFDictionaryRef)peerMeta;
173 CFStringRef peerID = asString(CFDictionaryGetValue(meta, kSOSPeerPeerIDKey), error);
174 CFSetRef vns = asSet(CFDictionaryGetValue(meta, kSOSPeerViewsKey), error);
175 if (vns && asDataOptional(CFDictionaryGetValue(meta, kSOSPeerKeyBagKey), keybag, error)) {
176 if (views)
177 *views = vns;
178 return peerID;
179 }
180 return NULL;
181 } else {
182 if (views) {
183 // Hack so tests can pass simple peerIDs
184 *views = SOSViewsGetV0ViewSet();
185 }
186 return asString(peerMeta, error);
187 }
188}
189
190CFTypeRef SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFTypeRef peerOrState, CFSetRef views, CFDataRef keyBag) {
191 assert(views);
192 if (peerOrState && CFGetTypeID(peerOrState) == SOSPeerGetTypeID()) {
193 // Inflated peer, update its views and move on
194 SOSPeerRef peer = (SOSPeerRef)peerOrState;
195 SOSPeerSetViewNameSet(peer, views);
196 SOSPeerSetKeyBag(peer, keyBag);
197 return CFRetainSafe(peer);
e0e0d90e
A
198 } else if (peerOrState && CFGetTypeID(peerOrState) == CFDictionaryGetTypeID()) {
199 CFMutableDictionaryRef state = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, peerOrState);
5c19dc3a
A
200 // Deserialized peer, just updated the serialized state with the new views
201 CFDictionarySetValue(state, kSOSPeerViewsKey, views);
202 if (keyBag)
203 CFDictionarySetValue(state, kSOSPeerKeyBagKey, keyBag);
204 else
205 CFDictionaryRemoveValue(state, kSOSPeerKeyBagKey);
e0e0d90e 206 return state;
5c19dc3a
A
207 } else {
208 // New peer, just create a state object.
209 if (keyBag)
210 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSOSPeerViewsKey, views, kSOSPeerKeyBagKey, keyBag, NULL);
211 else
212 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSOSPeerViewsKey, views, NULL);
213 }
214}
215
216CFTypeRef SOSPeerOrStateSetViewsAndCopyState(CFTypeRef peerOrState, CFSetRef views) {
217 assert(views);
218 if (peerOrState && CFGetTypeID(peerOrState) == SOSPeerGetTypeID()) {
219 // Inflated peer, update its views and deflate it
220 SOSPeerRef peer = (SOSPeerRef)peerOrState;
221 SOSPeerSetViewNameSet(peer, views);
222 return SOSPeerCopyState(peer, NULL);
e0e0d90e 223 } else if (peerOrState && CFGetTypeID(peerOrState) == CFDictionaryGetTypeID()) {
5c19dc3a 224 // We have a deflated peer. Update its views and keep it deflated
e0e0d90e 225 CFMutableDictionaryRef state = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, peerOrState);
5c19dc3a 226 CFDictionarySetValue(state, kSOSPeerViewsKey, views);
e0e0d90e 227 return state;
5c19dc3a
A
228 } else {
229 return NULL;
230 }
231}
232
233bool SOSPeerMapEntryIsBackup(const void *mapEntry) {
234 if (!mapEntry) return false;
235 if (CFGetTypeID(mapEntry) == SOSPeerGetTypeID()) {
236 return SOSPeerGetKeyBag((SOSPeerRef)mapEntry);
237 } else {
238 return CFDictionaryContainsKey(mapEntry, kSOSPeerKeyBagKey);
239 }
240}
241
242//
243// MARK: - SOSManifest
244//
245
246enum {
247 kSOSPeerMaxManifestWindowDepth = 4
248};
249
250static CFStringRef SOSManifestCreateOptionalDescriptionWithLabel(SOSManifestRef manifest, CFStringRef label) {
251 if (!manifest) return CFSTR(" - ");
252 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(" %@%@"), label, manifest);
253}
254
255static CFStringRef SOSManifestArrayCreateOptionalDescriptionWithLabel(CFArrayRef manifests, CFStringRef label) {
256 CFIndex count = manifests ? CFArrayGetCount(manifests) : 0;
257 if (count == 0) return CFSTR(" - ");
258 SOSManifestRef manifest = (SOSManifestRef)CFArrayGetValueAtIndex(manifests, 0);
259 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(" %@[%" PRIdCFIndex "]%@"), label, count, manifest);
260}
261
262static void SOSManifestArraySetManifest(CFMutableArrayRef *manifests, SOSManifestRef manifest) {
263 if (manifest) {
264 if (*manifests)
265 CFArrayRemoveAllValues(*manifests);
266 else
267 *manifests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
268 CFArrayAppendValue(*manifests, manifest);
269 } else {
270 CFReleaseNull(*manifests);
271 }
272}
273
274static void SOSManifestMutableArrayAppendManifest(CFMutableArrayRef manifests, SOSManifestRef manifest) {
275 if (manifest) {
276 CFIndex count = CFArrayGetCount(manifests);
277 CFIndex ixOfManifest = CFArrayGetFirstIndexOfValue(manifests, CFRangeMake(0, count), manifest);
278 if (ixOfManifest != 0) {
279 // If the manifest isn't at the front of the array move it there.
280 // If it's not in the array, remove enough entires from the end to
281 // make room to put it in the front.
282 if (ixOfManifest != kCFNotFound) {
283 CFArrayRemoveValueAtIndex(manifests, ixOfManifest);
284 } else {
285 while (count >= kSOSPeerMaxManifestWindowDepth)
286 CFArrayRemoveValueAtIndex(manifests, --count);
287 }
288
289 CFArrayInsertValueAtIndex(manifests, 0, manifest);
290 }
291 } else {
292 // pending == NULL => nothing clear history
293 CFArrayRemoveAllValues(manifests);
294 }
295}
296
297static void SOSManifestArrayAppendManifest(CFMutableArrayRef *manifests, SOSManifestRef manifest) {
298 if (*manifests)
299 SOSManifestMutableArrayAppendManifest(*manifests, manifest);
300 else
301 SOSManifestArraySetManifest(manifests, manifest);
302}
303
304//
305// MARK: - SOSPeer
306//
307
308struct __OpaqueSOSPeer {
309 CFRuntimeBase _base;
310
5c19dc3a
A
311 CFStringRef peer_id;
312 CFSetRef views;
313 CFIndex version;
314 uint64_t sequenceNumber;
315 bool mustSendMessage;
316 bool sendObjects;
317
318 SOSManifestRef pendingObjects;
319 SOSManifestRef unwantedManifest;
320 SOSManifestRef confirmedManifest;
321 CFMutableArrayRef proposedManifests;
322 CFMutableArrayRef localManifests;
323
324 // Only backup peers have these:
325 CFDataRef _keyBag;
326 FILE *journalFile;
327};
328
329CFGiblisWithCompareFor(SOSPeer)
330
331static CFStringRef SOSPeerCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
332 SOSPeerRef peer = (SOSPeerRef)cf;
333 if(peer){
334 CFStringRef po = SOSManifestCreateOptionalDescriptionWithLabel(peer->pendingObjects, CFSTR("O"));
335 CFStringRef uo = SOSManifestCreateOptionalDescriptionWithLabel(peer->unwantedManifest, CFSTR("U"));
336 CFStringRef co = SOSManifestCreateOptionalDescriptionWithLabel(peer->confirmedManifest, CFSTR("C"));
337 CFStringRef pe = SOSManifestArrayCreateOptionalDescriptionWithLabel(peer->proposedManifests, CFSTR("P"));
338 CFStringRef lo = SOSManifestArrayCreateOptionalDescriptionWithLabel(peer->localManifests, CFSTR("L"));
339 CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%@ %s%s%@%@%@%@%@>"),
340 SOSPeerGetID(peer),
341 SOSPeerMustSendMessage(peer) ? "F" : "f",
342 SOSPeerSendObjects(peer) ? "S" : "s",
343 po, uo, co, pe, lo);
344 CFReleaseSafe(lo);
345 CFReleaseSafe(pe);
346 CFReleaseSafe(co);
347 CFReleaseSafe(uo);
348 CFReleaseSafe(po);
349
350 return desc;
351 }
352 else
353 return CFSTR("NULL");
354}
355
356static Boolean SOSPeerCompare(CFTypeRef cfA, CFTypeRef cfB)
357{
358 SOSPeerRef peerA = (SOSPeerRef)cfA, peerB = (SOSPeerRef)cfB;
359 // Use mainly to see if peerB is actually this device (peerA)
360 return CFStringCompare(SOSPeerGetID(peerA), SOSPeerGetID(peerB), 0) == kCFCompareEqualTo;
361}
362
5c19dc3a
A
363
364static bool SOSPeerGetPersistedBoolean(CFDictionaryRef persisted, CFStringRef key) {
365 CFBooleanRef boolean = CFDictionaryGetValue(persisted, key);
366 return boolean && CFBooleanGetValue(boolean);
367}
368
369static CFDataRef SOSPeerGetPersistedData(CFDictionaryRef persisted, CFStringRef key) {
370 return asData(CFDictionaryGetValue(persisted, key), NULL);
371}
372
373static int64_t SOSPeerGetPersistedInt64(CFDictionaryRef persisted, CFStringRef key) {
374 int64_t integer = 0;
375 CFNumberRef number = CFDictionaryGetValue(persisted, key);
376 if (number) {
377 CFNumberGetValue(number, kCFNumberSInt64Type, &integer);
378 }
379 return integer;
380}
381
382static void SOSPeerGetOptionalPersistedCFIndex(CFDictionaryRef persisted, CFStringRef key, CFIndex *value) {
383 CFNumberRef number = CFDictionaryGetValue(persisted, key);
384 if (number) {
385 CFNumberGetValue(number, kCFNumberCFIndexType, value);
386 }
387}
388
389static CFSetRef SOSPeerGetPersistedViewNameSet(SOSPeerRef peer, CFDictionaryRef persisted, CFStringRef key) {
390 CFSetRef vns = CFDictionaryGetValue(persisted, key);
391 if (!vns) {
392 // Engine state in db contained a v0 peer, thus it must be in the V0ViewSet.
393 vns = SOSViewsGetV0ViewSet();
394 secnotice("peer", "%@ had no views, inferring: %@", peer->peer_id, vns);
395 }
396 return vns;
397}
398
399//
400// MARK: Backup Peers
401//
402
403void SOSBackupPeerPostNotification(const char *reason) {
404 // Let sbd know when a notable event occurs
405 // - Disk full
406 // - Backup bag change
407 secnotice("backup", "posting notification to CloudServices: %s", reason?reason:"");
408 notify_post(kSecItemBackupNotification);
409}
410
411static bool SOSPeerDoWithJournalPath(SOSPeerRef peer, CFErrorRef *error, void(^with)(const char *journalPath)) {
412 // TODO: Probably switch to using CFURL to construct the path.
413 bool ok = true;
414 char strBuffer[PATH_MAX + 1];
415 size_t userTempLen = confstr(_CS_DARWIN_USER_TEMP_DIR, strBuffer, sizeof(strBuffer));
416 if (userTempLen == 0) {
417 ok = SecCheckErrno(-1, error, CFSTR("confstr on _CS_DARWIN_USER_TEMP_DIR returned an error."));
418 } else {
419 CFStringRef journalName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s/SOSBackup-%@"), strBuffer, SOSPeerGetID(peer));
420 CFStringPerformWithCString(journalName, with);
421 CFReleaseSafe(journalName);
422 }
423 return ok;
424}
425
426static FILE *fopen_journal(const char *journalPath, const char *mode, CFErrorRef *error) {
427 FILE *file = fopen(journalPath, mode);
428 SecCheckErrno(!file, error, CFSTR("fopen %s,%s"), journalPath, mode);
429 return file;
430}
431
432#include <sys/stat.h>
433
434#if !defined(NDEBUG)
435static off_t getFileSize(int fd) {
436 return lseek(fd, 0, SEEK_END);
437}
438#endif
439
440int SOSPeerHandoffFD(SOSPeerRef peer, CFErrorRef *error) {
441 __block int fd = -1;
442 SOSPeerDoWithJournalPath(peer, error, ^(const char *journalName) {
443 fd = open(journalName, O_RDONLY | O_CLOEXEC);
444 if (SecCheckErrno(fd < 0, error, CFSTR("open %s"), journalName)) {
445 if (!SecCheckErrno(unlink(journalName), error, CFSTR("unlink %s"), journalName)) {
446 close(fd);
447 fd = -1;
448 } else {
449 secdebug("backup", "Handing off file %s with fd %d of size %llu", journalName, fd, getFileSize(fd));
450 }
451 } else {
452 secdebug("backup", "Handing off file %s failed, %@", journalName, error?*error:NULL);
453 }
454 });
455 return fd;
456}
457
458static CFDataRef SOSPeerCopyAKSKeyBag(SOSPeerRef peer, CFErrorRef *error) {
459 if (CFEqual(peer->peer_id, kSOSViewKeychainV0_tomb)) {
460 return CFRetainSafe(peer->_keyBag);
461 } else {
462 CFDataRef aksKeybag = NULL;
463 SOSBackupSliceKeyBagRef backupSliceKeyBag = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, peer->_keyBag, error);
464 if (backupSliceKeyBag) {
465 aksKeybag = SOSBSKBCopyAKSBag(backupSliceKeyBag, error);
466 CFRelease(backupSliceKeyBag);
467 }
468 return aksKeybag;
469 }
470}
471
472bool SOSPeerAppendToJournal(SOSPeerRef peer, CFErrorRef *error, void(^with)(FILE *journalFile, keybag_handle_t kbhandle)) {
473 __block bool ok = true;
474 // We only need a keybag if we are writing ADDs. Since we don't know at this layer
475 // what operations we may be doing, open keybag if we have one, otherwise don't
476 ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *fname) {
477 FILE *file = fopen_journal(fname, "a", error);
478 if (file) {
479 keybag_handle_t kbhandle = bad_keybag_handle;
480 CFDataRef keybag = SOSPeerCopyAKSKeyBag(peer, error);
481 ok = keybag;
482 if (ok && (ok = ks_open_keybag(keybag, NULL, &kbhandle, error))) {
483 with(file, kbhandle);
484 if (kbhandle != bad_keybag_handle)
485 ok &= ks_close_keybag(kbhandle, error);
486 }
487 CFReleaseSafe(keybag);
488 fclose(file);
489 }
490 });
491 return ok;
492}
493
494static bool SOSPeerTruncateJournal(SOSPeerRef peer, CFErrorRef *error, void(^with)(FILE *journalFile)) {
495 __block bool ok = true;
496 ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *fname) {
497 FILE *file = fopen_journal(fname, "w", error);
498 if (file) {
499 with(file);
500 fclose(file);
501 }
502 });
503 return ok;
504}
505
506bool SOSPeerSetState(SOSPeerRef p, SOSEngineRef engine, CFDictionaryRef state, CFErrorRef *error) {
507 bool ok = true;
508 if (state) {
509 SOSPeerGetOptionalPersistedCFIndex(state, kSOSPeerVersionKey, &p->version);
510
511 p->sequenceNumber = SOSPeerGetPersistedInt64(state, kSOSPeerSequenceNumberKey);
512 p->mustSendMessage = SOSPeerGetPersistedBoolean(state, kSOSPeerMustSendMessageKey);
513 p->sendObjects = SOSPeerGetPersistedBoolean(state, kSOSPeerSendObjectsKey);
514 CFRetainAssign(p->views, SOSPeerGetPersistedViewNameSet(p, state, kSOSPeerViewsKey));
515 SOSPeerSetKeyBag(p, SOSPeerGetPersistedData(state, kSOSPeerKeyBagKey));
5c19dc3a
A
516 CFAssignRetained(p->pendingObjects, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerPendingObjectsKey));
517 CFAssignRetained(p->unwantedManifest, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerUnwantedManifestKey));
518 CFAssignRetained(p->confirmedManifest, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerConfirmedManifestKey));
519 CFAssignRetained(p->proposedManifests, SOSEngineCopyPersistedManifestArray(engine, state, kSOSPeerProposedManifestKey, error));
520 ok &= p->proposedManifests != NULL;
521 CFAssignRetained(p->localManifests, SOSEngineCopyPersistedManifestArray(engine, state, kSOSPeerLocalManifestKey, error));
522 ok &= p->localManifests != NULL;
523 }
524 return ok;
525}
526
527static SOSPeerRef SOSPeerCreate_Internal(SOSEngineRef engine, CFDictionaryRef state, CFStringRef theirPeerID, CFIndex version, CFErrorRef *error) {
528 SOSPeerRef p = CFTypeAllocate(SOSPeer, struct __OpaqueSOSPeer, kCFAllocatorDefault);
529 p->peer_id = CFRetainSafe(theirPeerID);
530 p->version = version;
531 CFDictionaryRef empty = NULL;
532 if (!state) {
533 empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
534 state = empty;
535 }
536 if (!SOSPeerSetState(p, engine, state, error)) {
537 CFReleaseNull(p);
538 }
539 CFReleaseNull(empty);
540 return p;
541}
542
5c19dc3a
A
543static void SOSPeerPersistBool(CFMutableDictionaryRef persist, CFStringRef key, bool value) {
544 CFDictionarySetValue(persist, key, value ? kCFBooleanTrue : kCFBooleanFalse);
545}
546
547static void SOSPeerPersistInt64(CFMutableDictionaryRef persist, CFStringRef key, int64_t value) {
548 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value);
549 CFDictionarySetValue(persist, key, number);
550 CFReleaseSafe(number);
551}
552
553static void SOSPeerPersistCFIndex(CFMutableDictionaryRef persist, CFStringRef key, CFIndex value) {
554 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &value);
555 CFDictionarySetValue(persist, key, number);
556 CFReleaseSafe(number);
557}
558
559static bool SOSPeerPersistOptionalManifest(CFMutableDictionaryRef persist, CFStringRef key, SOSManifestRef manifest, CFErrorRef *error) {
560 if (!manifest)
561 return true;
562 CFDataRef digest = SOSManifestGetDigest(manifest, error);
563 bool ok = digest;
564 if (ok)
565 CFDictionarySetValue(persist, key, digest);
566 return ok;
567}
568
569static bool SSOSPeerPersistManifestArray(CFMutableDictionaryRef persist, CFStringRef key, CFArrayRef manifests, CFErrorRef *error) {
570 CFMutableArrayRef digests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
571 SOSManifestRef manifest;
572 if (manifests) CFArrayForEachC(manifests, manifest) {
573 CFDataRef digest = SOSManifestGetDigest(manifest, error);
574 if (!digest)
575 CFReleaseNull(digests);
576 if (digests) {
577 CFArrayAppendValue(digests, digest);
578 }
579 }
580 if (digests) {
581 CFDictionarySetValue(persist, key, digests);
582 CFRelease(digests);
583 }
584 return digests;
585}
586
587static void SOSPeerPersistOptionalValue(CFMutableDictionaryRef persist, CFStringRef key, CFTypeRef value) {
588 if (value)
589 CFDictionarySetValue(persist, key, value);
590}
591
592CFDictionaryRef SOSPeerCopyState(SOSPeerRef peer, CFErrorRef *error) {
593 CFMutableDictionaryRef state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
594 SOSPeerPersistInt64(state, kSOSPeerSequenceNumberKey, peer->sequenceNumber);
595 if (peer->version) {
596 SOSPeerPersistCFIndex(state, kSOSPeerVersionKey, peer->version);
597 }
598
599 SOSPeerPersistBool(state, kSOSPeerMustSendMessageKey, peer->mustSendMessage);
600 SOSPeerPersistBool(state, kSOSPeerSendObjectsKey, peer->sendObjects);
601 SOSPeerPersistOptionalValue(state, kSOSPeerViewsKey, peer->views);
602
603 CFDataRef keybag = SOSPeerGetKeyBag(peer);
604 if (keybag && !CFEqual(peer->peer_id, kSOSViewKeychainV0_tomb))
605 SOSPeerPersistOptionalValue(state, kSOSPeerKeyBagKey, keybag);
606
fa7225c8 607 if (!SOSPeerPersistOptionalManifest(state, kSOSPeerPendingObjectsKey, peer->pendingObjects, error)
5c19dc3a
A
608 || !SOSPeerPersistOptionalManifest(state, kSOSPeerUnwantedManifestKey, peer->unwantedManifest, error)
609 || !SOSPeerPersistOptionalManifest(state, kSOSPeerConfirmedManifestKey, peer->confirmedManifest, error)
610 || !SSOSPeerPersistManifestArray(state, kSOSPeerProposedManifestKey, peer->proposedManifests, error)
611 || !SSOSPeerPersistManifestArray(state, kSOSPeerLocalManifestKey, peer->localManifests, error)) {
612 CFReleaseNull(state);
613 }
614 return state;
615}
616
617SOSPeerRef SOSPeerCreateWithState(SOSEngineRef engine, CFStringRef peer_id, CFDictionaryRef state, CFErrorRef *error) {
618 return SOSPeerCreate_Internal(engine, state, peer_id, 0, error);
619}
620
621static void SOSPeerDestroy(CFTypeRef cf) {
622 SOSPeerRef peer = (SOSPeerRef)cf;
623 CFReleaseNull(peer->peer_id);
624 CFReleaseNull(peer->views);
5c19dc3a
A
625 CFReleaseNull(peer->pendingObjects);
626 CFReleaseNull(peer->unwantedManifest);
627 CFReleaseNull(peer->confirmedManifest);
628 CFReleaseNull(peer->proposedManifests);
629 CFReleaseNull(peer->localManifests);
630}
631
632bool SOSPeerDidConnect(SOSPeerRef peer) {
633 SOSPeerSetMustSendMessage(peer, true);
634 SOSPeerSetProposedManifest(peer, SOSPeerGetConfirmedManifest(peer));
635 // TODO: Return false if nothing changed.
636 return true;
637}
638
639// MARK: accessors
640
641CFIndex SOSPeerGetVersion(SOSPeerRef peer) {
642 return peer->version;
643}
644
645CFStringRef SOSPeerGetID(SOSPeerRef peer) {
646 return peer->peer_id;
647}
648
649CFSetRef SOSPeerGetViewNameSet(SOSPeerRef peer) {
650 return peer->views;
651}
652
653void SOSPeerSetViewNameSet(SOSPeerRef peer, CFSetRef views) {
654 CFRetainAssign(peer->views, views);
655}
656
657CFDataRef SOSPeerGetKeyBag(SOSPeerRef peer) {
658 return peer->_keyBag;
659}
660
661static bool SOSPeerUnlinkBackupJournal(SOSPeerRef peer, CFErrorRef *error) {
662 __block bool ok = true;
663 ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *journalName) {
664 secnotice("backup", "%@ unlinking journal file %s", peer, journalName);
665 ok &= SecCheckErrno(unlink(journalName), error, CFSTR("unlink %s"), journalName);
666 });
667 return ok;
668}
669
670static bool SOSPeerWriteReset(SOSPeerRef peer, CFErrorRef *error) {
671 __block bool ok = true;
672 __block CFErrorRef localError = NULL;
673 ok &= SOSPeerTruncateJournal(peer, &localError, ^(FILE *journalFile) {
674 ok = SOSBackupEventWriteReset(journalFile, peer->_keyBag, &localError);
675 if (ok && !peer->_keyBag)
676 ok = SOSBackupEventWriteCompleteMarker(journalFile, 999, &localError);
677 });
678 if (!ok) {
679 secwarning("%@ failed to write reset to backup journal: %@", peer->peer_id, localError);
680 CFErrorPropagate(localError, error);
681 }
682
683 // Forget we ever wrote anything to the journal.
684 SOSPeerSetConfirmedManifest(peer, NULL);
685 SOSPeerSetProposedManifest(peer, NULL);
686
687 SOSPeerSetMustSendMessage(peer, !ok);
688 return ok;
689}
690
691void SOSPeerKeyBagDidChange(SOSPeerRef peer) {
692 // If !keyBag unlink the file, instead of writing a reset.
693 // CloudServices does not want to hear about empty keybags
694 SOSPeerSetSendObjects(peer, false);
695 if (!peer->_keyBag) {
696 SOSPeerUnlinkBackupJournal(peer, NULL);
697 } else {
698 // Attempt to write a reset (ignoring failures since it will
699 // be pended stickily if it fails).
700 SOSPeerWriteReset(peer, NULL);
701 SOSCCSyncWithAllPeers();
702 }
703}
704
705void SOSPeerSetKeyBag(SOSPeerRef peer, CFDataRef keyBag) {
706 if (CFEqualSafe(keyBag, peer->_keyBag)) return;
707 bool hadKeybag = peer->_keyBag;
708 if (!keyBag) {
709 secwarning("%@ keybag for backup unset", SOSPeerGetID(peer));
710 } else {
711 secnotice("backup", "%@ backup bag: %@", SOSPeerGetID(peer), keyBag);
712 }
713 CFRetainAssign(peer->_keyBag, keyBag);
714 // Don't call SOSPeerKeybagDidChange for the inital edge from NULL -> having a keybag.
715 if (hadKeybag) {
716 SOSPeerKeyBagDidChange(peer);
717 }
718}
719
720bool SOSPeerWritePendingReset(SOSPeerRef peer, CFErrorRef *error) {
721 return !SOSPeerMustSendMessage(peer) || SOSPeerWriteReset(peer, error);
722}
723
724uint64_t SOSPeerNextSequenceNumber(SOSPeerRef peer) {
725 return ++peer->sequenceNumber;
726}
727
728uint64_t SOSPeerGetMessageVersion(SOSPeerRef peer) {
729 return SOSPeerGetVersion(peer);
730}
731
732bool SOSPeerMustSendMessage(SOSPeerRef peer) {
733 return peer->mustSendMessage;
734}
735
736void SOSPeerSetMustSendMessage(SOSPeerRef peer, bool sendMessage) {
737 peer->mustSendMessage = sendMessage;
738}
739
740bool SOSPeerSendObjects(SOSPeerRef peer) {
741 return peer->sendObjects;
742}
743
744void SOSPeerSetSendObjects(SOSPeerRef peer, bool sendObjects) {
745 peer->sendObjects = sendObjects;
746}
747
748// MARK: Manifests
749
750SOSManifestRef SOSPeerGetProposedManifest(SOSPeerRef peer) {
751 if (peer->proposedManifests && CFArrayGetCount(peer->proposedManifests))
752 return (SOSManifestRef)CFArrayGetValueAtIndex(peer->proposedManifests, 0);
753 return NULL;
754}
755
756SOSManifestRef SOSPeerGetConfirmedManifest(SOSPeerRef peer) {
757 return peer->confirmedManifest;
758}
759
760void SOSPeerSetConfirmedManifest(SOSPeerRef peer, SOSManifestRef confirmed) {
761 CFRetainAssign(peer->confirmedManifest, confirmed);
762
763 // TODO: Clear only expired pending and local manifests from the array - this clears them all
764 // To do so we'd have to track the messageIds we sent to our peer and when we proposed a particular manifest.
765 // Then we simply remove the entries from messages older than the one we are confirming now
766 //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerProposedManifestKey));
767 //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerLocalManifestKey));
768}
769
770void SOSPeerAddProposedManifest(SOSPeerRef peer, SOSManifestRef proposed) {
771 SOSManifestArrayAppendManifest(&peer->proposedManifests, proposed);
772}
773
774void SOSPeerSetProposedManifest(SOSPeerRef peer, SOSManifestRef proposed) {
775 SOSManifestArraySetManifest(&peer->proposedManifests, proposed);
776}
777
778void SOSPeerAddLocalManifest(SOSPeerRef peer, SOSManifestRef local) {
779 SOSManifestArrayAppendManifest(&peer->localManifests, local);
780}
781
782SOSManifestRef SOSPeerGetPendingObjects(SOSPeerRef peer) {
783 return peer->pendingObjects;
784}
785
786void SOSPeerSetPendingObjects(SOSPeerRef peer, SOSManifestRef pendingObjects) {
787 CFRetainAssign(peer->pendingObjects, pendingObjects);
788}
789
790SOSManifestRef SOSPeerGetUnwantedManifest(SOSPeerRef peer) {
791 return peer->unwantedManifest;
792}
793
794void SOSPeerSetUnwantedManifest(SOSPeerRef peer, SOSManifestRef unwantedManifest) {
795 CFRetainAssign(peer->unwantedManifest, unwantedManifest);
796}
797
798SOSManifestRef SOSPeerCopyManifestForDigest(SOSPeerRef peer, CFDataRef digest) {
799 if (!digest) return NULL;
800 SOSManifestRef manifest;
801 if (peer->proposedManifests) CFArrayForEachC(peer->proposedManifests, manifest) {
802 if (CFEqual(digest, SOSManifestGetDigest(manifest, NULL)))
803 return CFRetainSafe(manifest);
804 }
805 if (peer->localManifests) CFArrayForEachC(peer->localManifests, manifest) {
806 if (CFEqual(digest, SOSManifestGetDigest(manifest, NULL)))
807 return CFRetainSafe(manifest);
808 }
809 if (peer->confirmedManifest && CFEqual(digest, SOSManifestGetDigest(peer->confirmedManifest, NULL)))
810 return CFRetainSafe(peer->confirmedManifest);
811
812 return NULL;
813}
814
815static void SOSMarkManifestInUse(struct SOSDigestVector *mdInUse, SOSManifestRef manifest) {
816 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
817 if (digest)
818 SOSDigestVectorAppend(mdInUse, CFDataGetBytePtr(digest));
819}
820
821static void SOSMarkManifestsInUse(struct SOSDigestVector *mdInUse, CFArrayRef manifests) {
822 if (!isArray(manifests)) return;
823 SOSManifestRef manifest = NULL;
824 CFArrayForEachC(manifests, manifest) {
825 SOSMarkManifestInUse(mdInUse, manifest);
826 }
827}
828
829// Add all digests we are using to mdInUse
830void SOSPeerMarkDigestsInUse(SOSPeerRef peer, struct SOSDigestVector *mdInUse) {
831 SOSMarkManifestInUse(mdInUse, peer->pendingObjects);
832 SOSMarkManifestInUse(mdInUse, peer->unwantedManifest);
833 SOSMarkManifestInUse(mdInUse, peer->confirmedManifest);
834 SOSMarkManifestsInUse(mdInUse, peer->localManifests);
835 SOSMarkManifestsInUse(mdInUse, peer->proposedManifests);
836}
837
838static void SOSAddManifestInUse(CFMutableDictionaryRef mfc, SOSManifestRef manifest) {
839 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
840 CFDataRef data = SOSManifestGetData(manifest);
841 if (digest && data)
842 CFDictionarySetValue(mfc, digest, data);
843}
844
845static void SOSAddManifestsInUse(CFMutableDictionaryRef mfc, CFArrayRef manifests) {
846 if (!isArray(manifests)) return;
847 SOSManifestRef manifest = NULL;
848 CFArrayForEachC(manifests, manifest) {
849 SOSAddManifestInUse(mfc, manifest);
850 }
851}
852
853void SOSPeerAddManifestsInUse(SOSPeerRef peer, CFMutableDictionaryRef mfc) {
854 SOSAddManifestInUse(mfc, peer->pendingObjects);
855 SOSAddManifestInUse(mfc, peer->unwantedManifest);
856 SOSAddManifestInUse(mfc, peer->confirmedManifest);
857 SOSAddManifestsInUse(mfc, peer->localManifests);
858 SOSAddManifestsInUse(mfc, peer->proposedManifests);
859
860}
861
862// absentFromRemote
863// AbsentLocally
864// additionsFromRemote
865// original intent was that digests only got added to pendingObjects. We only know for sure if it is something added locally via api call
866
867
868bool SOSPeerDidReceiveRemovalsAndAdditions(SOSPeerRef peer, SOSManifestRef absentFromRemote, SOSManifestRef additionsFromRemote, SOSManifestRef unwantedFromRemote,
869 SOSManifestRef local, CFErrorRef *error) {
870 // We assume that incoming manifests are all sorted, and absentFromRemote is disjoint from additionsFromRemote
871 bool ok = true;
872 SOSManifestRef remoteMissing = NULL, sharedRemovals = NULL, sharedAdditions = NULL;
873
874 // TODO: Simplify -- a lot.
875 ok = ok && (remoteMissing = SOSManifestCreateIntersection(absentFromRemote, local, error)); // remoteMissing = absentFromRemote <Intersected> local
876 ok = ok && (sharedRemovals = SOSManifestCreateComplement(remoteMissing, absentFromRemote, error)); // sharedRemovals = absentFromRemote - remoteMissing
877 ok = ok && (sharedAdditions = SOSManifestCreateIntersection(additionsFromRemote, local, error)); // sharedAdditions = additionsFromRemote <Intersected> local
878 //ok = ok && (remoteAdditions = SOSManifestCreateComplement(sharedAdditions, additionsFromRemote, error)); // remoteAdditions = additionsFromRemote - sharedAdditions
879
880 // remoteMissing are things we have that remote has asked for => add to pendingObjects
881 // sharedRemovals are things we don't have that remote has asked for => remove from pendingDeletes
882 // sharedAdditions are things we have that remote has too => remove from pendingObjects
883 // remoteAdditions are things that remote said they have that we don't and we should probably ask for => add to pendingDeletes?
884 // unwantedFromRemote are things we received from remote for which we already have a newer object according to the conflict resolver.
885 secnotice("peer", "%@ RM:%@ SR:%@ SA:%@ UR:%@", peer, remoteMissing, sharedRemovals, sharedAdditions, unwantedFromRemote);
886
887 SOSManifestRef pendingObjectsManifest = SOSManifestCreateWithPatch(peer->pendingObjects, sharedAdditions, remoteMissing, error);
888 SOSManifestRef unwantedManifest = SOSManifestCreateWithPatch(peer->unwantedManifest, sharedRemovals, unwantedFromRemote, error);
889 CFAssignRetained(peer->pendingObjects, pendingObjectsManifest); // PO = PO - sharedAdditions + remoteMissing
890 CFAssignRetained(peer->unwantedManifest, unwantedManifest); // U = U - sharedRemovals + unwantedFromRemote
891
892 CFReleaseSafe(remoteMissing);
893 CFReleaseSafe(sharedRemovals);
894 CFReleaseSafe(sharedAdditions);
895
896 secnotice("peer", "%@ C:%@ U:%@ O:%@", peer, SOSPeerGetConfirmedManifest(peer), SOSPeerGetUnwantedManifest(peer), SOSPeerGetPendingObjects(peer));
897
898 return ok;
899}
900
901// Called for a normal syncing peer. Only updates pendingObjects currently.
902bool SOSPeerDataSourceWillCommit(SOSPeerRef peer, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
903 bool isAPITransaction = source == kSOSDataSourceAPITransaction;
904 SOSManifestRef unconfirmedAdditions = NULL;
905 if (isAPITransaction && SOSManifestGetCount(additions)) {
906 // Remove confirmed from additions, leaving us with additions to the local db that the remote peer doesn't have yet
907 unconfirmedAdditions = SOSManifestCreateComplement(SOSPeerGetConfirmedManifest(peer), additions, error);
908 }
909
910 if (SOSManifestGetCount(removals) || SOSManifestGetCount(unconfirmedAdditions)) {
911 SOSManifestRef pendingObjectsManifest = SOSManifestCreateWithPatch(peer->pendingObjects, removals, unconfirmedAdditions, error);
912
913//#if DEBUG
914 // TODO: Only do this if debugScope "peer", notice is enabled.
915 // if (!SecIsScopeActive(kSecLevelNotice, "peer"))
916 {
917 // pended == UA unless the db is renotifying of an addition for something we already have
918 SOSManifestRef unpended = NULL, pended = NULL;
919 SOSManifestDiff(peer->pendingObjects, pendingObjectsManifest, &unpended, &pended, error);
920 secinfo("peer", "%@: willCommit R:%@ A:%@ UA:%@ %s O%s%@%s%@",
921 SOSPeerGetID(peer), removals, additions, unconfirmedAdditions,
922 (isAPITransaction ? "api": "sos"),
923 (SOSManifestGetCount(unpended) ? "-" : ""),
924 (SOSManifestGetCount(unpended) ? (CFStringRef)unpended : CFSTR("")),
925 (SOSManifestGetCount(pended) ? "+" : SOSManifestGetCount(unpended) ? "" : "="),
926 (SOSManifestGetCount(pended) ? (CFStringRef)pended : CFSTR("")));
927 CFReleaseSafe(unpended);
928 CFReleaseSafe(pended);
929 }
930//#endif /* DEBUG */
931 CFAssignRetained(peer->pendingObjects, pendingObjectsManifest);
932 }
933 CFReleaseSafe(unconfirmedAdditions);
934
935 return true;
936}
937
938bool SOSPeerWriteAddEvent(FILE *journalFile, keybag_handle_t kbhandle, SOSDataSourceRef dataSource, SOSObjectRef object, CFErrorRef *error) {
939 CFDictionaryRef backup_item = NULL;
940 bool ok = ((backup_item = SOSObjectCopyBackup(dataSource, object, kbhandle, error))
941 && SOSBackupEventWriteAdd(journalFile, backup_item, error));
942 CFReleaseSafe(backup_item);
943 return ok;
944}
945
946// Called for a backup peer, should stream objects in changes right away
947bool SOSPeerDataSourceWillChange(SOSPeerRef peer, SOSDataSourceRef dataSource, SOSDataSourceTransactionSource source, CFArrayRef changes, CFErrorRef *error) {
948 __block bool ok = true;
949 ok &= SOSPeerWritePendingReset(peer, error) && SOSPeerAppendToJournal(peer, error, ^(FILE *journalFile, keybag_handle_t kbhandle) {
950 struct SOSDigestVector dvdel = SOSDigestVectorInit;
951 struct SOSDigestVector dvadd = SOSDigestVectorInit;
952 SOSChangeRef change;
953 CFArrayForEachC(changes, change) {
954 bool isDelete = false;
955 CFErrorRef localError = NULL;
956 CFDataRef digest = NULL;
957 SOSObjectRef object = NULL;
958 bool ok = digest = SOSChangeCopyDigest(dataSource, change, &isDelete, &object, &localError);
959 if (isDelete) {
960 ok &= SOSBackupEventWriteDelete(journalFile, digest, &localError);
961 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(digest));
962 } else {
963 ok &= SOSPeerWriteAddEvent(journalFile, kbhandle, dataSource, object, &localError);
964 SOSDigestVectorAppend(&dvadd, CFDataGetBytePtr(digest));
965 }
966 if (!ok) {
967 secerror("bad change %@: %@", change, localError);
968 }
969 CFReleaseSafe(digest);
970 CFReleaseSafe(localError);
971 };
972
973 if (ok) {
974 // Update our proposed manifest since we just wrote stuff
975 struct SOSDigestVector dvresult = SOSDigestVectorInit;
976 SOSDigestVectorSort(&dvdel);
977 SOSDigestVectorSort(&dvadd);
978 if ((ok = SOSDigestVectorPatchSorted(SOSManifestGetDigestVector(SOSPeerGetProposedManifest(peer)), &dvdel,
979 &dvadd, &dvresult, error))) {
980 SOSManifestRef proposed;
981 ok = proposed = SOSManifestCreateWithDigestVector(&dvresult, error);
982 SOSPeerSetProposedManifest(peer, proposed);
983 CFReleaseSafe(proposed);
984 }
985 SOSDigestVectorFree(&dvresult);
986 }
987 SOSDigestVectorFree(&dvdel);
988 SOSDigestVectorFree(&dvadd);
989
990 // Only Write marker if we are actually in sync now (local == propopsed).
991 if (SOSPeerSendObjects(peer))
992 SOSBackupEventWriteCompleteMarker(journalFile, 799, error);
993 });
994
995 if (!ok) {
996 // We were unable to stream everything out neatly
997 SOSCCSyncWithAllPeers();
998 }
999 return ok;
1000}