+++ /dev/null
-/*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-/*
- * SOSPeer.c - Implementation of a secure object syncing peer
- */
-#include <Security/SecureObjectSync/SOSPeer.h>
-#include <Security/SecureObjectSync/SOSPeerRateLimiter.h>
-#include <Security/SecureObjectSync/SOSDigestVector.h>
-#include <Security/SecureObjectSync/SOSInternal.h>
-#include <Security/SecureObjectSync/SOSTransport.h>
-#include <Security/SecureObjectSync/SOSEngine.h>
-#include <Security/SecureObjectSync/SOSViews.h>
-#include <Security/SecureObjectSync/SOSChangeTracker.h>
-#include <utilities/SecCFError.h>
-#include <utilities/SecCFRelease.h>
-#include <utilities/SecCFWrappers.h>
-#include <utilities/SecDb.h>
-#include <utilities/SecFileLocations.h>
-#include <utilities/SecIOFormat.h>
-#include <utilities/array_size.h>
-#include <utilities/debugging.h>
-#include <Security/SecureObjectSync/SOSBackupEvent.h>
-#include <Security/SecItemBackup.h>
-
-#include <securityd/SOSCloudCircleServer.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-
-#include <stdlib.h>
-
-#include <AssertMacros.h>
-
-// Backup Peer Support
-#include <securityd/SecKeybagSupport.h>
-#include <notify.h>
-
-
-//
-// MARK: - SOSPeerPersistence code
-//
-static CFStringRef kSOSPeerSequenceNumberKey = CFSTR("sequence-number");
-
-CFStringRef kSOSPeerDataLabel = CFSTR("iCloud Peer Data Meta-data");
-
-//
-// MARK: SOSPeerState (dictionary keys)
-//
-
-// PeerState dictionary keys
-static CFStringRef kSOSPeerSendObjectsKey = CFSTR("send-objects"); // bool
-static CFStringRef kSOSPeerMustSendMessageKey = CFSTR("must-send"); // bool
-static CFStringRef kSOSPeerHasBeenInSyncKey = CFSTR("has-been-in-sync"); // bool
-static CFStringRef kSOSPeerPendingObjectsKey = CFSTR("pending-objects"); // digest
-static CFStringRef kSOSPeerUnwantedManifestKey = CFSTR("unwanted-manifest"); // digest
-static CFStringRef kSOSPeerConfirmedManifestKey = CFSTR("confirmed-manifest"); //digest
-static CFStringRef kSOSPeerProposedManifestKey = CFSTR("pending-manifest"); // array of digests
-static CFStringRef kSOSPeerLocalManifestKey = CFSTR("local-manifest"); // array of digests
-static CFStringRef kSOSPeerVersionKey = CFSTR("vers"); // int
-
-//
-// SOSPeerMeta keys that can also be used in peerstate...
-//
-static CFStringRef kSOSPeerPeerIDKey = CFSTR("peer-id"); // string
-static CFStringRef kSOSPeerViewsKey = CFSTR("views"); // set (or array) of string
-static CFStringRef kSOSPeerKeyBagKey = CFSTR("keybag"); // data
-
-/*
- Theory of syncing for both incoming and outgoing messages
-
- A peerstate consists of:
- (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)})
- T: to be sent (pendingObjects) manifest
- U: unwanted objects manifest
- C: confirmed manifest
- P: proposed manifest
- D->M? digest or manifest to optional manifest function
- M->D manifest to digest of manifest function
- H->O? hash (manifest entry) to optional object function (the datasource)
- O->{ Mapping from incoming O objects to one of:
- (Op,Or): Op = Peers object, Or is replaced local object.
- Oa,(Op,Or): Oa = appeared local object, Or = Oa, see above for (Op, Or)
- (Om,Oa),(Op,Or): Om missing local object, was apparently Oa instead see above for (Oa, Op, Or)
- Om,Oi: Om missing local object, inserted Oi (nothing replaced), but Om still disapeared from manifest
- Oi Oi inserted object from peer (nothing replaced)
- Ob: Ob both remote and local object are identical, nothing changed
- Oa,Ob: Oa = appeared local object equal to Oa = Ob. Equivalent to single Oi
- (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
- (Ol,Ou): Ol local object wins from peers Ou older object => append Ou to U
- Oa,(Ol,Ou): Oa appeared as a local object and Oa = Ol above
- (Om,Oa),(Ol,Ou): Om removed and Oa replaced Om and Oa = Ol as above
- }
- A message consists of
- (B,M,E,O)
- B: base digest
- M: missing manifest
- E: extra manifest
- O: objects
-
- To send a message we simply compute:
- B: D->M->C
- M: C \ L
- E: L \ C # subsetted if not all of O is sent
- O: H->O?->O # as size permits
- and change P to (C \ M:) union E: union O->H->O:
-
- To receive a message we compute
- Op,Oi,Ob,Oo and Om,Oa,Ol
- C: (D->M->B \ M) union E union O->H->O
- A = E \ (O->H->O)
- T: T \ (Om union Or union A union Oa,Ob.last union (Om,Oa),Ob.last ) union (M intersect L)
- */
-
-
-//
-// MARK: SOSPeerMeta
-//
-
-static SOSPeerMetaRef SOSPeerMetaCreate(CFStringRef peerID) {
- return CFRetain(peerID);
-}
-
-static SOSPeerMetaRef SOSPeerMetaCreateWithViews(CFStringRef peerID, CFSetRef views) {
- const void *keys[] = { kSOSPeerPeerIDKey, kSOSPeerViewsKey };
- const void *values[] = { peerID, views };
- return CFDictionaryCreate(kCFAllocatorDefault, keys, values, array_size(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-}
-
-static SOSPeerMetaRef SOSPeerMetaCreateWithViewsAndKeyBag(CFStringRef peerID, CFSetRef views, CFDataRef keybag) {
- const void *keys[] = { kSOSPeerPeerIDKey, kSOSPeerViewsKey, kSOSPeerKeyBagKey };
- const void *values[] = { peerID, views, keybag };
- return CFDictionaryCreate(kCFAllocatorDefault, keys, values, array_size(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-}
-
-SOSPeerMetaRef SOSPeerMetaCreateWithComponents(CFStringRef peerID, CFSetRef views, CFDataRef keybag) {
- if (!isString(peerID))
- return NULL;
- if (views) {
- if (keybag)
- return SOSPeerMetaCreateWithViewsAndKeyBag(peerID, views, keybag);
- else
- return SOSPeerMetaCreateWithViews(peerID, views);
- } else
- return SOSPeerMetaCreate(peerID);
-}
-
-SOSPeerMetaRef SOSPeerMetaCreateWithState(CFStringRef peerID, CFDictionaryRef state) {
- return SOSPeerMetaCreateWithComponents(peerID, CFDictionaryGetValue(state, kSOSPeerViewsKey), CFDictionaryGetValue(state, kSOSPeerKeyBagKey));
-}
-
-CFStringRef SOSPeerMetaGetComponents(SOSPeerMetaRef peerMeta, CFSetRef *views, CFDataRef *keybag, CFErrorRef *error) {
- if (isDictionary(peerMeta)) {
- CFDictionaryRef meta = (CFDictionaryRef)peerMeta;
- CFStringRef peerID = asString(CFDictionaryGetValue(meta, kSOSPeerPeerIDKey), error);
- CFSetRef vns = asSet(CFDictionaryGetValue(meta, kSOSPeerViewsKey), error);
- if (vns && asDataOptional(CFDictionaryGetValue(meta, kSOSPeerKeyBagKey), keybag, error)) {
- if (views)
- *views = vns;
- return peerID;
- }
- return NULL;
- } else {
- if (views) {
- // Hack so tests can pass simple peerIDs
- *views = SOSViewsGetV0ViewSet();
- }
- return asString(peerMeta, error);
- }
-}
-
-CFTypeRef SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFTypeRef peerOrState, CFSetRef views, CFDataRef keyBag) {
- assert(views);
- if (peerOrState && CFGetTypeID(peerOrState) == SOSPeerGetTypeID()) {
- // Inflated peer, update its views and move on
- SOSPeerRef peer = (SOSPeerRef)peerOrState;
- SOSPeerSetViewNameSet(peer, views);
- SOSPeerSetKeyBag(peer, keyBag);
- return CFRetainSafe(peer);
- } else if (peerOrState && CFGetTypeID(peerOrState) == CFDictionaryGetTypeID()) {
- CFMutableDictionaryRef state = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, peerOrState);
- // Deserialized peer, just updated the serialized state with the new views
- CFDictionarySetValue(state, kSOSPeerViewsKey, views);
- if (keyBag)
- CFDictionarySetValue(state, kSOSPeerKeyBagKey, keyBag);
- else
- CFDictionaryRemoveValue(state, kSOSPeerKeyBagKey);
- return state;
- } else {
- // New peer, just create a state object.
- if (keyBag)
- return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSOSPeerViewsKey, views, kSOSPeerKeyBagKey, keyBag, NULL);
- else
- return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSOSPeerViewsKey, views, NULL);
- }
-}
-
-CFTypeRef SOSPeerOrStateSetViewsAndCopyState(CFTypeRef peerOrState, CFSetRef views) {
- assert(views);
-
- if (peerOrState && CFGetTypeID(peerOrState) == SOSPeerGetTypeID()) {
- // Inflated peer, update its views and deflate it
- SOSPeerRef peer = (SOSPeerRef)peerOrState;
- SOSPeerSetViewNameSet(peer, views);
- return SOSPeerCopyState(peer, NULL);
- } else if (peerOrState && CFGetTypeID(peerOrState) == CFDictionaryGetTypeID()) {
- // We have a deflated peer. Update its views and keep it deflated
- CFSetRef oldViews = (CFSetRef) CFDictionaryGetValue(peerOrState, kSOSPeerViewsKey);
- CFMutableDictionaryRef state = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, peerOrState);
- CFDictionarySetValue(state, kSOSPeerViewsKey, views);
- if (oldViews && !CFSetIsSubset(views, oldViews)) {
- CFDictionarySetValue(state, kSOSPeerHasBeenInSyncKey, kCFBooleanFalse);
- }
- return state;
- } else {
- return NULL;
- }
-}
-
-bool SOSPeerMapEntryIsBackup(const void *mapEntry) {
- if (!mapEntry) return false;
- if (CFGetTypeID(mapEntry) == SOSPeerGetTypeID()) {
- return SOSPeerGetKeyBag((SOSPeerRef)mapEntry);
- } else {
- return CFDictionaryContainsKey(mapEntry, kSOSPeerKeyBagKey);
- }
-}
-
-//
-// MARK: - SOSManifest
-//
-
-enum {
- kSOSPeerMaxManifestWindowDepth = 4
-};
-
-static CFStringRef SOSManifestCreateOptionalDescriptionWithLabel(SOSManifestRef manifest, CFStringRef label) {
- if (!manifest) return CFSTR(" - ");
- return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(" %@%@"), label, manifest);
-}
-
-static CFStringRef SOSManifestArrayCreateOptionalDescriptionWithLabel(CFArrayRef manifests, CFStringRef label) {
- CFIndex count = manifests ? CFArrayGetCount(manifests) : 0;
- if (count == 0) return CFSTR(" - ");
- SOSManifestRef manifest = (SOSManifestRef)CFArrayGetValueAtIndex(manifests, 0);
- return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(" %@[%" PRIdCFIndex "]%@"), label, count, manifest);
-}
-
-static void SOSManifestArraySetManifest(CFMutableArrayRef *manifests, SOSManifestRef manifest) {
- if (manifest) {
- if (*manifests)
- CFArrayRemoveAllValues(*manifests);
- else
- *manifests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
- CFArrayAppendValue(*manifests, manifest);
- } else {
- CFReleaseNull(*manifests);
- }
-}
-
-static void SOSManifestMutableArrayAppendManifest(CFMutableArrayRef manifests, SOSManifestRef manifest) {
- if (manifest) {
- CFIndex count = CFArrayGetCount(manifests);
- CFIndex ixOfManifest = CFArrayGetFirstIndexOfValue(manifests, CFRangeMake(0, count), manifest);
- if (ixOfManifest != 0) {
- // If the manifest isn't at the front of the array move it there.
- // If it's not in the array, remove enough entires from the end to
- // make room to put it in the front.
- if (ixOfManifest != kCFNotFound) {
- CFArrayRemoveValueAtIndex(manifests, ixOfManifest);
- } else {
- while (count >= kSOSPeerMaxManifestWindowDepth)
- CFArrayRemoveValueAtIndex(manifests, --count);
- }
-
- CFArrayInsertValueAtIndex(manifests, 0, manifest);
- }
- } else {
- // pending == NULL => nothing clear history
- CFArrayRemoveAllValues(manifests);
- }
-}
-
-static void SOSManifestArrayAppendManifest(CFMutableArrayRef *manifests, SOSManifestRef manifest) {
- if (*manifests)
- SOSManifestMutableArrayAppendManifest(*manifests, manifest);
- else
- SOSManifestArraySetManifest(manifests, manifest);
-}
-
-//
-// MARK: - SOSPeer
-//
-
-struct __OpaqueSOSPeer {
- CFRuntimeBase _base;
-
- CFStringRef peer_id;
- CFSetRef views;
- CFIndex version;
- uint64_t sequenceNumber;
- bool mustSendMessage;
- bool sendObjects;
-
- bool hasBeenInSync;
-
- SOSManifestRef pendingObjects;
- SOSManifestRef unwantedManifest;
- SOSManifestRef confirmedManifest;
- CFMutableArrayRef proposedManifests;
- CFMutableArrayRef localManifests;
-
- CFTypeRef limiter;
-
- CFMutableDictionaryRef otrTimers;
-
- // Only backup peers have these:
- CFDataRef _keyBag;
- FILE *journalFile;
-};
-
-CFGiblisWithCompareFor(SOSPeer)
-
-static CFStringRef SOSPeerCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
- SOSPeerRef peer = (SOSPeerRef)cf;
- if(peer){
- CFStringRef po = SOSManifestCreateOptionalDescriptionWithLabel(peer->pendingObjects, CFSTR("O"));
- CFStringRef uo = SOSManifestCreateOptionalDescriptionWithLabel(peer->unwantedManifest, CFSTR("U"));
- CFStringRef co = SOSManifestCreateOptionalDescriptionWithLabel(peer->confirmedManifest, CFSTR("C"));
- CFStringRef pe = SOSManifestArrayCreateOptionalDescriptionWithLabel(peer->proposedManifests, CFSTR("P"));
- CFStringRef lo = SOSManifestArrayCreateOptionalDescriptionWithLabel(peer->localManifests, CFSTR("L"));
- CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%@ %s%s%s%@%@%@%@%@>"),
- SOSPeerGetID(peer),
- SOSPeerMustSendMessage(peer) ? "F" : "f",
- SOSPeerSendObjects(peer) ? "S" : "s",
- SOSPeerHasBeenInSync(peer) ? "K" : "k",
- po, uo, co, pe, lo);
- CFReleaseSafe(lo);
- CFReleaseSafe(pe);
- CFReleaseSafe(co);
- CFReleaseSafe(uo);
- CFReleaseSafe(po);
-
- return desc;
- }
- else
- return CFSTR("NULL");
-}
-
-static Boolean SOSPeerCompare(CFTypeRef cfA, CFTypeRef cfB)
-{
- SOSPeerRef peerA = (SOSPeerRef)cfA, peerB = (SOSPeerRef)cfB;
- // Use mainly to see if peerB is actually this device (peerA)
- return CFStringCompare(SOSPeerGetID(peerA), SOSPeerGetID(peerB), 0) == kCFCompareEqualTo;
-}
-
-
-static bool SOSPeerGetPersistedBoolean(CFDictionaryRef persisted, CFStringRef key) {
- CFBooleanRef boolean = CFDictionaryGetValue(persisted, key);
- return boolean && CFBooleanGetValue(boolean);
-}
-
-static CFDataRef SOSPeerGetPersistedData(CFDictionaryRef persisted, CFStringRef key) {
- return asData(CFDictionaryGetValue(persisted, key), NULL);
-}
-
-static int64_t SOSPeerGetPersistedInt64(CFDictionaryRef persisted, CFStringRef key) {
- int64_t integer = 0;
- CFNumberRef number = CFDictionaryGetValue(persisted, key);
- if (number) {
- CFNumberGetValue(number, kCFNumberSInt64Type, &integer);
- }
- return integer;
-}
-
-static void SOSPeerGetOptionalPersistedCFIndex(CFDictionaryRef persisted, CFStringRef key, CFIndex *value) {
- CFNumberRef number = CFDictionaryGetValue(persisted, key);
- if (number) {
- CFNumberGetValue(number, kCFNumberCFIndexType, value);
- }
-}
-
-static CFSetRef SOSPeerGetPersistedViewNameSet(SOSPeerRef peer, CFDictionaryRef persisted, CFStringRef key) {
- CFSetRef vns = CFDictionaryGetValue(persisted, key);
- if (!vns) {
- // Engine state in db contained a v0 peer, thus it must be in the V0ViewSet.
- vns = SOSViewsGetV0ViewSet();
- secnotice("peer", "%@ had no views, inferring: %@", peer->peer_id, vns);
- }
- return vns;
-}
-
-//
-// MARK: Backup Peers
-//
-
-void SOSBackupPeerPostNotification(const char *reason) {
- // Let sbd know when a notable event occurs
- // - Disk full
- // - Backup bag change
- secnotice("backup", "posting notification to CloudServices: %s", reason?reason:"");
- notify_post(kSecItemBackupNotification);
-}
-
-static bool SOSPeerDoWithJournalPath(SOSPeerRef peer, CFErrorRef *error, void(^with)(const char *journalPath)) {
- // TODO: Probably switch to using CFURL to construct the path.
- bool ok = true;
- char strBuffer[PATH_MAX + 1];
- size_t userTempLen = confstr(_CS_DARWIN_USER_TEMP_DIR, strBuffer, sizeof(strBuffer));
- if (userTempLen == 0) {
- ok = SecCheckErrno(-1, error, CFSTR("confstr on _CS_DARWIN_USER_TEMP_DIR returned an error."));
- } else {
- CFStringRef journalName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s/SOSBackup-%@"), strBuffer, SOSPeerGetID(peer));
- CFStringPerformWithCString(journalName, with);
- CFReleaseSafe(journalName);
- }
- return ok;
-}
-
-static FILE *fopen_journal(const char *journalPath, const char *mode, CFErrorRef *error) {
- FILE *file = fopen(journalPath, mode);
- SecCheckErrno(!file, error, CFSTR("fopen %s,%s"), journalPath, mode);
- return file;
-}
-
-#include <sys/stat.h>
-
-#if !defined(NDEBUG)
-static off_t getFileSize(int fd) {
- struct stat sb;
- fstat(fd, &sb);
- return sb.st_size;
-}
-#endif
-
-int SOSPeerHandoffFD(SOSPeerRef peer, CFErrorRef *error) {
- __block int fd = -1;
- SOSPeerDoWithJournalPath(peer, error, ^(const char *journalName) {
- fd = open(journalName, O_RDONLY | O_CLOEXEC);
- if (SecCheckErrno(fd < 0, error, CFSTR("open %s"), journalName)) {
- if (!SecCheckErrno(unlink(journalName), error, CFSTR("unlink %s"), journalName)) {
- close(fd);
- fd = -1;
- } else {
- secdebug("backup", "Handing off file %s with fd %d of size %llu", journalName, fd, getFileSize(fd));
- }
- } else {
- secdebug("backup", "Handing off file %s failed, %@", journalName, error?*error:NULL);
- }
- });
- return fd;
-}
-
-static CFDataRef SOSPeerCopyAKSKeyBag(SOSPeerRef peer, CFErrorRef *error) {
- if (CFEqual(peer->peer_id, kSOSViewKeychainV0_tomb)) {
- return CFRetainSafe(peer->_keyBag);
- } else {
- CFDataRef aksKeybag = NULL;
- SOSBackupSliceKeyBagRef backupSliceKeyBag = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, peer->_keyBag, error);
- if (backupSliceKeyBag) {
- aksKeybag = SOSBSKBCopyAKSBag(backupSliceKeyBag, error);
- CFRelease(backupSliceKeyBag);
- }
- return aksKeybag;
- }
-}
-
-bool SOSPeerAppendToJournal(SOSPeerRef peer, CFErrorRef *error, void(^with)(FILE *journalFile, keybag_handle_t kbhandle)) {
- __block bool ok = true;
- // We only need a keybag if we are writing ADDs. Since we don't know at this layer
- // what operations we may be doing, open keybag if we have one, otherwise don't
- ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *fname) {
- FILE *file = fopen_journal(fname, "a", error);
- if (file) {
- keybag_handle_t kbhandle = bad_keybag_handle;
- CFDataRef keybag = SOSPeerCopyAKSKeyBag(peer, error);
- ok = keybag;
- if (ok && (ok = ks_open_keybag(keybag, NULL, &kbhandle, error))) {
- with(file, kbhandle);
- if (kbhandle != bad_keybag_handle)
- ok &= ks_close_keybag(kbhandle, error);
- }
- CFReleaseSafe(keybag);
- fclose(file);
- }
- });
- return ok;
-}
-
-static bool SOSPeerTruncateJournal(SOSPeerRef peer, CFErrorRef *error, void(^with)(FILE *journalFile)) {
- __block bool ok = true;
- ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *fname) {
- FILE *file = fopen_journal(fname, "w", error);
- if (file) {
- with(file);
- fclose(file);
- }
- });
- return ok;
-}
-
-bool SOSPeerSetState(SOSPeerRef p, SOSEngineRef engine, CFDictionaryRef state, CFErrorRef *error) {
- bool ok = true;
- if (state) {
- SOSPeerGetOptionalPersistedCFIndex(state, kSOSPeerVersionKey, &p->version);
-
- p->sequenceNumber = SOSPeerGetPersistedInt64(state, kSOSPeerSequenceNumberKey);
- p->mustSendMessage = SOSPeerGetPersistedBoolean(state, kSOSPeerMustSendMessageKey);
- p->sendObjects = SOSPeerGetPersistedBoolean(state, kSOSPeerSendObjectsKey);
- p->hasBeenInSync = SOSPeerGetPersistedBoolean(state, kSOSPeerHasBeenInSyncKey);
- CFRetainAssign(p->views, SOSPeerGetPersistedViewNameSet(p, state, kSOSPeerViewsKey));
- SOSPeerSetKeyBag(p, SOSPeerGetPersistedData(state, kSOSPeerKeyBagKey));
- CFAssignRetained(p->pendingObjects, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerPendingObjectsKey));
- CFAssignRetained(p->unwantedManifest, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerUnwantedManifestKey));
- CFAssignRetained(p->confirmedManifest, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerConfirmedManifestKey));
- CFAssignRetained(p->proposedManifests, SOSEngineCopyPersistedManifestArray(engine, state, kSOSPeerProposedManifestKey, error));
- ok &= p->proposedManifests != NULL;
- CFAssignRetained(p->localManifests, SOSEngineCopyPersistedManifestArray(engine, state, kSOSPeerLocalManifestKey, error));
- ok &= p->localManifests != NULL;
- }
- return ok;
-}
-
-static SOSPeerRef SOSPeerCreate_Internal(SOSEngineRef engine, CFDictionaryRef state, CFStringRef theirPeerID, CFIndex version, CFErrorRef *error) {
- SOSPeerRef p = CFTypeAllocate(SOSPeer, struct __OpaqueSOSPeer, kCFAllocatorDefault);
- p->peer_id = CFRetainSafe(theirPeerID);
- p->version = version;
- p->otrTimers = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-
- CFDictionaryRef empty = NULL;
- if (!state) {
- empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- state = empty;
- }
- if (!SOSPeerSetState(p, engine, state, error)) {
- CFReleaseNull(p);
- }
- CFReleaseNull(empty);
- return p;
-}
-
-static void SOSPeerPersistBool(CFMutableDictionaryRef persist, CFStringRef key, bool value) {
- CFDictionarySetValue(persist, key, value ? kCFBooleanTrue : kCFBooleanFalse);
-}
-
-static void SOSPeerPersistInt64(CFMutableDictionaryRef persist, CFStringRef key, int64_t value) {
- CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value);
- CFDictionarySetValue(persist, key, number);
- CFReleaseSafe(number);
-}
-
-static void SOSPeerPersistCFIndex(CFMutableDictionaryRef persist, CFStringRef key, CFIndex value) {
- CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &value);
- CFDictionarySetValue(persist, key, number);
- CFReleaseSafe(number);
-}
-
-static bool SOSPeerPersistOptionalManifest(CFMutableDictionaryRef persist, CFStringRef key, SOSManifestRef manifest, CFErrorRef *error) {
- if (!manifest)
- return true;
- CFDataRef digest = SOSManifestGetDigest(manifest, error);
- bool ok = digest;
- if (ok)
- CFDictionarySetValue(persist, key, digest);
- return ok;
-}
-
-static bool SSOSPeerPersistManifestArray(CFMutableDictionaryRef persist, CFStringRef key, CFArrayRef manifests, CFErrorRef *error) {
- CFMutableArrayRef digests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
- SOSManifestRef manifest;
- if (manifests) CFArrayForEachC(manifests, manifest) {
- CFDataRef digest = SOSManifestGetDigest(manifest, error);
- if (!digest)
- CFReleaseNull(digests);
- if (digests) {
- CFArrayAppendValue(digests, digest);
- }
- }
- if (digests) {
- CFDictionarySetValue(persist, key, digests);
- CFRelease(digests);
- }
- return digests;
-}
-
-static void SOSPeerPersistOptionalValue(CFMutableDictionaryRef persist, CFStringRef key, CFTypeRef value) {
- if (value)
- CFDictionarySetValue(persist, key, value);
-}
-
-CFDictionaryRef SOSPeerCopyState(SOSPeerRef peer, CFErrorRef *error) {
- CFMutableDictionaryRef state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- SOSPeerPersistInt64(state, kSOSPeerSequenceNumberKey, peer->sequenceNumber);
- if (peer->version) {
- SOSPeerPersistCFIndex(state, kSOSPeerVersionKey, peer->version);
- }
-
- SOSPeerPersistBool(state, kSOSPeerMustSendMessageKey, peer->mustSendMessage);
- SOSPeerPersistBool(state, kSOSPeerSendObjectsKey, peer->sendObjects);
- SOSPeerPersistBool(state, kSOSPeerHasBeenInSyncKey, peer->hasBeenInSync);
- SOSPeerPersistOptionalValue(state, kSOSPeerViewsKey, peer->views);
-
- CFDataRef keybag = SOSPeerGetKeyBag(peer);
- if (keybag && !CFEqual(peer->peer_id, kSOSViewKeychainV0_tomb))
- SOSPeerPersistOptionalValue(state, kSOSPeerKeyBagKey, keybag);
-
- if (!SOSPeerPersistOptionalManifest(state, kSOSPeerPendingObjectsKey, peer->pendingObjects, error)
- || !SOSPeerPersistOptionalManifest(state, kSOSPeerUnwantedManifestKey, peer->unwantedManifest, error)
- || !SOSPeerPersistOptionalManifest(state, kSOSPeerConfirmedManifestKey, peer->confirmedManifest, error)
- || !SSOSPeerPersistManifestArray(state, kSOSPeerProposedManifestKey, peer->proposedManifests, error)
- || !SSOSPeerPersistManifestArray(state, kSOSPeerLocalManifestKey, peer->localManifests, error)) {
- CFReleaseNull(state);
- }
- return state;
-}
-
-SOSPeerRef SOSPeerCreateWithState(SOSEngineRef engine, CFStringRef peer_id, CFDictionaryRef state, CFErrorRef *error) {
- return SOSPeerCreate_Internal(engine, state, peer_id, 0, error);
-}
-
-static void SOSPeerDestroy(CFTypeRef cf) {
- SOSPeerRef peer = (SOSPeerRef)cf;
- CFReleaseNull(peer->peer_id);
- CFReleaseNull(peer->views);
- CFReleaseNull(peer->pendingObjects);
- CFReleaseNull(peer->unwantedManifest);
- CFReleaseNull(peer->confirmedManifest);
- CFReleaseNull(peer->proposedManifests);
- CFReleaseNull(peer->localManifests);
- CFReleaseNull(peer->otrTimers);
- CFReleaseNull(peer->limiter);
- CFReleaseNull(peer->_keyBag);
-}
-
-bool SOSPeerDidConnect(SOSPeerRef peer) {
- SOSPeerSetMustSendMessage(peer, true);
- SOSPeerSetProposedManifest(peer, SOSPeerGetConfirmedManifest(peer));
- // TODO: Return false if nothing changed.
- return true;
-}
-
-// MARK: accessors
-
-CFIndex SOSPeerGetVersion(SOSPeerRef peer) {
- return peer->version;
-}
-
-void SOSPeerSetRateLimiter(SOSPeerRef peer, CFTypeRef limiter)
-{
- peer->limiter = CFRetainSafe(limiter);
-}
-
-CFTypeRef SOSPeerGetRateLimiter(SOSPeerRef peer)
-{
- return (peer->limiter ? peer->limiter : NULL);
-}
-
-CFStringRef SOSPeerGetID(SOSPeerRef peer) {
- return peer->peer_id;
-}
-
-CFSetRef SOSPeerGetViewNameSet(SOSPeerRef peer) {
- return peer->views;
-}
-
-void SOSPeerSetViewNameSet(SOSPeerRef peer, CFSetRef views) {
- if (peer->views && !CFSetIsSubset(views, peer->views)) {
- SOSPeerSetHasBeenInSync(peer, false);
- }
-
- CFRetainAssign(peer->views, views);
-}
-
-CFDataRef SOSPeerGetKeyBag(SOSPeerRef peer) {
- return peer->_keyBag;
-}
-
-static bool SOSPeerUnlinkBackupJournal(SOSPeerRef peer, CFErrorRef *error) {
- __block bool ok = true;
- ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *journalName) {
- secnotice("backup", "%@ unlinking journal file %s", peer, journalName);
- ok &= SecCheckErrno(unlink(journalName), error, CFSTR("unlink %s"), journalName);
- });
- return ok;
-}
-
-static bool SOSPeerWriteReset(SOSPeerRef peer, CFErrorRef *error) {
- __block bool ok = true;
- __block CFErrorRef localError = NULL;
- ok &= SOSPeerTruncateJournal(peer, &localError, ^(FILE *journalFile) {
- ok = SOSBackupEventWriteReset(journalFile, peer->_keyBag, &localError);
- if (ok && !peer->_keyBag)
- ok = SOSBackupEventWriteCompleteMarker(journalFile, 999, &localError);
- });
-
- if (!ok) {
- secwarning("%@ failed to write reset to backup journal: %@", peer->peer_id, localError);
- CFErrorPropagate(localError, error);
- } else {
- secnotice("backup-peer", "%@ Wrote reset.", peer->peer_id);
- }
-
- // Forget we ever wrote anything to the journal.
- SOSPeerSetConfirmedManifest(peer, NULL);
- SOSPeerSetProposedManifest(peer, NULL);
-
- SOSPeerSetMustSendMessage(peer, !ok);
- return ok;
-}
-
-void SOSPeerKeyBagDidChange(SOSPeerRef peer) {
- // If !keyBag unlink the file, instead of writing a reset.
- // CloudServices does not want to hear about empty keybags
- SOSPeerSetSendObjects(peer, false);
- if (!peer->_keyBag) {
- SOSPeerUnlinkBackupJournal(peer, NULL);
- } else {
- // Attempt to write a reset (ignoring failures since it will
- // be pended stickily if it fails).
- SOSPeerWriteReset(peer, NULL);
- SOSCCRequestSyncWithBackupPeer(SOSPeerGetID(peer));
- }
-}
-
-void SOSPeerSetKeyBag(SOSPeerRef peer, CFDataRef keyBag) {
- if (CFEqualSafe(keyBag, peer->_keyBag)) return;
- bool hadKeybag = peer->_keyBag;
- if (!keyBag) {
- secwarning("%@ keybag for backup unset", SOSPeerGetID(peer));
- } else {
- secnotice("backup", "%@ backup bag: %@", SOSPeerGetID(peer), keyBag);
- }
- CFRetainAssign(peer->_keyBag, keyBag);
- // Don't call SOSPeerKeybagDidChange for the inital edge from NULL -> having a keybag.
- if (hadKeybag) {
- SOSPeerKeyBagDidChange(peer);
- }
-}
-
-bool SOSPeerWritePendingReset(SOSPeerRef peer, CFErrorRef *error) {
- return !SOSPeerMustSendMessage(peer) || SOSPeerWriteReset(peer, error);
-}
-
-uint64_t SOSPeerNextSequenceNumber(SOSPeerRef peer) {
- return ++peer->sequenceNumber;
-}
-
-uint64_t SOSPeerGetMessageVersion(SOSPeerRef peer) {
- return SOSPeerGetVersion(peer);
-}
-
-bool SOSPeerMustSendMessage(SOSPeerRef peer) {
- return peer->mustSendMessage;
-}
-
-void SOSPeerSetMustSendMessage(SOSPeerRef peer, bool sendMessage) {
- peer->mustSendMessage = sendMessage;
-}
-
-bool SOSPeerSendObjects(SOSPeerRef peer) {
- return peer->sendObjects;
-}
-
-void SOSPeerSetSendObjects(SOSPeerRef peer, bool sendObjects) {
- peer->sendObjects = sendObjects;
-}
-
-bool SOSPeerHasBeenInSync(SOSPeerRef peer) {
- return peer->hasBeenInSync;
-}
-
-void SOSPeerSetHasBeenInSync(SOSPeerRef peer, bool hasBeenInSync) {
- peer->hasBeenInSync = hasBeenInSync;
-}
-
-// MARK: Manifests
-
-SOSManifestRef SOSPeerGetProposedManifest(SOSPeerRef peer) {
- if (peer->proposedManifests && CFArrayGetCount(peer->proposedManifests))
- return (SOSManifestRef)CFArrayGetValueAtIndex(peer->proposedManifests, 0);
- return NULL;
-}
-
-SOSManifestRef SOSPeerGetConfirmedManifest(SOSPeerRef peer) {
- return peer->confirmedManifest;
-}
-
-void SOSPeerSetConfirmedManifest(SOSPeerRef peer, SOSManifestRef confirmed) {
- CFRetainAssign(peer->confirmedManifest, confirmed);
-
- // TODO: Clear only expired pending and local manifests from the array - this clears them all
- // To do so we'd have to track the messageIds we sent to our peer and when we proposed a particular manifest.
- // Then we simply remove the entries from messages older than the one we are confirming now
- //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerProposedManifestKey));
- //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerLocalManifestKey));
-}
-
-void SOSPeerAddProposedManifest(SOSPeerRef peer, SOSManifestRef proposed) {
- SOSManifestArrayAppendManifest(&peer->proposedManifests, proposed);
-}
-
-void SOSPeerSetProposedManifest(SOSPeerRef peer, SOSManifestRef proposed) {
- SOSManifestArraySetManifest(&peer->proposedManifests, proposed);
-}
-
-void SOSPeerAddLocalManifest(SOSPeerRef peer, SOSManifestRef local) {
- SOSManifestArrayAppendManifest(&peer->localManifests, local);
-}
-
-SOSManifestRef SOSPeerGetPendingObjects(SOSPeerRef peer) {
- return peer->pendingObjects;
-}
-
-void SOSPeerSetPendingObjects(SOSPeerRef peer, SOSManifestRef pendingObjects) {
- CFRetainAssign(peer->pendingObjects, pendingObjects);
-}
-
-SOSManifestRef SOSPeerGetUnwantedManifest(SOSPeerRef peer) {
- return peer->unwantedManifest;
-}
-
-void SOSPeerSetUnwantedManifest(SOSPeerRef peer, SOSManifestRef unwantedManifest) {
- CFRetainAssign(peer->unwantedManifest, unwantedManifest);
-}
-bool SOSPeerTimerForPeerExist(SOSPeerRef peer){
- dispatch_source_t timer = SOSPeerGetOTRTimer(peer);
- return timer ? true : false;
-}
-void SOSPeerSetOTRTimer(SOSPeerRef peer, dispatch_source_t timer){
- NSMutableDictionary* timers = (NSMutableDictionary*)CFBridgingRelease(peer->otrTimers);
- if(!timers)
- timers = [[NSMutableDictionary alloc]init];
-
- [timers setObject:timer forKey:(__bridge NSString*)SOSPeerGetID(peer)];
- peer->otrTimers = (CFMutableDictionaryRef)CFBridgingRetain(timers);
-}
-
-dispatch_source_t SOSPeerGetOTRTimer(SOSPeerRef peer){
- return (dispatch_source_t)CFDictionaryGetValue(peer->otrTimers, SOSPeerGetID(peer));
-}
-
-void SOSPeerRemoveOTRTimerEntry(SOSPeerRef peer){
- CFDictionaryRemoveValue(peer->otrTimers, SOSPeerGetID(peer));
-}
-
-SOSManifestRef SOSPeerCopyManifestForDigest(SOSPeerRef peer, CFDataRef digest) {
- if (!digest) return NULL;
- SOSManifestRef manifest;
- if (peer->proposedManifests) CFArrayForEachC(peer->proposedManifests, manifest) {
- if (CFEqual(digest, SOSManifestGetDigest(manifest, NULL)))
- return CFRetainSafe(manifest);
- }
- if (peer->localManifests) CFArrayForEachC(peer->localManifests, manifest) {
- if (CFEqual(digest, SOSManifestGetDigest(manifest, NULL)))
- return CFRetainSafe(manifest);
- }
- if (peer->confirmedManifest && CFEqual(digest, SOSManifestGetDigest(peer->confirmedManifest, NULL)))
- return CFRetainSafe(peer->confirmedManifest);
-
- return NULL;
-}
-
-static void SOSMarkManifestInUse(struct SOSDigestVector *mdInUse, SOSManifestRef manifest) {
- CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
- if (digest)
- SOSDigestVectorAppend(mdInUse, CFDataGetBytePtr(digest));
-}
-
-static void SOSMarkManifestsInUse(struct SOSDigestVector *mdInUse, CFArrayRef manifests) {
- if (!isArray(manifests)) return;
- SOSManifestRef manifest = NULL;
- CFArrayForEachC(manifests, manifest) {
- SOSMarkManifestInUse(mdInUse, manifest);
- }
-}
-
-// Add all digests we are using to mdInUse
-void SOSPeerMarkDigestsInUse(SOSPeerRef peer, struct SOSDigestVector *mdInUse) {
- SOSMarkManifestInUse(mdInUse, peer->pendingObjects);
- SOSMarkManifestInUse(mdInUse, peer->unwantedManifest);
- SOSMarkManifestInUse(mdInUse, peer->confirmedManifest);
- SOSMarkManifestsInUse(mdInUse, peer->localManifests);
- SOSMarkManifestsInUse(mdInUse, peer->proposedManifests);
-}
-
-static void SOSAddManifestInUse(CFMutableDictionaryRef mfc, SOSManifestRef manifest) {
- CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
- CFDataRef data = SOSManifestGetData(manifest);
- if (digest && data)
- CFDictionarySetValue(mfc, digest, data);
-}
-
-static void SOSAddManifestsInUse(CFMutableDictionaryRef mfc, CFArrayRef manifests) {
- if (!isArray(manifests)) return;
- SOSManifestRef manifest = NULL;
- CFArrayForEachC(manifests, manifest) {
- SOSAddManifestInUse(mfc, manifest);
- }
-}
-
-void SOSPeerAddManifestsInUse(SOSPeerRef peer, CFMutableDictionaryRef mfc) {
- SOSAddManifestInUse(mfc, peer->pendingObjects);
- SOSAddManifestInUse(mfc, peer->unwantedManifest);
- SOSAddManifestInUse(mfc, peer->confirmedManifest);
- SOSAddManifestsInUse(mfc, peer->localManifests);
- SOSAddManifestsInUse(mfc, peer->proposedManifests);
-
-}
-
-// absentFromRemote
-// AbsentLocally
-// additionsFromRemote
-// original intent was that digests only got added to pendingObjects. We only know for sure if it is something added locally via api call
-
-
-bool SOSPeerDidReceiveRemovalsAndAdditions(SOSPeerRef peer, SOSManifestRef absentFromRemote, SOSManifestRef additionsFromRemote, SOSManifestRef unwantedFromRemote,
- SOSManifestRef local, CFErrorRef *error) {
- // We assume that incoming manifests are all sorted, and absentFromRemote is disjoint from additionsFromRemote
- bool ok = true;
- SOSManifestRef remoteMissing = NULL, sharedRemovals = NULL, sharedAdditions = NULL;
-
- // TODO: Simplify -- a lot.
- ok = ok && (remoteMissing = SOSManifestCreateIntersection(absentFromRemote, local, error)); // remoteMissing = absentFromRemote <Intersected> local
- ok = ok && (sharedRemovals = SOSManifestCreateComplement(remoteMissing, absentFromRemote, error)); // sharedRemovals = absentFromRemote - remoteMissing
- ok = ok && (sharedAdditions = SOSManifestCreateIntersection(additionsFromRemote, local, error)); // sharedAdditions = additionsFromRemote <Intersected> local
- //ok = ok && (remoteAdditions = SOSManifestCreateComplement(sharedAdditions, additionsFromRemote, error)); // remoteAdditions = additionsFromRemote - sharedAdditions
-
- // remoteMissing are things we have that remote has asked for => add to pendingObjects
- // sharedRemovals are things we don't have that remote has asked for => remove from pendingDeletes
- // sharedAdditions are things we have that remote has too => remove from pendingObjects
- // remoteAdditions are things that remote said they have that we don't and we should probably ask for => add to pendingDeletes?
- // unwantedFromRemote are things we received from remote for which we already have a newer object according to the conflict resolver.
- secnotice("peer", "%@ RM:%@ SR:%@ SA:%@ UR:%@", peer, remoteMissing, sharedRemovals, sharedAdditions, unwantedFromRemote);
-
- SOSManifestRef pendingObjectsManifest = SOSManifestCreateWithPatch(peer->pendingObjects, sharedAdditions, remoteMissing, error);
- SOSManifestRef unwantedManifest = SOSManifestCreateWithPatch(peer->unwantedManifest, sharedRemovals, unwantedFromRemote, error);
- CFAssignRetained(peer->pendingObjects, pendingObjectsManifest); // PO = PO - sharedAdditions + remoteMissing
- CFAssignRetained(peer->unwantedManifest, unwantedManifest); // U = U - sharedRemovals + unwantedFromRemote
-
- CFReleaseSafe(remoteMissing);
- CFReleaseSafe(sharedRemovals);
- CFReleaseSafe(sharedAdditions);
-
- secnotice("peer", "%@ C:%@ U:%@ O:%@", peer, SOSPeerGetConfirmedManifest(peer), SOSPeerGetUnwantedManifest(peer), SOSPeerGetPendingObjects(peer));
-
- return ok;
-}
-
-// Called for a normal syncing peer. Only updates pendingObjects currently.
-bool SOSPeerDataSourceWillCommit(SOSPeerRef peer, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
- bool isAPITransaction = source == kSOSDataSourceAPITransaction;
- bool isCKKSTransaction = source == kSOSDataSourceCKKSTransaction;
- SOSManifestRef unconfirmedAdditions = NULL;
- if ((isAPITransaction || isCKKSTransaction) && SOSManifestGetCount(additions)) {
- // Remove confirmed from additions, leaving us with additions to the local db that the remote peer doesn't have yet
- unconfirmedAdditions = SOSManifestCreateComplement(SOSPeerGetConfirmedManifest(peer), additions, error);
- }
-
- if (SOSManifestGetCount(removals) || SOSManifestGetCount(unconfirmedAdditions)) {
- SOSManifestRef pendingObjectsManifest = SOSManifestCreateWithPatch(peer->pendingObjects, removals, unconfirmedAdditions, error);
-
-//#if DEBUG
- // TODO: Only do this if debugScope "peer", notice is enabled.
- // if (!SecIsScopeActive(kSecLevelNotice, "peer"))
- {
- // pended == UA unless the db is renotifying of an addition for something we already have
- SOSManifestRef unpended = NULL, pended = NULL;
- SOSManifestDiff(peer->pendingObjects, pendingObjectsManifest, &unpended, &pended, error);
- secinfo("peer", "%@: willCommit R:%@ A:%@ UA:%@ %s O%s%@%s%@",
- SOSPeerGetID(peer), removals, additions, unconfirmedAdditions,
- (isAPITransaction ? "api": isCKKSTransaction ? "ckks" : "sos"),
- (SOSManifestGetCount(unpended) ? "-" : ""),
- (SOSManifestGetCount(unpended) ? (CFStringRef)unpended : CFSTR("")),
- (SOSManifestGetCount(pended) ? "+" : SOSManifestGetCount(unpended) ? "" : "="),
- (SOSManifestGetCount(pended) ? (CFStringRef)pended : CFSTR("")));
- CFReleaseSafe(unpended);
- CFReleaseSafe(pended);
- }
-//#endif /* DEBUG */
- CFAssignRetained(peer->pendingObjects, pendingObjectsManifest);
- }
- CFReleaseSafe(unconfirmedAdditions);
-
- return true;
-}
-
-bool SOSPeerWriteAddEvent(FILE *journalFile, keybag_handle_t kbhandle, SOSDataSourceRef dataSource, SOSObjectRef object, CFErrorRef *error) {
- CFDictionaryRef backup_item = NULL;
- bool ok = ((backup_item = SOSObjectCopyBackup(dataSource, object, kbhandle, error))
- && SOSBackupEventWriteAdd(journalFile, backup_item, error));
- CFReleaseSafe(backup_item);
- return ok;
-}
-
-// Called for a backup peer, should stream objects in changes right away
-bool SOSPeerDataSourceWillChange(SOSPeerRef peer, SOSDataSourceRef dataSource, SOSDataSourceTransactionSource source, CFArrayRef changes, CFErrorRef *error) {
- __block bool ok = true;
- ok &= SOSPeerWritePendingReset(peer, error) && SOSPeerAppendToJournal(peer, error, ^(FILE *journalFile, keybag_handle_t kbhandle) {
- struct SOSDigestVector dvdel = SOSDigestVectorInit;
- struct SOSDigestVector dvadd = SOSDigestVectorInit;
- SOSChangeRef change;
- CFArrayForEachC(changes, change) {
- bool isDelete = false;
- CFErrorRef localError = NULL;
- CFDataRef digest = NULL;
- SOSObjectRef object = NULL;
- bool ok = digest = SOSChangeCopyDigest(dataSource, change, &isDelete, &object, &localError);
- if (isDelete) {
- ok &= SOSBackupEventWriteDelete(journalFile, digest, &localError);
- SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(digest));
- } else {
- ok &= SOSPeerWriteAddEvent(journalFile, kbhandle, dataSource, object, &localError);
- SOSDigestVectorAppend(&dvadd, CFDataGetBytePtr(digest));
- }
- if (!ok) {
- secerror("bad change %@: %@", change, localError);
- }
- CFReleaseSafe(digest);
- CFReleaseSafe(localError);
- };
-
- if (ok) {
- // Update our proposed manifest since we just wrote stuff
- struct SOSDigestVector dvresult = SOSDigestVectorInit;
- SOSDigestVectorSort(&dvdel);
- SOSDigestVectorSort(&dvadd);
- if ((ok = SOSDigestVectorPatchSorted(SOSManifestGetDigestVector(SOSPeerGetProposedManifest(peer)), &dvdel,
- &dvadd, &dvresult, error))) {
- SOSManifestRef proposed;
- ok = proposed = SOSManifestCreateWithDigestVector(&dvresult, error);
- SOSPeerSetProposedManifest(peer, proposed);
- CFReleaseSafe(proposed);
- }
- SOSDigestVectorFree(&dvresult);
- }
- SOSDigestVectorFree(&dvdel);
- SOSDigestVectorFree(&dvadd);
-
- // Only Write marker if we are actually in sync now (local == propopsed).
- if (SOSPeerSendObjects(peer))
- SOSBackupEventWriteCompleteMarker(journalFile, 799, error);
- });
-
- if (!ok) {
- // We were unable to stream everything out neatly
- SOSCCRequestSyncWithBackupPeer(SOSPeerGetID(peer));
- }
- return ok;
-}
-