SOSChangeTracker.c [plain text]
#import "keychain/SecureObjectSync/SOSChangeTracker.h"
#include "keychain/SecureObjectSync/SOSDigestVector.h"
#include "keychain/SecureObjectSync/SOSEnginePriv.h"
#include "keychain/SecureObjectSync/SOSManifest.h"
#include "keychain/SecureObjectSync/SOSInternal.h"
#include <utilities/SecCFError.h>
#include <utilities/SecCFWrappers.h>
CFStringRef SOSChangeCopyDescription(SOSChangeRef change) {
CFTypeRef object = NULL;
bool isAdd = SOSChangeGetObject(change, &object);
return (isData(object)
? isAdd ? CFSTR("a") : CFSTR("d")
: isAdd ? CFSTR("A") : CFSTR("D"));
}
CFDataRef SOSChangeCopyDigest(SOSDataSourceRef dataSource, SOSChangeRef change, bool *isDel, SOSObjectRef *object, CFErrorRef *error) {
CFDataRef digest = NULL;
if (isArray(change)) {
if (CFArrayGetCount(change) != 1) {
SecError(errSecDecode, error, CFSTR("change array count: %ld"), CFArrayGetCount(change));
return NULL;
}
change = CFArrayGetValueAtIndex(change, 0);
*isDel = true;
} else {
*isDel = false;
}
if (isData(change)) {
digest = (CFDataRef)CFRetain(change);
} else {
digest = SOSObjectCopyDigest(dataSource, (SOSObjectRef)change, error);
*object = (SOSObjectRef)change;
}
assert(digest && CFDataGetLength(digest) == CCSHA1_OUTPUT_SIZE);
return digest;
}
CFStringRef SOSChangesCopyDescription(CFArrayRef changes) {
CFMutableStringRef desc = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("("));
CFTypeRef change;
if (changes) CFArrayForEachC(changes, change) {
CFStringRef changeDesc = SOSChangeCopyDescription(change);
CFStringAppend(desc, changeDesc);
CFReleaseNull(changeDesc);
}
CFStringAppend(desc, CFSTR(")"));
return desc;
}
struct __OpaqueSOSChangeTracker {
CFRuntimeBase _base;
SOSManifestRef manifest; CFMutableArrayRef changeChildren; CFMutableArrayRef manifestChildren; };
static CFStringRef SOSChangeTrackerCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
SOSChangeTrackerRef ct = (SOSChangeTrackerRef)cf;
CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("<ChangeTracker %@ children %ld/%ld>"),
ct->manifest ? ct->manifest : (SOSManifestRef)CFSTR("NonConcrete"),
CFArrayGetCount(ct->changeChildren), CFArrayGetCount(ct->manifestChildren));
return desc;
}
static void SOSChangeTrackerDestroy(CFTypeRef cf) {
SOSChangeTrackerRef ct = (SOSChangeTrackerRef)cf;
CFReleaseSafe(ct->manifest);
CFReleaseSafe(ct->changeChildren);
CFReleaseSafe(ct->manifestChildren);
}
CFGiblisFor(SOSChangeTracker);
SOSChangeTrackerRef SOSChangeTrackerCreate(CFAllocatorRef allocator, bool isConcrete, CFArrayRef changeChildren, CFErrorRef *error) {
SOSChangeTrackerRef ct = NULL;
ct = CFTypeAllocate(SOSChangeTracker, struct __OpaqueSOSChangeTracker, allocator);
if (ct && isConcrete) {
ct->manifest = SOSManifestCreateWithData(NULL, error);
if (!ct->manifest)
CFReleaseNull(ct);
}
if (ct) {
if (changeChildren)
ct->changeChildren = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, changeChildren);
else
ct->changeChildren = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
ct->manifestChildren = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
}
return ct;
}
void SOSChangeTrackerSetConcrete(SOSChangeTrackerRef ct, bool isConcrete) {
if (!isConcrete)
CFReleaseNull(ct->manifest);
else if (!ct->manifest) {
ct->manifest = SOSManifestCreateWithData(NULL, NULL);
}
}
void SOSChangeTrackerRegisterChangeUpdate(SOSChangeTrackerRef ct, SOSChangeTrackerUpdatesChanges child) {
CFArrayAppendValue(ct->changeChildren, child);
}
void SOSChangeTrackerRegisterManifestUpdate(SOSChangeTrackerRef ct, SOSChangeTrackerUpdatesManifests child) {
CFArrayAppendValue(ct->manifestChildren, child);
}
void SOSChangeTrackerResetRegistration(SOSChangeTrackerRef ct) {
CFArrayRemoveAllValues(ct->changeChildren);
CFArrayRemoveAllValues(ct->manifestChildren);
}
void SOSChangeTrackerSetManifest(SOSChangeTrackerRef ct, SOSManifestRef manifest) {
CFRetainAssign(ct->manifest, manifest);
}
SOSManifestRef SOSChangeTrackerCopyManifest(SOSChangeTrackerRef ct, CFErrorRef *error) {
if (ct->manifest) {
return (SOSManifestRef)CFRetain(ct->manifest);
}
SOSErrorCreate(kSOSErrorNotConcreteError, error, NULL, CFSTR("ChangeTracker is not concrete"));
return NULL;
}
static bool SOSChangeTrackerCreateManifestsWithChanges(SOSEngineRef engine, CFArrayRef changes, SOSManifestRef *removals, SOSManifestRef *additions, CFErrorRef *error) {
bool ok = true;
struct SOSDigestVector dvdels = SOSDigestVectorInit;
struct SOSDigestVector dvadds = SOSDigestVectorInit;
struct SOSDigestVector *dv;
CFTypeRef change;
CFArrayForEachC(changes, change) {
CFDataRef digest, allocatedDigest = NULL;
if (isArray(change)) {
assert(CFArrayGetCount(change) == 1);
change = CFArrayGetValueAtIndex(change, 0);
dv = &dvdels;
} else {
dv = &dvadds;
}
if (isData(change)) {
digest = (CFDataRef)change;
} else {
CFErrorRef digestError = NULL;
digest = allocatedDigest = SOSObjectCopyDigest(SOSEngineGetDataSource(engine), (SOSObjectRef)change, &digestError);
if (!digest) {
secerror("change %@ SOSObjectCopyDigest: %@", change, digestError);
CFReleaseNull(digestError);
continue;
}
}
if (CFDataGetLength(digest) == 20) {
SOSDigestVectorAppend(dv, CFDataGetBytePtr(digest));
} else {
secerror("change %@ bad length digest: %@", change, digest);
}
CFReleaseNull(allocatedDigest);
}
if (ok && removals)
ok = *removals = SOSManifestCreateWithDigestVector(&dvdels, error);
if (ok && additions)
ok = *additions = SOSManifestCreateWithDigestVector(&dvadds, error);
SOSDigestVectorFree(&dvadds);
SOSDigestVectorFree(&dvdels);
return ok;
}
bool SOSChangeTrackerTrackChanges(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, CFArrayRef changes, CFErrorRef *error) {
bool ok = true;
if (changes && CFArrayGetCount(changes)) {
CFStringRef changesDesc = SOSChangesCopyDescription(changes);
secnotice("tracker", "%@ %s %s changes: %@", ct, phase == kSOSDataSourceTransactionWillCommit ? "will-commit" : phase == kSOSDataSourceTransactionDidCommit ? "did-commit" : "did-rollback",
source == kSOSDataSourceSOSTransaction ? "sos" :
source == kSOSDataSourceCKKSTransaction ? "ckks" :
source == kSOSDataSourceAPITransaction ? "api" :
"unknown",
changesDesc);
CFReleaseSafe(changesDesc);
if (ct->manifest || ct->manifestChildren) {
SOSManifestRef additions = NULL;
SOSManifestRef removals = NULL;
ok &= SOSChangeTrackerCreateManifestsWithChanges(engine, changes, &removals, &additions, error);
if (ok) {
if (ct->manifest) {
SOSManifestRef updatedManifest = SOSManifestCreateWithPatch(ct->manifest, removals, additions, error);
if (updatedManifest){
CFTransferRetained(ct->manifest, updatedManifest);
}
}
if (ct->manifestChildren) {
SOSChangeTrackerUpdatesManifests child;
CFArrayForEachC(ct->manifestChildren, child) {
ok = ok && child(ct, engine, txn, source, phase, removals, additions, error);
}
}
}
CFReleaseSafe(removals);
CFReleaseSafe(additions);
}
if (ct->changeChildren) {
SOSChangeTrackerUpdatesChanges child;
CFArrayForEachC(ct->changeChildren, child) {
ok = ok && child(ct, engine, txn, source, phase, changes, error);
}
}
}
return ok;
}