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