X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_transform/lib/Transform.cpp diff --git a/Security/libsecurity_transform/lib/Transform.cpp b/Security/libsecurity_transform/lib/Transform.cpp deleted file mode 100644 index 20db57fa..00000000 --- a/Security/libsecurity_transform/lib/Transform.cpp +++ /dev/null @@ -1,1834 +0,0 @@ -#include -#include -#include -#include -#include "Transform.h" -#include "StreamSource.h" -#include "SingleShotSource.h" -#include "Monitor.h" -#include "Utilities.h" -#include "c++utils.h" -#include "misc.h" -#include "SecTransformInternal.h" -#include "GroupTransform.h" -#include "GroupTransform.h" -#include - -static const int kMaxPendingTransactions = 20; - -static CFTypeID internalID = _kCFRuntimeNotATypeID; - -// Use &dispatchQueueToTransformKey as a key to dispatch_get_specific to map from -// a transforms master, activation, or any attribute queue to the Transform* -static unsigned char dispatchQueueToTransformKey; - -static char RandomChar() -{ - return arc4random() % 26 + 'A'; // good enough -} - - -static CFStringRef ah_set_describe(const void *v) { - transform_attribute *ta = ah2ta(static_cast(const_cast(v))); - return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@=%@ (conn: %@)"), ta->transform->GetName(), ta->name, ta->value ? ta->value : CFSTR("NULL"), ta->connections ? static_cast(ta->connections) : static_cast(CFSTR("NONE"))); -} - -static CFStringRef AttributeHandleFormat(CFTypeRef ah, CFDictionaryRef dict) { - transform_attribute *ta = ah2ta(ah); - return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), ta->transform->GetName(), ta->name); -} - -static CFStringRef AttributeHandleDebugFormat(CFTypeRef ah) { - transform_attribute *ta = ah2ta(ah); - return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@ (%p)"), ta->transform->GetName(), ta->name, ta); -} - -static void AttributeHandleFinalize(CFTypeRef ah) -{ - transform_attribute *ta = ah2ta(ah); - if (!ta) - { - return; - } - - if (ta->transform) - { - // When we release AH's we clear out the transform pointer, so if we get here with transform!=NULL somebody - // has released an AH we are still using and we will crash very very soon (in our code) if we don't abort here - syslog(LOG_ERR, "over release of SecTransformAttributeRef at %p\n", ah); - abort(); - } - - if (ta->value) - { - CFRelease(ta->value); - } - - // ta->q already released - - if (ta->connections) - { - CFRelease(ta->connections); - } - - if (ta->semaphore) - { - dispatch_release(ta->semaphore); - } - - if (ta->attribute_changed_block) - { - Block_release(ta->attribute_changed_block); - } - - if (ta->attribute_validate_block) - { - Block_release(ta->attribute_validate_block); - } - - free(ta); -} - - - -static CFHashCode ah_set_hash(const void *v) { - return CFHash(ah2ta(static_cast(const_cast(v)))->name); -} - -static Boolean ah_set_equal(const void *v1, const void *v2) { - return CFEqual(ah2ta(static_cast(const_cast(v1)))->name, ah2ta(static_cast(const_cast(v2)))->name); -} - -CFTypeID transform_attribute::cftype; - -SecTransformAttributeRef Transform::makeAH(transform_attribute *ta) { - if (ta) { - SecTransformAttributeRef ah = _CFRuntimeCreateInstance(NULL, transform_attribute::cftype, sizeof(struct transform_attribute*), NULL); - if (!ah) { - return NULL; - } - *(struct transform_attribute **)(1 + (CFRuntimeBase*)ah) = ta; - return ah; - } else { - return NULL; - } -} - -static pthread_key_t ah_search_key_slot; - -static void destroy_ah_search_key(void *ah) { - CFRelease(ah); - pthread_setspecific(ah_search_key_slot, NULL); -} - - - -SecTransformAttributeRef Transform::getAH(SecTransformStringOrAttributeRef attrib, bool create_ok, bool create_underscore_ok) -{ - if (CFGetTypeID(attrib) == transform_attribute::cftype) - { - return (SecTransformAttributeRef)attrib; - } - - CFStringRef label = (CFStringRef)attrib; - static dispatch_once_t once = 0; - const char *name = (const char *)"SecTransformAttributeRef"; - static CFRuntimeClass ahclass; - static CFSetCallBacks tasetcb; - - dispatch_once(&once, ^{ - ahclass.className = name; - ahclass.copyFormattingDesc = AttributeHandleFormat; - ahclass.copyDebugDesc = AttributeHandleDebugFormat; - ahclass.finalize = AttributeHandleFinalize; - transform_attribute::cftype = _CFRuntimeRegisterClass(&ahclass); - if (transform_attribute::cftype == _kCFRuntimeNotATypeID) { - abort(); - } - - tasetcb.equal = ah_set_equal; - tasetcb.hash = ah_set_hash; - tasetcb.copyDescription = ah_set_describe; - - pthread_key_create(&ah_search_key_slot, destroy_ah_search_key); - }); - - SecTransformAttributeRef search_for = pthread_getspecific(ah_search_key_slot); - if (!search_for) - { - search_for = makeAH((transform_attribute*)malloc(sizeof(transform_attribute))); - if (!search_for) - { - return NULL; - } - - bzero(ah2ta(search_for), sizeof(transform_attribute)); - pthread_setspecific(ah_search_key_slot, search_for); - } - - if (!mAttributes) - { - mAttributes = CFSetCreateMutable(NULL, 0, &tasetcb); - } - - ah2ta(search_for)->name = label; - SecTransformAttributeRef ah = static_cast(const_cast(CFSetGetValue(mAttributes, search_for))); - if (ah == NULL && create_ok) - { - if (CFStringGetLength(label) && L'_' == CFStringGetCharacterAtIndex(label, 0) && !create_underscore_ok) - { - // Attributes with a leading _ belong to the Transform system only, not to random 3rd party transforms. - return NULL; - } - - transform_attribute *ta = static_cast(malloc(sizeof(transform_attribute))); - ah = makeAH(ta); - if (!ah) - { - return NULL; - } - - ta->name = CFStringCreateCopy(NULL, label); - if (!ta->name) - { - free(ta); - return NULL; - } - CFIndex cnt = CFSetGetCount(mAttributes); - CFSetAddValue(mAttributes, ah); - if (CFSetGetCount(mAttributes) != cnt+1) - { - CFRelease(ta->name); - free(ta); - return NULL; - } - - CFStringRef qname = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/%@"), dispatch_queue_get_label(this->mDispatchQueue), label); - CFIndex used, sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(qname), kCFStringEncodingUTF8); - UInt8 *qnbuf = (UInt8 *)alloca(sz); - CFStringGetBytes(qname, CFRangeMake(0, CFStringGetLength(qname)), kCFStringEncodingUTF8, '?', FALSE, qnbuf, sz, &used); - qnbuf[used] = '\0'; - ta->q = dispatch_queue_create((char*)qnbuf, NULL); - CFRelease(qname); - ta->semaphore = dispatch_semaphore_create(kMaxPendingTransactions); - - - ta->pushback_state = transform_attribute::pb_empty; - ta->pushback_value = NULL; - ta->value = NULL; - ta->connections = NULL; - ta->transform = this; - - dispatch_set_target_queue(ta->q, mDispatchQueue); - ta->required = 0; - ta->requires_outbound_connection = 0; - ta->deferred = 0; - ta->stream = 0; - ta->ignore_while_externalizing = 0; - ta->has_incoming_connection = 0; - ta->direct_error_handling = 0; - ta->allow_external_sets = 0; - ta->has_been_deferred = 0; - ta->attribute_changed_block = NULL; - ta->attribute_validate_block = NULL; - } - - return ah; -} - -transform_attribute *Transform::getTA(SecTransformStringOrAttributeRef attrib, bool create_ok) -{ - SecTransformAttributeRef ah = getAH(attrib, create_ok); - if (ah) - { - return ah2ta(ah); - } - else - { - return NULL; - } -} - - - -void Transform::TAGetAll(transform_attribute **attributes) { - CFSetGetValues(mAttributes, (const void**)attributes); - CFIndex i, n = CFSetGetCount(mAttributes); - for(i = 0; i < n; ++i) { - attributes[i] = ah2ta(attributes[i]); - } -} - - - -bool Transform::HasNoOutboundConnections() -{ - // make an array big enough to hold all of the attributes - CFIndex numAttributes = CFSetGetCount(mAttributes); - transform_attribute* attributes[numAttributes]; - - TAGetAll(attributes); - - // check all of the attributes - CFIndex i; - for (i = 0; i < numAttributes; ++i) - { - if (attributes[i]->connections && CFArrayGetCount(attributes[i]->connections) != 0) - { - return false; - } - } - - return true; -} - - - -bool Transform::HasNoInboundConnections() -{ - // make an array big enough to hold all of the attributes - CFIndex numAttributes = CFSetGetCount(mAttributes); - transform_attribute* attributes[numAttributes]; - - TAGetAll(attributes); - - // check all of the attributes - CFIndex i; - for (i = 0; i < numAttributes; ++i) - { - if (attributes[i]->has_incoming_connection) - { - return false; - } - } - - return true; -} - - - -CFIndex Transform::GetAttributeCount() -{ - return CFSetGetCount(mAttributes); -} - -Transform::Transform(CFStringRef transformType, CFStringRef CFobjectType) : - CoreFoundationObject(CFobjectType), - mIsActive(false), - mIsFinalizing(false), - mAlwaysSelfNotify(false), - mGroup(NULL), - mAbortError(NULL), - mTypeName(CFStringCreateCopy(NULL, transformType)) -{ - mAttributes = NULL; - mPushedback = NULL; - mProcessingPushbacks = FALSE; - - if (internalID == _kCFRuntimeNotATypeID) { - (void)SecTransformNoData(); - internalID = CoreFoundationObject::FindObjectType(gInternalCFObjectName); - } - - // create a name for the transform - char rname[10]; - unsigned i; - for (i = 0; i < sizeof(rname) - 1; ++i) - { - rname[i] = RandomChar(); - } - - rname[i] = 0; - - char *tname = const_cast(CFStringGetCStringPtr(transformType, kCFStringEncodingUTF8)); - if (!tname) { - CFIndex sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType), kCFStringEncodingUTF8); - tname = static_cast(alloca(sz)); - if (tname) { - CFStringGetCString(transformType, tname, sz, kCFStringEncodingUTF8); - } else { - tname = const_cast("-"); - } - } - - char* name; - asprintf(&name, "%s-%s", rname, tname); - - char *dqName; - asprintf(&dqName, "%s-%s", rname, tname); - - char *aqName; - asprintf(&aqName, "aq-%s-%s", rname, tname); - - mDispatchQueue = dispatch_queue_create(dqName, NULL); - dispatch_queue_set_specific(mDispatchQueue, &dispatchQueueToTransformKey, this, NULL); - // mActivationQueue's job in life is to be suspended until just after this transform is made active. - // It's primary use is when a value flowing across a connection isn't sure if the target transform is active yet. - mActivationQueue = dispatch_queue_create(aqName, NULL); - dispatch_set_target_queue(mActivationQueue, mDispatchQueue); - dispatch_suspend(mActivationQueue); - mActivationPending = dispatch_group_create(); - - // set up points for ABORT, DEBUG, INPUT, and OUTPUT - AbortAH = getAH(kSecTransformAbortAttributeName, true); - transform_attribute *ta = ah2ta(AbortAH); - ta->ignore_while_externalizing = 1; - CFStringRef attributeName = CFStringCreateWithCStringNoCopy(NULL, name, 0, kCFAllocatorMalloc); - SetAttributeNoCallback(kSecTransformTransformName, attributeName); - CFRelease(attributeName); - - free(dqName); - free(aqName); - - DebugAH = getAH(kSecTransformDebugAttributeName, true); - ah2ta(DebugAH)->ignore_while_externalizing = 1; - - ta = getTA(kSecTransformInputAttributeName, true); - ta->required = ta->deferred = ta->stream = 1; - ta->allow_external_sets = 0; - ta->value = NULL; - ta->has_been_deferred = 0; - ta = getTA(kSecTransformOutputAttributeName, true); - ta->requires_outbound_connection = ta->stream = 1; -} - -static void run_and_release_finalizer(void *finalizer_block) -{ - ((dispatch_block_t)finalizer_block)(); - Block_release(finalizer_block); -} - -static void set_dispatch_finalizer(dispatch_object_t object, dispatch_block_t finalizer) -{ - finalizer = Block_copy(finalizer); - dispatch_set_context(object, finalizer); - dispatch_set_finalizer_f(object, run_and_release_finalizer); -} - -void Transform::FinalizePhase2() -{ - delete this; -} - -void Transform::FinalizeForClang() -{ - CFIndex numAttributes = CFSetGetCount(mAttributes); - SecTransformAttributeRef handles[numAttributes]; - CFSetGetValues(mAttributes, (const void**)&handles); - - for(CFIndex i = 0; i < numAttributes; ++i) { - SecTransformAttributeRef ah = handles[i]; - transform_attribute *ta = ah2ta(ah); - - set_dispatch_finalizer(ta->q, ^{ - // NOTE: not done until all pending use of the attribute queue has ended AND retain count is zero - ta->transform = NULL; - CFRelease(ah); - }); - // If there is a pending pushback the attribute queue will be suspended, and needs a kick before it can be destructed. - if (__sync_bool_compare_and_swap(&ta->pushback_state, transform_attribute::pb_value, transform_attribute::pb_discard)) { - dispatch_resume(ta->q); - } - dispatch_release(ta->q); - } - - // We might be finalizing a transform as it is being activated, make sure that is complete before we do the rest - dispatch_group_notify(mActivationPending, mDispatchQueue, ^{ - if (mActivationQueue != NULL) { - // This transform has not been activated (and does not have a activation pending), so we need to resume to activation queue before we can release it - dispatch_resume(mActivationQueue); - dispatch_release(mActivationQueue); - } - - set_dispatch_finalizer(mDispatchQueue, ^{ - // NOTE: delayed until all pending work items on the transform's queue are complete, and all of the attribute queues have been finalized, and the retain count is zero - FinalizePhase2(); - }); - dispatch_release(mDispatchQueue); - }); -} - -void Transform::Finalize() -{ - // When _all_ transforms in the group have been marked as finalizing we can tear down our own context without anyone else in the group sending us values - // (NOTE: moved block into member function as clang hits an internal error and declines to compile) - dispatch_block_t continue_finalization = ^{ this->FinalizeForClang(); }; - dispatch_block_t mark_as_finalizing = ^{ this->mIsFinalizing = true; }; - - // Mark the transform as "finalizing" so it knows not to propagate values across connections - if (this == dispatch_get_specific(&dispatchQueueToTransformKey)) { - mark_as_finalizing(); - } else { - dispatch_sync(mDispatchQueue, mark_as_finalizing); - } - - if (mGroup) { - (void)transforms_assume(mGroup->mIsFinalizing); // under retain? - mGroup->AddAllChildrenFinalizedCallback(mDispatchQueue, continue_finalization); - mGroup->ChildStartedFinalization(this); - } else { - // a "bare" transform (normally itself a group) still needs to be deconstructed - dispatch_async(mDispatchQueue, continue_finalization); - } -} - -Transform::~Transform() -{ - CFRelease(mAttributes); - if (mAbortError) { - CFRelease(mAbortError); - mAbortError = NULL; - } - - // See if we can catch anything using us after our death - mDispatchQueue = (dispatch_queue_t)0xdeadbeef; - - CFRelease(mTypeName); - - if (NULL != mPushedback) - { - CFRelease(mPushedback); - } - dispatch_release(mActivationPending); -} - -CFStringRef Transform::GetName() { - return (CFStringRef)GetAttribute(kSecTransformTransformName); -} - -CFTypeID Transform::GetCFTypeID() -{ - return CoreFoundationObject::FindObjectType(CFSTR("SecTransform")); -} - -std::string Transform::DebugDescription() -{ - return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName()); -} - -CFErrorRef Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type, CFTypeRef value) -{ - SecTransformAttributeRef ah = getAH(key, true); - transform_attribute *ta = ah2ta(ah); - switch (type) - { - case kSecTransformMetaAttributeRequired: - ta->required = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; - break; - - case kSecTransformMetaAttributeRequiresOutboundConnection: - ta->requires_outbound_connection = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; - break; - - case kSecTransformMetaAttributeDeferred: - ta->deferred = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; - break; - - case kSecTransformMetaAttributeStream: - ta->stream = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; - break; - - case kSecTransformMetaAttributeHasOutboundConnections: - return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah); - - case kSecTransformMetaAttributeHasInboundConnection: - return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah); - - case kSecTransformMetaAttributeCanCycle: - return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah); - - case kSecTransformMetaAttributeExternalize: - ta->ignore_while_externalizing = CFBooleanGetValue((CFBooleanRef)value) ? 0 : 1; - break; - - case kSecTransformMetaAttributeValue: - return SetAttributeNoCallback(ah, value); - - case kSecTransformMetaAttributeRef: - return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah); - - case kSecTransformMetaAttributeName: - return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah); - - default: - return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set unknown meta attribute #%d to %@ on %@", type, value, key); - } - - return NULL; -} - -CFTypeRef Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type) { - SecTransformAttributeRef ah = getAH(key, true); - transform_attribute *ta = ah2ta(ah); - switch (type) { - case kSecTransformMetaAttributeRequired: - return (CFTypeRef)(ta->required ? kCFBooleanTrue : kCFBooleanFalse); - case kSecTransformMetaAttributeRequiresOutboundConnection: - return (CFTypeRef)(ta->requires_outbound_connection ? kCFBooleanTrue : kCFBooleanFalse); - case kSecTransformMetaAttributeDeferred: - return (CFTypeRef)(ta->deferred ? kCFBooleanTrue : kCFBooleanFalse); - case kSecTransformMetaAttributeStream: - return (CFTypeRef)(ta->stream ? kCFBooleanTrue : kCFBooleanFalse); - case kSecTransformMetaAttributeHasOutboundConnections: - return (CFTypeRef)((ta->connections && CFArrayGetCount(ta->connections)) ? kCFBooleanTrue : kCFBooleanFalse); - case kSecTransformMetaAttributeHasInboundConnection: - return (CFTypeRef)(ta->has_incoming_connection ? kCFBooleanTrue : kCFBooleanFalse); - case kSecTransformMetaAttributeCanCycle: - return (CFTypeRef)kCFBooleanFalse; - case kSecTransformMetaAttributeExternalize: - return (CFTypeRef)(ta->ignore_while_externalizing ? kCFBooleanFalse : kCFBooleanTrue); - case kSecTransformMetaAttributeRef: - return ah; - case kSecTransformMetaAttributeValue: - return ta->value; - case kSecTransformMetaAttributeName: - return ta->name; - default: - return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't get unknown meta attribute #%d from %@", type, key); - break; - } - - return NULL; -} - - - -CFErrorRef Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError) -{ - // pull apart the error - CFIndex code = CFErrorGetCode(sourceError); - CFStringRef domain = CFErrorGetDomain(sourceError); - CFDictionaryRef oldUserInfo = CFErrorCopyUserInfo(sourceError); - CFMutableDictionaryRef userInfo = CFDictionaryCreateMutableCopy(NULL, 0, oldUserInfo); - CFRelease(oldUserInfo); - - // add the new key and value to the dictionary - CFDictionaryAddValue(userInfo, kSecTransformAbortOriginatorKey, GetCFObject()); - - // make a new CFError - CFErrorRef newError = CFErrorCreate(NULL, domain, code, userInfo); - CFRelease(userInfo); - return newError; -} - -// NOTE: If called prior to execution will schedule a later call to AbortAllTransforms -void Transform::AbortJustThisTransform(CFErrorRef abortErr) -{ - (void)transforms_assume(abortErr); - (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey) == this); - - Boolean wasActive = mIsActive; - - if (OSAtomicCompareAndSwapPtr(NULL, (void *)abortErr, (void**)&mAbortError)) { - // send an abort message to the attribute so that it can shut down - // note that this bypasses the normal processes. The message sent is a notification - // that things aren't working well any more, the transform cannot make any other assumption. - - // mAbortError is released in the destructor which is triggered (in part) - // by the dispatch queue finalizer so we don't need a retain/release of - // abortErr for the abortAction block, but we do need to retain it - // here to match with the release by the destructor. - CFRetain(abortErr); - - dispatch_block_t abortAction = ^{ - // This actually makes the abort happen, it needs to run on the transform's queue while the - // transform is executing. - - if (!wasActive) { - // When this abort was first processed we were not executing, so - // additional transforms may have been added to our group (indeed, - // we may not have had a group at all), so we need to let everyone - // know about the problem. This will end up letting ourself (and - // maybe some others) know an additional time, but the CompareAndSwap - // prevents that from being an issue. - this->AbortAllTransforms(abortErr); - } - - SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle = getAH(kSecTransformInputAttributeName, false); - // Calling AttributeChanged directly lets an error "skip ahead" of the input queue, - // and even execute if the input queue is suspended pending pushback retries. - AttributeChanged(inputAttributeHandle, abortErr); - try_pushbacks(); - }; - - if (mIsActive) { - // This transform is running, so we use the normal queue (which we are - // already executing on) - abortAction(); - } else { - // This transform hasn't run yet, do the work on the activation queue - // so it happens as soon as the transforms starts executing. - dispatch_async(mActivationQueue, abortAction); - } - } else { - Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH, abortErr, mAbortError); - } -} - -// abort all transforms in the root group & below -void Transform::AbortAllTransforms(CFTypeRef err) -{ - Debug("%@ set to %@, aborting\n", AbortAH, err); - CFErrorRef error = NULL; - - CFTypeRef replacementErr = NULL; - - if (CFGetTypeID(err) != CFErrorGetTypeID()) - { - replacementErr = err = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "ABORT set to a %@ (%@) not a %@", CFCopyTypeIDDescription(CFGetTypeID(err)), err, CFCopyTypeIDDescription(CFErrorGetTypeID())); - } - - error = RefactorErrorToIncludeAbortingTransform((CFErrorRef)err); - - if (replacementErr) - { - CFRelease(replacementErr); - } - - GroupTransform *root = GetRootGroup(); - if (root) - { - // tell everyone in the (root) group to "AbortJustThisTransform" - dispatch_group_t all_aborted = dispatch_group_create(); - root->ForAllNodesAsync(false, all_aborted, ^(Transform* t){ - t->AbortJustThisTransform(error); - }); - dispatch_group_notify(all_aborted, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) { - CFRelease(error); - dispatch_release(all_aborted); - }); - } - else - { - // We are everyone so we AbortJustThisTransform "directly" - // NOTE: this can only happen prior to execution (execution always happens in a group) - (void)transforms_assume_zero(mIsActive); - this->AbortJustThisTransform(error); - } -} - - - -CFErrorRef Transform::Disconnect(Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey) -{ - //CFTypeRef thisTransform = (SecTransformRef) GetCFObject(); - - // find this transform in the backlinks for the destination - CFIndex i; - - // now remove the link in the transform dictionary - transform_attribute *src = getTA(myKey, true); - SecTransformAttributeRef dst = destinationTransform->getAH(hisKey); - - if (src->connections == NULL) - { - return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Cannot find transform in destination."); - } - - CFIndex numConnections = CFArrayGetCount(src->connections); - for (i = 0; i < numConnections; ++i) - { - if (CFArrayGetValueAtIndex(src->connections, i) == dst) - { - CFArrayRemoveValueAtIndex(src->connections, i); - numConnections = CFArrayGetCount(src->connections); - } - - // clear the has_incoming_connection bit in the destination. We can do this because inputs can have only one connection. - transform_attribute* dstTA = ah2ta(dst); - dstTA->has_incoming_connection = false; - } - - if (HasNoInboundConnections() && HasNoOutboundConnections()) - { - // we have been orphaned, just remove us - mGroup->RemoveMemberFromGroup(GetCFObject()); - mGroup = NULL; - } - - return NULL; -} - - - -CFErrorRef Transform::Connect(GroupTransform *group, Transform* destinationTransform, CFStringRef destAttr, CFStringRef srcAttr) -{ - if (group == NULL) - { - CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections without a specific group (do not call with group = NULL)"); - return err; - } - - GroupTransform *newSourceGroup = mGroup; - GroupTransform *newDestinationGroup = destinationTransform->mGroup; - - if (mGroup == NULL || mGroup == this) - { - newSourceGroup = group; - } - - if (destinationTransform->mGroup == NULL || destinationTransform->mGroup == destinationTransform) - { - newDestinationGroup = group; - } - - if (newSourceGroup != newDestinationGroup && mGroup) - { - CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup->GetName(), destinationTransform->GetName(), newDestinationGroup->GetName()); - return err; - } - - if (!validConnectionPoint(srcAttr)) { - CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection from non-exported attribute %@ of %@", srcAttr, this->GetName()); - return err; - } - if (!destinationTransform->validConnectionPoint(destAttr)) { - CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection to non-exported attribute %@ of %@", destAttr, destinationTransform->GetName()); - return err; - } - - mGroup = newSourceGroup; - destinationTransform->mGroup = newDestinationGroup; - - // NOTE: this fails on OOM - group->AddMemberToGroup(this->GetCFObject()); - group->AddMemberToGroup(destinationTransform->GetCFObject()); - - transform_attribute *src = this->getTA(srcAttr, true); - SecTransformAttributeRef dst = destinationTransform->getAH(destAttr); - - if (!src->connections) - { - src->connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - } - CFArrayAppendValue(src->connections, dst); - - ah2ta(dst)->has_incoming_connection = 1; - - return NULL; -} - - -bool Transform::validConnectionPoint(CFStringRef attributeName) -{ - return true; -} - -// NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes -// SetAttribute eventually calls SetAttributeNoCallback -CFErrorRef Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key, CFTypeRef value) -{ - SecTransformAttributeRef ah = getAH(key, true); - if (!ah) - { - abort(); - } - transform_attribute *ta = ah2ta(ah); - - if (ah == AbortAH && value && (mIsActive || !ta->deferred)) - { - AbortAllTransforms(value); - return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress, "Abort started"); - } - - bool do_propagate = true; - - if (!ta->has_been_deferred) - { - bool doNotRetain = false; - - if (value) - { - CFStringRef name = ta->name; - if (CFGetTypeID(value) == CFReadStreamGetTypeID()) - { - CFTypeRef src = StreamSource::Make((CFReadStreamRef) value, this, name); - value = src; - do_propagate = false; - ta->has_been_deferred = 1; - doNotRetain = true; - } - else if (ta->deferred && !mIsActive) - { - if (ta->deferred) - { - Debug("%@ deferred value=%p\n", ah, value); - } - - CFTypeRef src = SingleShotSource::Make(value, this, name); - ta->has_been_deferred = 1; - - // the old value will be release when Transform::Do terminates - - value = src; - do_propagate = false; - doNotRetain = true; - } - else - { - ta->has_been_deferred = 0; - } - } - - if (ta->value != value) { - if (value && !doNotRetain) { - CFRetain(value); - } - if (ta->value) { - CFRelease(ta->value); - } - } - - ta->value = value; - } - - // propagate the changes out to all connections - if (ta->connections && mIsActive && do_propagate && !(mAbortError || mIsFinalizing)) - { - Debug("Propagating from %@ to %@\n", ah, ta->connections); - CFIndex i, numConnections = CFArrayGetCount(ta->connections); - for(i = 0; i < numConnections; ++i) { - SecTransformAttributeRef ah = static_cast(const_cast(CFArrayGetValueAtIndex(ta->connections, i))); - Transform *tt = ah2ta(ah)->transform; - if (NULL != tt) - { - if (tt->mIsActive) - { - tt->SetAttribute(ah, value); - } - else - { - dispatch_block_t setAttribute = ^{ - tt->SetAttribute(ah, value); - }; - // Here the target queue might not be activated yet, we can't - // look directly at the other transform's ActivationQueue as - // it might activate (or Finalize!) as we look, so just ask - // the other transform to deal with it. - dispatch_async(ah2ta(ah)->q, ^(void) { - // This time we are on the right queue to know this is the real deal - if (tt->mIsActive) { - setAttribute(); - } else { - dispatch_async(ah2ta(ah)->transform->mActivationQueue, setAttribute); - } - }); - } - } - } - } - - return NULL; -} - -// external sets normally fail if the transform is running -CFErrorRef Transform::ExternalSetAttribute(CFTypeRef key, CFTypeRef value) -{ - if (!mIsActive) - { - return this->SetAttribute(key, value); - } - else - { - SecTransformAttributeRef ah = getAH(key, false); - if (ah != NULL && ah2ta(ah)->allow_external_sets) - { - return this->SetAttribute(static_cast(ah), value); - } - else - { - return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "%@ can not be set while %@ is executing", ah, this->GetName()); - } - } -} - - -// queue up the setting of the key and value -CFErrorRef Transform::SetAttribute(CFTypeRef key, CFTypeRef value) -{ - if (mAbortError) - { - return CreateSecTransformErrorRef(kSecTransformErrorAborted, "ABORT has been sent to the transform (%@)", mAbortError); - } - - // queue up the setting of the key and value - SecTransformAttributeRef ah; - if (CFGetTypeID(key) == transform_attribute::cftype) - { - ah = key; - } - else if (CFGetTypeID(key) == CFStringGetTypeID()) - { - ah = getAH(static_cast(key)); - if (!ah) - { - return CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute, "Can't set attribute %@ in transform %@", key, GetName()); - } - } - else - { - return CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key); - } - - // Do this after the error check above so we don't leak - if (value != NULL) - { - CFRetain(value); // if we use dispatch_async we need to own the value (the matching release is in the set block) - } - - - transform_attribute *ta = ah2ta(ah); - - dispatch_block_t set = ^{ - Do(ah, value); - - dispatch_semaphore_signal(ta->semaphore); - - if (value != NULL) - { - CFRelease(value); - } - }; - - - // when the transform is active, set attributes asynchronously. Otherwise, we are doing - // initialization and must wait for the operation to complete. - if (mIsActive) - { - dispatch_async(ta->q, set); - } - else - { - dispatch_sync(ta->q, set); - } - if (dispatch_semaphore_wait(ta->semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC))) { - Debug("Send from %@ to %@ is still waiting\n", GetName(), ah); - dispatch_semaphore_wait(ta->semaphore, DISPATCH_TIME_FOREVER); - } - - // Return the best available status (which will be NULL if we haven't aborted, or stated an - // intent to abort when execution starts) - // - // The value of the ABORT attribute can differ from mAbortError, first if a transform is aborted - // prior to running the general abort mechanic is deferred until execution. Second during - // execution the abort logic avoids most of the normal processing. Third, and most importantly - // during an abort the exact error that gets generated will differ from the value sent to ABORT - // (for example if a non-CFError was sent...plus even if it was a CFError we annotate that error). - - return mAbortError; -} - -CFErrorRef Transform::SendAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value) -{ - return SetAttributeNoCallback(key, value); -} - - - -CFTypeRef Transform::GetAttribute(SecTransformStringOrAttributeRef key) -{ - struct transform_attribute *ta = getTA(key, false); - if (ta == NULL || ta->value == NULL) { - return NULL; - } - - if (CFGetTypeID(ta->value) == internalID) - { - // this is one of our internal objects, so get the value from it - Source* source = (Source*) CoreFoundationHolder::ObjectFromCFType(ta->value); - return source->GetValue(); - } - else - { - return ta->value; - } -} - -CFErrorRef Transform::Pushback(SecTransformAttributeRef ah, CFTypeRef value) -{ - CFErrorRef result = NULL; - transform_attribute *ta = ah2ta(ah); - if (!(ta->pushback_state == transform_attribute::pb_empty || ta->pushback_state == transform_attribute::pb_repush)) - { - CFErrorRef error = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidOperation, CFSTR("Can not pushback new value until old value has been processed")); - SetAttribute(kSecTransformAbortAttributeName, error); - return error; - } - if (value == NULL && ta->pushback_value == NULL && ta->pushback_state == transform_attribute::pb_repush) - { - ta->pushback_state = transform_attribute::pb_presented_once; - } else - { - ta->pushback_state = transform_attribute::pb_value; - } - if (value) - { - CFRetain(value); - } - ta->pushback_value = value; - dispatch_suspend(ta->q); - if (!mPushedback) - { - mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - } - CFArrayAppendValue(mPushedback, ah); - return result; -} - -void Transform::try_pushbacks() { - if (!mPushedback || !CFArrayGetCount(mPushedback)) { - mProcessingPushbacks = FALSE; - return; - } - - CFArrayRef pb = (CFArrayRef)mPushedback; - mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - CFIndex i, n = CFArrayGetCount(pb); - int succeeded = 0; - for(i = 0; i < n; ++i) - { - SecTransformAttributeRef ah = CFArrayGetValueAtIndex(pb, i); - transform_attribute *ta = ah2ta(ah); - ta->pushback_state = transform_attribute::pb_repush; - CFTypeRef v = ta->pushback_value; - ta->pushback_value = NULL; - Do(ah, v); - if (v) - { - CFRelease(v); - } - if (ta->pushback_state == transform_attribute::pb_repush) { - ta->pushback_state = transform_attribute::pb_empty; - succeeded++; - } - // NOTE: a successful repush needs the queue unsuspended so it can run. - // A failed repush has suspended the queue an additional time, so we - // still need to resume it. - dispatch_resume(ta->q); - } - - CFRelease(pb); - - if (succeeded && CFArrayGetCount(mPushedback)) { - // some attribute changed while we proceeded the last batch of pushbacks, so any "new" pushbacks are eligible to run again. - // In theory the ones that were pushed after the last success don't need to be re-run but that isn't a big deal. - dispatch_async(mDispatchQueue, ^{ try_pushbacks(); }); - } else { - mProcessingPushbacks = FALSE; - } -} - -void Transform::Debug(const char *cfmt, ...) { - CFTypeRef d = ah2ta(DebugAH)->value; - if (d) { - CFWriteStreamRef out = NULL; - if (CFGetTypeID(d) == CFWriteStreamGetTypeID()) { - out = (CFWriteStreamRef)d; - } else { - static dispatch_once_t once; - static CFWriteStreamRef StdErrWriteStream; - dispatch_once(&once, ^{ - auto GCC_BUG_WORKAROUND CFURLRef GCC_BUG_WORKAROUND p = CFURLCreateWithFileSystemPath(NULL, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle, FALSE); - StdErrWriteStream = CFWriteStreamCreateWithFile(NULL, p); - CFWriteStreamOpen(StdErrWriteStream); - CFRelease(p); - }); - out = StdErrWriteStream; - } - - va_list ap; - va_start(ap, cfmt); - - CFStringRef fmt = CFStringCreateWithCString(NULL, cfmt, kCFStringEncodingUTF8); - CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, ap); - CFRelease(fmt); - va_end(ap); - - - CFIndex sz = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); - sz += 1; - CFIndex used = 0; - unsigned char *buf; - bool needs_free = true; - buf = (unsigned char*)malloc(sz); - if (buf) { - CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, '?', FALSE, buf, sz, &used); - } else { - buf = (unsigned char *)"malloc failure during Transform::Debug\n"; - needs_free = false; - } - - static dispatch_once_t once; - static dispatch_queue_t print_q; - dispatch_once(&once, ^{ - print_q = dispatch_queue_create("com.apple.security.debug.print_queue", 0); - dispatch_set_target_queue((dispatch_object_t)print_q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)); - }); - - dispatch_async(print_q, ^{ - CFWriteStreamWrite(out, buf, used); - if (needs_free) { - free(buf); - } - }); - - CFRelease(str); - } -} - -void Transform::Do(SecTransformAttributeRef ah, CFTypeRef value) -{ - transform_attribute *ta = ah2ta(ah); - if (ta->pushback_state == transform_attribute::pb_discard) - { - return; - } - (void)transforms_assume(dispatch_get_current_queue() == ((ta->pushback_state == transform_attribute::pb_repush) ? mDispatchQueue : ta->q)); - - if (mIsFinalizing) - { - Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value, ah, dispatch_queue_get_label(dispatch_get_current_queue())); - return; - } - - SetAttributeNoCallback(ah, value); - // While an abort is in progress things can get into bad - // states if we allow normal processing so we throw anything - // on the floor except CFErrorRef or NULL vales sent to - // ABORT or INPUT (we need to process them to let the - // transform shut down correctly) - if (mAbortError && (!(ah == this->AbortAH || ah == getTA(CFSTR("INPUT"), true)) && (value == NULL || CFGetTypeID(value) != CFErrorGetTypeID()))) - { - if (value) { - Debug("Ignoring value (%@) sent to %@ during abort\n", value, ah); - } else { - Debug("Ignoring NULL sent to %@ during abort\n", ah); - } - return; - } - - if (mIsActive || (mAlwaysSelfNotify && !ta->deferred)) - { - Debug("AttributeChanged: %@ (%s) = %@\n", ah, mIsActive ? "is executing" : "self notify set", value ? value : (CFTypeRef)CFSTR("(NULL)")); - AttributeChanged(ah, value); - } - - if (mPushedback && CFArrayGetCount(mPushedback) && !mProcessingPushbacks) - { - Debug("will process pushbacks (%@) later\n", mPushedback); - mProcessingPushbacks = TRUE; - dispatch_async(mDispatchQueue, ^{ try_pushbacks(); }); - } - - return; -} - - -void Transform::AttributeChanged(CFStringRef name, CFTypeRef value) -{ -} - -void Transform::AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value) -{ - AttributeChanged(ah2ta(ah)->name, value); -} - -CFArrayRef Transform::GetAllAH() { - CFIndex cnt = CFSetGetCount(mAttributes); - const void **values = (const void **)alloca(sizeof(void*)*cnt); - CFSetGetValues(mAttributes, values); - return CFArrayCreate(NULL, values, cnt, &kCFTypeArrayCallBacks); -} - -CFTypeRef Transform::Execute(dispatch_queue_t deliveryQueue, SecMessageBlock deliveryBlock, CFErrorRef* errorRef) -{ - if (!mGroup) - { - CFTypeRef g = GroupTransform::Make(); - mGroup = (GroupTransform*)CoreFoundationHolder::ObjectFromCFType(g); - mGroup->AddMemberToGroup(this->GetCFObject()); - SecMessageBlock smb = ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) - { - deliveryBlock(message, error, isFinal); - if (isFinal) - { - dispatch_async(this->mDispatchQueue, ^{ - CFRelease(g); - }); - } - }; - - CFTypeRef ret = this->Execute(deliveryQueue, deliveryBlock ? smb : (SecMessageBlock) NULL, errorRef); - - if (!deliveryBlock) - { - CFRelease(g); - } - - return ret; - } - - if (mIsActive) - { - if (errorRef) - { - *errorRef = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform has already executed, it may not be executed again.", GetName()); - } - - return NULL; - } - - // Do a retain on our parent since we are using it - GroupTransform *rootGroup = GetRootGroup(); - CFRetain(rootGroup->GetCFObject()); - - CFTypeRef result = NULL; - - CFTypeRef monitorRef = BlockMonitor::Make(deliveryQueue, deliveryBlock); - - __block CFStringRef outputAttached = NULL; - - dispatch_queue_t p2 = dispatch_queue_create("activate phase2", NULL); - dispatch_queue_t p3 = dispatch_queue_create("activate phase3", NULL); - dispatch_suspend(p2); - dispatch_suspend(p3); - // walk the transform, doing phase1 activating as we go, and queueing phase2 and phase3 work - CFErrorRef temp = TraverseTransform(NULL, ^(Transform *t){ - return t->ExecuteOperation(outputAttached, (SecMonitorRef)monitorRef, p2, p3); - }); - // ExecuteOperation is not called for the outer group, so we need to manually set mISActive for it. - rootGroup->mIsActive = true; - rootGroup->StartingExecutionInGroup(); - dispatch_resume(p2); - dispatch_sync(p2, ^{ dispatch_resume(p3); }); - dispatch_sync(p3, ^{ dispatch_release(p2); }); - dispatch_release(p3); - - if (errorRef) - { - *errorRef = temp; - } - if (temp) { - // It is safe to keep the monitors attached, because it is invalid to try to execute again, BUT - // we do need to release the reference to the group that the monitor would normally release - // when it processes the final message. - CFRelease(rootGroup->GetCFObject()); - CFRelease(monitorRef); - rootGroup->StartedExecutionInGroup(false); - return NULL; - } - - dispatch_group_t initialized = dispatch_group_create(); - rootGroup->ForAllNodesAsync(true, initialized, ^(Transform*t) { - t->Initialize(); - }); - - dispatch_group_notify(initialized, rootGroup->mDispatchQueue, ^{ - dispatch_release(initialized); - dispatch_group_t activated = dispatch_group_create(); - dispatch_group_enter(activated); - dispatch_async(rootGroup->mDispatchQueue, ^{ - rootGroup->ForAllNodesAsync(true, activated, ^(Transform*t) { - t->ActivateInputs(); - }); - dispatch_group_leave(activated); - }); - dispatch_group_notify(activated, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - dispatch_release(activated); - // once we have been activated (but not before!), the monitor belongs to the group, and we can drop our claim - CFRelease(monitorRef); - rootGroup->StartedExecutionInGroup(true); - }); - }); - - return result; -} - - -void Transform::Initialize() -{ -} - -static void ActivateInputs_set(const void *v, void *unused) { - transform_attribute *ta = static_cast(ah2ta(const_cast(v))); - if (ta->value && internalID == CFGetTypeID(ta->value)) { - Source* s = (Source*) CoreFoundationHolder::ObjectFromCFType(ta->value); - s->Activate(); - } -} - -void Transform::ActivateInputs() -{ - (void)transforms_assume_zero(mIsActive && this != dispatch_get_specific(&dispatchQueueToTransformKey)); - - // now run all of the forward links - if (!mIsFinalizing) { - CFSetApplyFunction(mAttributes, ActivateInputs_set, NULL); - } -} - -CFErrorRef Transform::ForAllNodes(bool parallel, bool includeOwningGroup, Transform::TransformOperation op) -{ - GroupTransform *g = GetRootGroup(); - if (g) { - return g->ForAllNodes(parallel, includeOwningGroup, op); - } else { - return op(this); - } -} - -CFErrorRef Transform::TraverseTransform(CFMutableSetRef visited, TransformOperation t) -{ - return ForAllNodes(true, true, t); -} - -CFErrorRef Transform::ExecuteOperation(CFStringRef &outputAttached, SecMonitorRef output, dispatch_queue_t phase2, dispatch_queue_t phase3) -{ - if (!mGroup) { - // top level groups are special, and don't go through this path. - return NULL; - } - - if (!TransformCanExecute()) - { - // oops, this transform isn't ready to go - return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "The transform %@ was not ready for execution.", GetName()); - } - - // check to see if required attributes are connected or set - CFIndex i, numAttributes = CFSetGetCount(mAttributes); - transform_attribute **attributes = (transform_attribute **)alloca(numAttributes * sizeof(transform_attribute *)); - TAGetAll(attributes); - CFMutableArrayRef still_need = NULL; - for(i = 0; i < numAttributes; ++i) { - transform_attribute *ta = attributes[i]; - if (ta->required && ta->value == NULL && !ta->has_incoming_connection) { - if (!still_need) { - still_need = CFArrayCreateMutable(NULL, i, &kCFTypeArrayCallBacks); - } - CFArrayAppendValue(still_need, ta->name); - } - } - if (still_need) { - CFStringRef elist = CFStringCreateByCombiningStrings(NULL, still_need, CFSTR(", ")); - CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorMissingParameter, "Can not execute %@, missing required attributes: %@", GetName(), elist); - CFRelease(elist); - CFRelease(still_need); - return err; - } - - // see if we can attach our output here (note mAttributes may have changed) - numAttributes = CFSetGetCount(mAttributes); - attributes = (transform_attribute **)alloca(numAttributes * sizeof(transform_attribute *)); - TAGetAll(attributes); - for (i = 0; i < numAttributes; ++i) - { - transform_attribute *ta = attributes[i]; - CFIndex arraySize = ta->connections ? CFArrayGetCount(ta->connections) : 0; - if (arraySize == 0 && ta->requires_outbound_connection) - { - if (CFStringCompare(ta->name, kSecTransformOutputAttributeName, 0) == kCFCompareEqualTo) { - // this is a place where we can hook up our output -- maybe - if (outputAttached) - { - // oops, we've already done that. - return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput, "Both %@ and %@ have loose outputs, attach one to something", outputAttached, ta->transform->GetName()); - } - // Delay the connect until after ForAllNodes returns - dispatch_async(phase2, ^{ - SecTransformConnectTransformsInternal(mGroup->GetCFObject(), - GetCFObject(), kSecTransformOutputAttributeName, - output, kSecTransformInputAttributeName); - }); - outputAttached = ta->transform->GetName(); - - // activate the attached monitor - Monitor* m = (Monitor*) CoreFoundationHolder::ObjectFromCFType(output); - m->mIsActive = true; - - // add the monitor to the output so that it doesn't get activated twice - } else { - return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta->name, GetName()); - } - - break; - } - } - - // Delay activation until after the Monitor is connected - dispatch_async(phase3, ^{ - phase3Activation(); - }); - - return NULL; -} - - - -void Transform::DoPhase3Activation() -{ - this->mIsActive = true; - // execution has now truly started ("mIsActive is true") - CFErrorRef initError = TransformStartingExecution(); - if (initError) - { - // Oops, now execution is about to grind to a halt - this->SendAttribute(AbortAH, initError); - } - - dispatch_resume(this->mActivationQueue); - dispatch_group_async(this->mActivationPending, this->mActivationQueue, ^{ - dispatch_release(this->mActivationQueue); - this->mActivationQueue = NULL; - }); -} - - - -// This would be best expressed as a block, but we seem to run into compiler errors -void Transform::phase3Activation() -{ - dispatch_async(this->mDispatchQueue, ^ - { - DoPhase3Activation(); - }); -} - - -Boolean Transform::TransformCanExecute() -{ - return true; -} - - - -CFErrorRef Transform::TransformStartingExecution() -{ - return NULL; -} - - - -bool Transform::IsExternalizable() -{ - return true; -} - -static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator, const void *value) { - if (value != NULL) { - return CFRetain(value); - } else { - return value; - } -} - -static void CFTypeOrNULLRelease(CFAllocatorRef allocator, const void *value) { - if (value != NULL) { - CFRelease(value); - } -} - -static CFStringRef CFTypeOrNULLCopyDescription (const void *value) { - if (value != NULL) { - return CFCopyDescription(value); - } else { - return CFSTR("NULL"); - } -} - -static Boolean CFTypeOrNULLEqual(const void *value1, const void *value2) { - if (value1 == NULL && value2 == NULL) { - return TRUE; - } else { - if (value1 == NULL || value2 == NULL) { - return FALSE; - } else { - return CFEqual(value1, value2); - } - } -} - -// Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState -CFDictionaryRef Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key) -{ - SecTransformMetaAttributeType types[] = - { - kSecTransformMetaAttributeRequired, - kSecTransformMetaAttributeRequiresOutboundConnection, - kSecTransformMetaAttributeDeferred, - kSecTransformMetaAttributeStream, - kSecTransformMetaAttributeCanCycle, - kSecTransformMetaAttributeValue - }; - - CFIndex i, cnt = sizeof(types)/sizeof(SecTransformMetaAttributeType); - CFTypeRef values[cnt]; - CFNumberRef keys[cnt]; - key = getAH(key); - - // NOTE: we save meta attributes that are in their "default" state on purpose because the - // default may change in the future and we definitely want to restore the default values at - // time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8 - // default we want to load all old transforms with stream=1, the simplest way to do that is - // to store all values, not just non-default values) - for(i = 0; i < cnt; ++i) - { - values[i] = GetMetaAttribute(key, types[i]); - int tmp = (int)types[i]; - keys[i] = CFNumberCreate(NULL, kCFNumberIntType, &tmp); - } - - static CFDictionaryValueCallBacks CFTypeOrNULL; - static dispatch_once_t once; - dispatch_block_t b = - ^{ - CFTypeOrNULL.version = 0; - CFTypeOrNULL.retain = CFTypeOrNULLRetain; - CFTypeOrNULL.release = CFTypeOrNULLRelease; - CFTypeOrNULL.copyDescription = CFTypeOrNULLCopyDescription; - CFTypeOrNULL.equal = CFTypeOrNULLEqual; - }; - dispatch_once(&once, b); - - CFDictionaryRef ret = CFDictionaryCreate(NULL, (const void**)&keys, (const void**)&values, cnt, &kCFTypeDictionaryKeyCallBacks, &CFTypeOrNULL); - - for(i = 0; i < cnt; ++i) - { - CFRelease(keys[i]); - } - - return ret; -} - -// return everything that doesn't have ignore_while_externalizing set -CFDictionaryRef Transform::CopyState() -{ - CFIndex i, j, cnt = CFSetGetCount(mAttributes); - transform_attribute *attrs[cnt]; - CFStringRef names[cnt]; - CFDictionaryRef values[cnt]; - TAGetAll(attrs); - for(i = j = 0; i < cnt; ++i) - { - transform_attribute *ta = attrs[i]; - if (!ta->ignore_while_externalizing) - { - names[j] = ta->name; - values[j++] = GetAHDictForSaveState(ta->name); - } - } - - CFDictionaryRef result = CFDictionaryCreate(NULL, (const void**)&names, (const void**)&values, j, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - for(i = j = 0; i < cnt; ++i) - { - transform_attribute *ta = attrs[i]; - if (!ta->ignore_while_externalizing) - { - CFRelease(values[j++]); - } - } - - return result; -} - - - -void Transform::RestoreState(CFDictionaryRef state) -{ - CFIndex i, cnt = CFDictionaryGetCount(state); - const void - **keys = (const void **)alloca(sizeof(void*)*cnt), - **values = (const void **)alloca(sizeof(void*)*cnt); - - CFDictionaryGetKeysAndValues(state, keys, values); - - // Open issue -- do we need to do anything to values that are already set, but are not in "state"? - // this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects, - // it only becomes an issue if we add a ResetFromState, or use it internally in that role. - - for(i = 0; i < cnt; i++) - { - SecTransformAttributeRef ah = getAH(keys[i]); - - if (NULL == ah) - { - continue; - } - - CFIndex j, meta_cnt = CFDictionaryGetCount((CFDictionaryRef)values[i]); - const void **types = (const void**)alloca(sizeof(void*)*meta_cnt), **meta_values = (const void**)alloca(sizeof(void*)*meta_cnt); - CFDictionaryGetKeysAndValues((CFDictionaryRef)values[i], types, meta_values); - - int t; - for(j = 0; j < meta_cnt; ++j) - { - CFNumberGetValue((CFNumberRef)types[j], kCFNumberIntType, &t); - if (t == kSecTransformMetaAttributeValue) - { - if (meta_values[j]) { - // SendMetaAttribute doesn't activate the callbacks - SetAttribute(ah, meta_values[j]); - } - } - else - { - CFErrorRef result = SendMetaAttribute(ah, (SecTransformMetaAttributeType)t, meta_values[j]); - if (result) - { - CFRelease(result); // see Transform::RestoreState is ignoring error returns - } - } - } - - CFErrorRef result = SendMetaAttribute(ah, kSecTransformMetaAttributeExternalize, kCFBooleanTrue); - if (result) - { - CFRelease(result); // see Transform::RestoreState is ignoring error returns - } - } -} - -GroupTransform* Transform::GetRootGroup() -{ - GroupTransform *g = mGroup; - if (g) { - while (g->mGroup) { - g = g->mGroup; - } - } else { - if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) { - return (GroupTransform *)this; - } - } - return g; -} - -CFDictionaryRef Transform::GetCustomExternalData() -{ - return NULL; -} - -void Transform::SetCustomExternalData(CFDictionaryRef customData) -{ - return; -} - -CFDictionaryRef Transform::Externalize(CFErrorRef* error) -{ - if (mIsActive) - { - return (CFDictionaryRef)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform is executing, you need to externalize it prior to execution", GetName()); - } - - // make arrays to hold the transforms and the connections - __block CFMutableArrayRef transforms = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - __block CFMutableArrayRef connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - GroupTransform *root = GetRootGroup(); - - CFErrorRef err = ForAllNodes(false, true, ^(Transform *t) { - if (t != root) { - return t->ProcessExternalize(transforms, connections); - } - return (CFErrorRef)NULL; - }); - - if (NULL != err) - { - // Really? This just seems like a bad idea - if (NULL != error) - { - *error = err; - } - return NULL; - - } - - // make a dictionary to hold the output - CFMutableDictionaryRef output = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(output, EXTERN_TRANSFORM_TRANSFORM_ARRAY, transforms); - CFDictionaryAddValue(output, EXTERN_TRANSFORM_CONNECTION_ARRAY, connections); - - // clean up - CFRelease(connections); - CFRelease(transforms); - - return output; -} - -CFErrorRef Transform::ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections) -{ - if (!IsExternalizable()) { - return NULL; - } - - CFDictionaryRef state = CopyState(); - if (state && CFGetTypeID(state) == CFErrorGetTypeID()) { - return (CFErrorRef)state; - } - - // make a dictionary to hold the name, type, and state of this node - CFMutableDictionaryRef node = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(node, EXTERN_TRANSFORM_NAME, GetName()); - - CFTypeRef type = CFStringCreateCopy(NULL, mTypeName); - CFDictionaryAddValue(node, EXTERN_TRANSFORM_TYPE, type); - CFRelease(type); - - if (state != NULL) - { - CFDictionaryAddValue(node, EXTERN_TRANSFORM_STATE, state); - CFRelease(state); - } - - CFDictionaryRef customItems = GetCustomExternalData(); - if (NULL != customItems) - { - CFDictionaryAddValue(node, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY, customItems); - CFRelease(customItems); - } - - // append the resulting dictionary to the node list - CFArrayAppendValue(transforms, node); - CFRelease(node); - - // now walk the attribute list - CFIndex numAttributes = CFSetGetCount(mAttributes); - transform_attribute *attributes[numAttributes]; - TAGetAll(attributes); - - CFIndex i; - - // walk the forward links - for (i = 0; i < numAttributes; ++i) - { - CFIndex arraySize = attributes[i]->connections ? CFArrayGetCount(attributes[i]->connections) : 0; - if (arraySize != 0) - { - CFIndex j; - for (j = 0; j < arraySize; ++j) - { - transform_attribute *ta = ah2ta((SecTransformAttributeRef)CFArrayGetValueAtIndex(attributes[i]->connections, j)); - - if (!ta->transform->IsExternalizable()) { - // just pretend non-externalizable transforms don't even exist. Don't write out connections, and don't talk to them about externalizing. - continue; - } - - // add this forward connection to the array -- make a dictionary - CFMutableDictionaryRef connection = - CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_NAME, GetName()); - CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE, attributes[i]->name); - CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_NAME, ta->transform->GetName()); - CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE, ta->name); - - CFArrayAppendValue(connections, connection); - CFRelease(connection); - } - } - } - - return NULL; -}