1 #include <CoreServices/CoreServices.h>
3 #include <libkern/OSAtomic.h>
6 #include "StreamSource.h"
7 #include "SingleShotSource.h"
12 #include "SecTransformInternal.h"
13 #include "GroupTransform.h"
14 #include "GroupTransform.h"
17 static const int kMaxPendingTransactions
= 20;
19 static CFTypeID internalID
= _kCFRuntimeNotATypeID
;
21 // Use &dispatchQueueToTransformKey as a key to dispatch_get_specific to map from
22 // a transforms master, activation, or any attribute queue to the Transform*
23 static unsigned char dispatchQueueToTransformKey
;
25 static char RandomChar()
27 return arc4random() % 26 + 'A'; // good enough
31 static CFStringRef
ah_set_describe(const void *v
) CF_RETURNS_RETAINED
{
32 transform_attribute
*ta
= ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v
)));
33 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@=%@ (conn: %@)"), ta
->transform
->GetName(), ta
->name
, ta
->value
? ta
->value
: CFSTR("NULL"), ta
->connections
? static_cast<CFTypeRef
>(ta
->connections
) : static_cast<CFTypeRef
>(CFSTR("NONE")));
36 static CFStringRef
AttributeHandleFormat(CFTypeRef ah
, CFDictionaryRef dict
) CF_RETURNS_RETAINED
{
37 transform_attribute
*ta
= ah2ta(ah
);
38 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@"), ta
->transform
->GetName(), ta
->name
);
41 static CFStringRef
AttributeHandleDebugFormat(CFTypeRef ah
) CF_RETURNS_RETAINED
{
42 transform_attribute
*ta
= ah2ta(ah
);
43 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@ (%p)"), ta
->transform
->GetName(), ta
->name
, ta
);
46 static void AttributeHandleFinalize(CFTypeRef ah
)
48 transform_attribute
*ta
= ah2ta(ah
);
56 // When we release AH's we clear out the transform pointer, so if we get here with transform!=NULL somebody
57 // has released an AH we are still using and we will crash very very soon (in our code) if we don't abort here
58 syslog(LOG_ERR
, "over release of SecTransformAttributeRef at %p\n", ah
);
64 CFReleaseNull(ta
->value
);
67 // ta->q already released
71 CFReleaseNull(ta
->connections
);
76 dispatch_release(ta
->semaphore
);
79 if (ta
->attribute_changed_block
)
81 Block_release(ta
->attribute_changed_block
);
84 if (ta
->attribute_validate_block
)
86 Block_release(ta
->attribute_validate_block
);
94 static CFHashCode
ah_set_hash(const void *v
) {
95 return CFHash(ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v
)))->name
);
98 static Boolean
ah_set_equal(const void *v1
, const void *v2
) {
99 return CFEqual(ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v1
)))->name
, ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v2
)))->name
);
102 CFTypeID
transform_attribute::cftype
;
104 SecTransformAttributeRef
Transform::makeAH(transform_attribute
*ta
) {
106 SecTransformAttributeRef ah
= _CFRuntimeCreateInstance(NULL
, transform_attribute::cftype
, sizeof(struct transform_attribute
*), NULL
);
110 *(struct transform_attribute
**)(1 + (CFRuntimeBase
*)ah
) = ta
;
117 static pthread_key_t ah_search_key_slot
;
119 static void destroy_ah_search_key(void *ah
) {
121 pthread_setspecific(ah_search_key_slot
, NULL
);
126 SecTransformAttributeRef
Transform::getAH(SecTransformStringOrAttributeRef attrib
, bool create_ok
, bool create_underscore_ok
)
128 if (CFGetTypeID(attrib
) == transform_attribute::cftype
)
130 return (SecTransformAttributeRef
)attrib
;
133 CFStringRef label
= (CFStringRef
)attrib
;
134 static dispatch_once_t once
= 0;
135 const char *name
= (const char *)"SecTransformAttributeRef";
136 static CFRuntimeClass ahclass
;
137 static CFSetCallBacks tasetcb
;
139 dispatch_once(&once
, ^{
140 ahclass
.className
= name
;
141 ahclass
.copyFormattingDesc
= AttributeHandleFormat
;
142 ahclass
.copyDebugDesc
= AttributeHandleDebugFormat
;
143 ahclass
.finalize
= AttributeHandleFinalize
;
144 transform_attribute::cftype
= _CFRuntimeRegisterClass(&ahclass
);
145 if (transform_attribute::cftype
== _kCFRuntimeNotATypeID
) {
149 tasetcb
.equal
= ah_set_equal
;
150 tasetcb
.hash
= ah_set_hash
;
151 tasetcb
.copyDescription
= ah_set_describe
;
153 pthread_key_create(&ah_search_key_slot
, destroy_ah_search_key
);
156 SecTransformAttributeRef search_for
= pthread_getspecific(ah_search_key_slot
);
159 transform_attribute
* ta
= (transform_attribute
*)malloc(sizeof(transform_attribute
));
160 search_for
= makeAH(ta
);
167 bzero(ah2ta(search_for
), sizeof(transform_attribute
));
168 pthread_setspecific(ah_search_key_slot
, search_for
);
173 mAttributes
= CFSetCreateMutable(NULL
, 0, &tasetcb
);
176 ah2ta(search_for
)->name
= label
;
177 SecTransformAttributeRef ah
= static_cast<SecTransformAttributeRef
>(const_cast<void*>(CFSetGetValue(mAttributes
, search_for
)));
178 if (ah
== NULL
&& create_ok
)
180 if (CFStringGetLength(label
) && L
'_' == CFStringGetCharacterAtIndex(label
, 0) && !create_underscore_ok
)
182 // Attributes with a leading _ belong to the Transform system only, not to random 3rd party transforms.
186 transform_attribute
*ta
= static_cast<transform_attribute
*>(malloc(sizeof(transform_attribute
)));
194 ta
->name
= CFStringCreateCopy(NULL
, label
);
201 CFIndex cnt
= CFSetGetCount(mAttributes
);
202 CFSetAddValue(mAttributes
, ah
);
203 if (CFSetGetCount(mAttributes
) != cnt
+1)
205 CFReleaseNull(ta
->name
);
211 CFStringRef qname
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s/%@"), dispatch_queue_get_label(this->mDispatchQueue
), label
);
212 CFIndex used
, sz
= 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(qname
), kCFStringEncodingUTF8
);
213 UInt8
*qnbuf
= (UInt8
*)alloca(sz
);
214 CFStringGetBytes(qname
, CFRangeMake(0, CFStringGetLength(qname
)), kCFStringEncodingUTF8
, '?', FALSE
, qnbuf
, sz
, &used
);
216 ta
->q
= dispatch_queue_create((char*)qnbuf
, NULL
);
217 CFReleaseNull(qname
);
218 ta
->semaphore
= dispatch_semaphore_create(kMaxPendingTransactions
);
221 ta
->pushback_state
= transform_attribute::pb_empty
;
222 ta
->pushback_value
= NULL
;
224 ta
->connections
= NULL
;
225 ta
->transform
= this;
227 dispatch_set_target_queue(ta
->q
, mDispatchQueue
);
229 ta
->requires_outbound_connection
= 0;
232 ta
->ignore_while_externalizing
= 0;
233 ta
->has_incoming_connection
= 0;
234 ta
->direct_error_handling
= 0;
235 ta
->allow_external_sets
= 0;
236 ta
->has_been_deferred
= 0;
237 ta
->attribute_changed_block
= NULL
;
238 ta
->attribute_validate_block
= NULL
;
244 transform_attribute
*Transform::getTA(SecTransformStringOrAttributeRef attrib
, bool create_ok
)
246 SecTransformAttributeRef ah
= getAH(attrib
, create_ok
);
259 void Transform::TAGetAll(transform_attribute
**attributes
) {
260 CFSetGetValues(mAttributes
, (const void**)attributes
);
261 CFIndex i
, n
= CFSetGetCount(mAttributes
);
262 for(i
= 0; i
< n
; ++i
) {
263 attributes
[i
] = ah2ta(attributes
[i
]);
269 bool Transform::HasNoOutboundConnections()
271 // make an array big enough to hold all of the attributes
272 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
273 transform_attribute
**attributes
= (transform_attribute
**)malloc(numAttributes
*sizeof(transform_attribute
*));
275 if (attributes
== NULL
) {
276 // No more memory, we assume it's orphaned
280 TAGetAll(attributes
);
282 // check all of the attributes
284 for (i
= 0; i
< numAttributes
; ++i
)
286 if (attributes
[i
]->connections
&& CFArrayGetCount(attributes
[i
]->connections
) != 0)
300 bool Transform::HasNoInboundConnections()
302 // make an array big enough to hold all of the attributes
303 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
304 transform_attribute
**attributes
= (transform_attribute
**)malloc(numAttributes
*sizeof(transform_attribute
*));
306 if (attributes
== NULL
) {
307 // No more memory, we assume it's orphaned
311 TAGetAll(attributes
);
313 // check all of the attributes
315 for (i
= 0; i
< numAttributes
; ++i
)
317 if (attributes
[i
]->has_incoming_connection
)
331 CFIndex
Transform::GetAttributeCount()
333 return CFSetGetCount(mAttributes
);
336 Transform::Transform(CFStringRef transformType
, CFStringRef CFobjectType
) :
337 CoreFoundationObject(CFobjectType
),
339 mIsFinalizing(false),
340 mAlwaysSelfNotify(false),
343 mTypeName(CFStringCreateCopy(NULL
, transformType
))
347 mProcessingPushbacks
= FALSE
;
349 if (internalID
== _kCFRuntimeNotATypeID
) {
350 (void)SecTransformNoData();
351 internalID
= CoreFoundationObject::FindObjectType(gInternalCFObjectName
);
354 // create a name for the transform
357 for (i
= 0; i
< sizeof(rname
) - 1; ++i
)
359 rname
[i
] = RandomChar();
364 char *tname
= const_cast<char*>(CFStringGetCStringPtr(transformType
, kCFStringEncodingUTF8
));
366 CFIndex sz
= 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType
), kCFStringEncodingUTF8
);
367 tname
= static_cast<typeof(tname
)>(alloca(sz
));
369 CFStringGetCString(transformType
, tname
, sz
, kCFStringEncodingUTF8
);
371 tname
= const_cast<char*>("-");
376 asprintf(&name
, "%s-%s", rname
, tname
);
379 asprintf(&dqName
, "%s-%s", rname
, tname
);
382 asprintf(&aqName
, "aq-%s-%s", rname
, tname
);
384 mDispatchQueue
= dispatch_queue_create(dqName
, NULL
);
385 dispatch_queue_set_specific(mDispatchQueue
, &dispatchQueueToTransformKey
, this, NULL
);
386 // mActivationQueue's job in life is to be suspended until just after this transform is made active.
387 // It's primary use is when a value flowing across a connection isn't sure if the target transform is active yet.
388 mActivationQueue
= dispatch_queue_create(aqName
, NULL
);
389 dispatch_set_target_queue(mActivationQueue
, mDispatchQueue
);
390 dispatch_suspend(mActivationQueue
);
391 mActivationPending
= dispatch_group_create();
393 // set up points for ABORT, DEBUG, INPUT, and OUTPUT
394 AbortAH
= getAH(kSecTransformAbortAttributeName
, true);
395 transform_attribute
*ta
= ah2ta(AbortAH
);
396 ta
->ignore_while_externalizing
= 1;
397 CFStringRef attributeName
= CFStringCreateWithCStringNoCopy(NULL
, name
, 0, kCFAllocatorMalloc
);
398 SetAttributeNoCallback(kSecTransformTransformName
, attributeName
);
399 CFReleaseNull(attributeName
);
404 DebugAH
= getAH(kSecTransformDebugAttributeName
, true);
405 ah2ta(DebugAH
)->ignore_while_externalizing
= 1;
407 ta
= getTA(kSecTransformInputAttributeName
, true);
408 ta
->required
= ta
->deferred
= ta
->stream
= 1;
409 ta
->allow_external_sets
= 0;
411 ta
->has_been_deferred
= 0;
412 ta
= getTA(kSecTransformOutputAttributeName
, true);
413 ta
->requires_outbound_connection
= ta
->stream
= 1;
416 static void run_and_release_finalizer(void *finalizer_block
)
418 ((dispatch_block_t
)finalizer_block
)();
419 Block_release(finalizer_block
);
422 static void set_dispatch_finalizer(dispatch_object_t object
, dispatch_block_t finalizer
)
424 finalizer
= Block_copy(finalizer
);
425 dispatch_set_context(object
, finalizer
);
426 dispatch_set_finalizer_f(object
, run_and_release_finalizer
);
429 void Transform::FinalizePhase2()
434 void Transform::FinalizeForClang()
436 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
437 SecTransformAttributeRef
*handles
= (const void**)malloc(numAttributes
*sizeof(SecTransformAttributeRef
));
439 if (handles
== NULL
) {
440 syslog(LOG_ERR
, "Unable to allocate SecTransformAttributeRef handles in FinalizeForClang");
444 CFSetGetValues(mAttributes
, handles
);
446 for(CFIndex i
= 0; i
< numAttributes
; ++i
) {
447 SecTransformAttributeRef ah
= handles
[i
];
448 transform_attribute
*ta
= ah2ta(ah
);
450 set_dispatch_finalizer(ta
->q
, ^{
451 // NOTE: not done until all pending use of the attribute queue has ended AND retain count is zero
452 ta
->transform
= NULL
;
455 // If there is a pending pushback the attribute queue will be suspended, and needs a kick before it can be destructed.
456 if (__sync_bool_compare_and_swap(&ta
->pushback_state
, transform_attribute::pb_value
, transform_attribute::pb_discard
)) {
457 dispatch_resume(ta
->q
);
459 dispatch_release(ta
->q
);
462 // We might be finalizing a transform as it is being activated, make sure that is complete before we do the rest
463 dispatch_group_notify(mActivationPending
, mDispatchQueue
, ^{
464 if (mActivationQueue
!= NULL
) {
465 // 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
466 dispatch_resume(mActivationQueue
);
467 dispatch_release(mActivationQueue
);
470 set_dispatch_finalizer(mDispatchQueue
, ^{
471 // 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
474 dispatch_release(mDispatchQueue
);
480 void Transform::Finalize()
482 // 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
483 // (NOTE: moved block into member function as clang hits an internal error and declines to compile)
484 dispatch_block_t continue_finalization
= ^{ this->FinalizeForClang(); };
485 dispatch_block_t mark_as_finalizing
= ^{ this->mIsFinalizing
= true; };
487 // Mark the transform as "finalizing" so it knows not to propagate values across connections
488 if (this == dispatch_get_specific(&dispatchQueueToTransformKey
)) {
489 mark_as_finalizing();
491 dispatch_sync(mDispatchQueue
, mark_as_finalizing
);
495 (void)transforms_assume(mGroup
->mIsFinalizing
); // under retain?
496 mGroup
->AddAllChildrenFinalizedCallback(mDispatchQueue
, continue_finalization
);
497 mGroup
->ChildStartedFinalization(this);
499 // a "bare" transform (normally itself a group) still needs to be deconstructed
500 dispatch_async(mDispatchQueue
, continue_finalization
);
504 Transform::~Transform()
506 CFReleaseNull(mAttributes
);
508 CFReleaseNull(mAbortError
);
512 // See if we can catch anything using us after our death
513 mDispatchQueue
= (dispatch_queue_t
)0xdeadbeef;
515 CFReleaseNull(mTypeName
);
517 if (NULL
!= mPushedback
)
519 CFReleaseNull(mPushedback
);
521 dispatch_release(mActivationPending
);
524 CFStringRef
Transform::GetName() CF_RETURNS_NOT_RETAINED
{
525 return (CFStringRef
)GetAttribute(kSecTransformTransformName
);
528 CFTypeID
Transform::GetCFTypeID()
530 return CoreFoundationObject::FindObjectType(CFSTR("SecTransform"));
533 std::string
Transform::DebugDescription()
535 return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName());
538 CFErrorRef
Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key
, SecTransformMetaAttributeType type
, CFTypeRef value
)
540 SecTransformAttributeRef ah
= getAH(key
, true);
541 transform_attribute
*ta
= ah2ta(ah
);
544 case kSecTransformMetaAttributeRequired
:
545 ta
->required
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
548 case kSecTransformMetaAttributeRequiresOutboundConnection
:
549 ta
->requires_outbound_connection
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
552 case kSecTransformMetaAttributeDeferred
:
553 ta
->deferred
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
556 case kSecTransformMetaAttributeStream
:
557 ta
->stream
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
560 case kSecTransformMetaAttributeHasOutboundConnections
:
561 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah
);
563 case kSecTransformMetaAttributeHasInboundConnection
:
564 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah
);
566 case kSecTransformMetaAttributeCanCycle
:
567 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah
);
569 case kSecTransformMetaAttributeExternalize
:
570 ta
->ignore_while_externalizing
= CFBooleanGetValue((CFBooleanRef
)value
) ? 0 : 1;
573 case kSecTransformMetaAttributeValue
:
574 return SetAttributeNoCallback(ah
, value
);
576 case kSecTransformMetaAttributeRef
:
577 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah
);
579 case kSecTransformMetaAttributeName
:
580 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah
);
583 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set unknown meta attribute #%d to %@ on %@", type
, value
, key
);
589 CFTypeRef
Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key
, SecTransformMetaAttributeType type
) {
590 SecTransformAttributeRef ah
= getAH(key
, true);
591 transform_attribute
*ta
= ah2ta(ah
);
593 case kSecTransformMetaAttributeRequired
:
594 return (CFTypeRef
)(ta
->required
? kCFBooleanTrue
: kCFBooleanFalse
);
595 case kSecTransformMetaAttributeRequiresOutboundConnection
:
596 return (CFTypeRef
)(ta
->requires_outbound_connection
? kCFBooleanTrue
: kCFBooleanFalse
);
597 case kSecTransformMetaAttributeDeferred
:
598 return (CFTypeRef
)(ta
->deferred
? kCFBooleanTrue
: kCFBooleanFalse
);
599 case kSecTransformMetaAttributeStream
:
600 return (CFTypeRef
)(ta
->stream
? kCFBooleanTrue
: kCFBooleanFalse
);
601 case kSecTransformMetaAttributeHasOutboundConnections
:
602 return (CFTypeRef
)((ta
->connections
&& CFArrayGetCount(ta
->connections
)) ? kCFBooleanTrue
: kCFBooleanFalse
);
603 case kSecTransformMetaAttributeHasInboundConnection
:
604 return (CFTypeRef
)(ta
->has_incoming_connection
? kCFBooleanTrue
: kCFBooleanFalse
);
605 case kSecTransformMetaAttributeCanCycle
:
606 return (CFTypeRef
)kCFBooleanFalse
;
607 case kSecTransformMetaAttributeExternalize
:
608 return (CFTypeRef
)(ta
->ignore_while_externalizing
? kCFBooleanFalse
: kCFBooleanTrue
);
609 case kSecTransformMetaAttributeRef
:
611 case kSecTransformMetaAttributeValue
:
613 case kSecTransformMetaAttributeName
:
616 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't get unknown meta attribute #%d from %@", type
, key
);
624 CFErrorRef
Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError
)
626 // pull apart the error
627 CFIndex code
= CFErrorGetCode(sourceError
);
628 CFStringRef domain
= CFErrorGetDomain(sourceError
);
629 CFDictionaryRef oldUserInfo
= CFErrorCopyUserInfo(sourceError
);
630 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutableCopy(NULL
, 0, oldUserInfo
);
631 CFReleaseNull(oldUserInfo
);
633 // add the new key and value to the dictionary
634 CFDictionaryAddValue(userInfo
, kSecTransformAbortOriginatorKey
, GetCFObject());
636 // make a new CFError
637 CFErrorRef newError
= CFErrorCreate(NULL
, domain
, code
, userInfo
);
638 CFReleaseNull(userInfo
);
642 // NOTE: If called prior to execution will schedule a later call to AbortAllTransforms
643 void Transform::AbortJustThisTransform(CFErrorRef abortErr
)
645 (void)transforms_assume(abortErr
);
646 (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey
) == this);
648 Boolean wasActive
= mIsActive
;
650 if (OSAtomicCompareAndSwapPtr(NULL
, (void *)abortErr
, (void**)&mAbortError
)) {
651 // send an abort message to the attribute so that it can shut down
652 // note that this bypasses the normal processes. The message sent is a notification
653 // that things aren't working well any more, the transform cannot make any other assumption.
655 // mAbortError is released in the destructor which is triggered (in part)
656 // by the dispatch queue finalizer so we don't need a retain/release of
657 // abortErr for the abortAction block, but we do need to retain it
658 // here to match with the release by the destructor.
659 CFRetainSafe(abortErr
);
661 dispatch_block_t abortAction
= ^{
662 // This actually makes the abort happen, it needs to run on the transform's queue while the
663 // transform is executing.
666 // When this abort was first processed we were not executing, so
667 // additional transforms may have been added to our group (indeed,
668 // we may not have had a group at all), so we need to let everyone
669 // know about the problem. This will end up letting ourself (and
670 // maybe some others) know an additional time, but the CompareAndSwap
671 // prevents that from being an issue.
672 this->AbortAllTransforms(abortErr
);
675 SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle
= getAH(kSecTransformInputAttributeName
, false);
676 // Calling AttributeChanged directly lets an error "skip ahead" of the input queue,
677 // and even execute if the input queue is suspended pending pushback retries.
678 AttributeChanged(inputAttributeHandle
, abortErr
);
683 // This transform is running, so we use the normal queue (which we are
684 // already executing on)
687 // This transform hasn't run yet, do the work on the activation queue
688 // so it happens as soon as the transforms starts executing.
689 dispatch_async(mActivationQueue
, abortAction
);
692 Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH
, abortErr
, mAbortError
);
696 // abort all transforms in the root group & below
697 void Transform::AbortAllTransforms(CFTypeRef err
)
699 Debug("%@ set to %@, aborting\n", AbortAH
, err
);
700 CFErrorRef error
= NULL
;
702 CFTypeRef replacementErr
= NULL
;
704 if (CFGetTypeID(err
) != CFErrorGetTypeID())
706 CFStringRef thisErrorTypeDescription
= CFCopyTypeIDDescription(CFGetTypeID(err
));
707 CFStringRef regularErrorTypeDescription
= CFCopyTypeIDDescription(CFErrorGetTypeID());
708 replacementErr
= err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidType
, "ABORT set to a %@ (%@) not a %@", thisErrorTypeDescription
, err
, regularErrorTypeDescription
);
709 CFReleaseNull(thisErrorTypeDescription
);
710 CFReleaseNull(regularErrorTypeDescription
);
713 error
= RefactorErrorToIncludeAbortingTransform((CFErrorRef
)err
);
717 CFReleaseNull(replacementErr
);
720 GroupTransform
*root
= GetRootGroup();
723 // tell everyone in the (root) group to "AbortJustThisTransform"
724 dispatch_group_t all_aborted
= dispatch_group_create();
725 root
->ForAllNodesAsync(false, all_aborted
, ^(Transform
* t
){
726 t
->AbortJustThisTransform(error
);
728 dispatch_group_notify(all_aborted
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH
, 0), ^(void) {
729 CFReleaseSafe(error
);
730 dispatch_release(all_aborted
);
735 // We are everyone so we AbortJustThisTransform "directly"
736 // NOTE: this can only happen prior to execution (execution always happens in a group)
737 (void)transforms_assume_zero(mIsActive
);
738 this->AbortJustThisTransform(error
);
744 CFErrorRef
Transform::Disconnect(Transform
* destinationTransform
, CFStringRef myKey
, CFStringRef hisKey
)
746 //CFTypeRef thisTransform = (SecTransformRef) GetCFObject();
748 // find this transform in the backlinks for the destination
751 // now remove the link in the transform dictionary
752 transform_attribute
*src
= getTA(myKey
, true);
753 SecTransformAttributeRef dst
= destinationTransform
->getAH(hisKey
);
755 if (src
->connections
== NULL
)
757 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Cannot find transform in destination.");
760 CFIndex numConnections
= CFArrayGetCount(src
->connections
);
761 for (i
= 0; i
< numConnections
; ++i
)
763 if (CFArrayGetValueAtIndex(src
->connections
, i
) == dst
)
765 CFArrayRemoveValueAtIndex(src
->connections
, i
);
766 numConnections
= CFArrayGetCount(src
->connections
);
769 // clear the has_incoming_connection bit in the destination. We can do this because inputs can have only one connection.
770 transform_attribute
* dstTA
= ah2ta(dst
);
771 dstTA
->has_incoming_connection
= false;
774 if (HasNoInboundConnections() && HasNoOutboundConnections())
776 // we have been orphaned, just remove us
777 mGroup
->RemoveMemberFromGroup(GetCFObject());
786 CFErrorRef
Transform::Connect(GroupTransform
*group
, Transform
* destinationTransform
, CFStringRef destAttr
, CFStringRef srcAttr
)
790 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make connections without a specific group (do not call with group = NULL)");
794 GroupTransform
*newSourceGroup
= mGroup
;
795 GroupTransform
*newDestinationGroup
= destinationTransform
->mGroup
;
797 if (mGroup
== NULL
|| mGroup
== this)
799 newSourceGroup
= group
;
802 if (destinationTransform
->mGroup
== NULL
|| destinationTransform
->mGroup
== destinationTransform
)
804 newDestinationGroup
= group
;
807 if (newSourceGroup
!= newDestinationGroup
&& mGroup
)
809 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup
->GetName(), destinationTransform
->GetName(), newDestinationGroup
->GetName());
813 if (!validConnectionPoint(srcAttr
)) {
814 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make a connection from non-exported attribute %@ of %@", srcAttr
, this->GetName());
817 if (!destinationTransform
->validConnectionPoint(destAttr
)) {
818 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make a connection to non-exported attribute %@ of %@", destAttr
, destinationTransform
->GetName());
822 mGroup
= newSourceGroup
;
823 destinationTransform
->mGroup
= newDestinationGroup
;
825 // NOTE: this fails on OOM
826 group
->AddMemberToGroup(this->GetCFObject());
827 group
->AddMemberToGroup(destinationTransform
->GetCFObject());
829 transform_attribute
*src
= this->getTA(srcAttr
, true);
830 SecTransformAttributeRef dst
= destinationTransform
->getAH(destAttr
);
832 if (!src
->connections
)
834 src
->connections
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
836 CFArrayAppendValue(src
->connections
, dst
);
838 ah2ta(dst
)->has_incoming_connection
= 1;
844 bool Transform::validConnectionPoint(CFStringRef attributeName
)
849 // NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes
850 // SetAttribute eventually calls SetAttributeNoCallback
851 CFErrorRef
Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key
, CFTypeRef value
)
853 SecTransformAttributeRef ah
= getAH(key
, true);
858 transform_attribute
*ta
= ah2ta(ah
);
860 if (ah
== AbortAH
&& value
&& (mIsActive
|| !ta
->deferred
))
862 AbortAllTransforms(value
);
863 return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress
, "Abort started");
866 bool do_propagate
= true;
868 if (!ta
->has_been_deferred
)
870 bool doNotRetain
= false;
874 CFStringRef name
= ta
->name
;
875 if (CFGetTypeID(value
) == CFReadStreamGetTypeID())
877 CFTypeRef src
= StreamSource::Make((CFReadStreamRef
) value
, this, name
);
879 do_propagate
= false;
880 ta
->has_been_deferred
= 1;
883 else if (ta
->deferred
&& !mIsActive
)
887 Debug("%@ deferred value=%p\n", ah
, value
);
890 CFTypeRef src
= SingleShotSource::Make(value
, this, name
);
891 ta
->has_been_deferred
= 1;
893 // the old value will be release when Transform::Do terminates
896 do_propagate
= false;
901 ta
->has_been_deferred
= 0;
905 if (ta
->value
!= value
) {
906 if (value
&& !doNotRetain
) {
910 CFReleaseNull(ta
->value
);
917 // propagate the changes out to all connections
918 if (ta
->connections
&& mIsActive
&& do_propagate
&& !(mAbortError
|| mIsFinalizing
))
920 Debug("Propagating from %@ to %@\n", ah
, ta
->connections
);
921 CFIndex i
, numConnections
= CFArrayGetCount(ta
->connections
);
922 for(i
= 0; i
< numConnections
; ++i
) {
923 SecTransformAttributeRef ah
= static_cast<SecTransformAttributeRef
>(const_cast<void *>(CFArrayGetValueAtIndex(ta
->connections
, i
)));
924 Transform
*tt
= ah2ta(ah
)->transform
;
929 tt
->SetAttribute(ah
, value
);
933 dispatch_block_t setAttribute
= ^{
934 tt
->SetAttribute(ah
, value
);
936 // Here the target queue might not be activated yet, we can't
937 // look directly at the other transform's ActivationQueue as
938 // it might activate (or Finalize!) as we look, so just ask
939 // the other transform to deal with it.
940 dispatch_async(ah2ta(ah
)->q
, ^(void) {
941 // This time we are on the right queue to know this is the real deal
945 dispatch_async(ah2ta(ah
)->transform
->mActivationQueue
, setAttribute
);
956 // external sets normally fail if the transform is running
957 CFErrorRef
Transform::ExternalSetAttribute(CFTypeRef key
, CFTypeRef value
)
961 return this->SetAttribute(key
, value
);
965 SecTransformAttributeRef ah
= getAH(key
, false);
966 if (ah
!= NULL
&& ah2ta(ah
)->allow_external_sets
)
968 return this->SetAttribute(static_cast<CFTypeRef
>(ah
), value
);
972 return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "%@ can not be set while %@ is executing", ah
, this->GetName());
978 // queue up the setting of the key and value
979 CFErrorRef
Transform::SetAttribute(CFTypeRef key
, CFTypeRef value
)
983 CFErrorRef result
= CreateSecTransformErrorRef(kSecTransformErrorAborted
, "ABORT has been sent to the transform (%@)", mAbortError
);
984 CFAutorelease(result
);
988 // queue up the setting of the key and value
989 SecTransformAttributeRef ah
;
990 if (CFGetTypeID(key
) == transform_attribute::cftype
)
994 else if (CFGetTypeID(key
) == CFStringGetTypeID())
996 ah
= getAH(static_cast<CFStringRef
>(key
));
999 CFErrorRef result
= CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute
, "Can't set attribute %@ in transform %@", key
, GetName());
1000 CFAutorelease(result
);
1006 CFErrorRef result
= CreateSecTransformErrorRef(kSecTransformErrorInvalidType
, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key
);
1007 CFAutorelease(result
);
1011 // Do this after the error check above so we don't leak
1012 CFRetainSafe(value
); // if we use dispatch_async we need to own the value (the matching release is in the set block)
1014 transform_attribute
*ta
= ah2ta(ah
);
1016 dispatch_block_t set
= ^{
1018 dispatch_semaphore_signal(ta
->semaphore
);
1019 CFReleaseSafe(value
);
1023 // when the transform is active, set attributes asynchronously. Otherwise, we are doing
1024 // initialization and must wait for the operation to complete.
1027 dispatch_async(ta
->q
, set
);
1031 dispatch_sync(ta
->q
, set
);
1033 if (dispatch_semaphore_wait(ta
->semaphore
, dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
))) {
1034 Debug("Send from %@ to %@ is still waiting\n", GetName(), ah
);
1035 dispatch_semaphore_wait(ta
->semaphore
, DISPATCH_TIME_FOREVER
);
1038 // Return the best available status (which will be NULL if we haven't aborted, or stated an
1039 // intent to abort when execution starts)
1041 // The value of the ABORT attribute can differ from mAbortError, first if a transform is aborted
1042 // prior to running the general abort mechanic is deferred until execution. Second during
1043 // execution the abort logic avoids most of the normal processing. Third, and most importantly
1044 // during an abort the exact error that gets generated will differ from the value sent to ABORT
1045 // (for example if a non-CFError was sent...plus even if it was a CFError we annotate that error).
1050 CFErrorRef
Transform::SendAttribute(SecTransformStringOrAttributeRef key
, CFTypeRef value
)
1052 return SetAttributeNoCallback(key
, value
);
1057 CFTypeRef
Transform::GetAttribute(SecTransformStringOrAttributeRef key
)
1059 struct transform_attribute
*ta
= getTA(key
, false);
1060 if (ta
== NULL
|| ta
->value
== NULL
) {
1064 if (CFGetTypeID(ta
->value
) == internalID
)
1066 // this is one of our internal objects, so get the value from it
1067 Source
* source
= (Source
*) CoreFoundationHolder::ObjectFromCFType(ta
->value
);
1068 return source
->GetValue();
1076 CFErrorRef
Transform::Pushback(SecTransformAttributeRef ah
, CFTypeRef value
)
1078 CFErrorRef result
= NULL
;
1079 transform_attribute
*ta
= ah2ta(ah
);
1080 if (!(ta
->pushback_state
== transform_attribute::pb_empty
|| ta
->pushback_state
== transform_attribute::pb_repush
))
1082 CFErrorRef error
= fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidOperation
, CFSTR("Can not pushback new value until old value has been processed"));
1083 SetAttribute(kSecTransformAbortAttributeName
, error
);
1086 if (value
== NULL
&& ta
->pushback_value
== NULL
&& ta
->pushback_state
== transform_attribute::pb_repush
)
1088 ta
->pushback_state
= transform_attribute::pb_presented_once
;
1091 ta
->pushback_state
= transform_attribute::pb_value
;
1095 CFRetainSafe(value
);
1097 ta
->pushback_value
= value
;
1098 dispatch_suspend(ta
->q
);
1101 mPushedback
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1103 CFArrayAppendValue(mPushedback
, ah
);
1107 void Transform::try_pushbacks() {
1108 if (!mPushedback
|| !CFArrayGetCount(mPushedback
)) {
1109 mProcessingPushbacks
= FALSE
;
1113 CFArrayRef pb
= (CFArrayRef
)mPushedback
;
1114 mPushedback
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1115 CFIndex i
, n
= CFArrayGetCount(pb
);
1117 for(i
= 0; i
< n
; ++i
)
1119 SecTransformAttributeRef ah
= CFArrayGetValueAtIndex(pb
, i
);
1120 transform_attribute
*ta
= ah2ta(ah
);
1121 ta
->pushback_state
= transform_attribute::pb_repush
;
1122 CFTypeRef v
= ta
->pushback_value
;
1123 ta
->pushback_value
= NULL
;
1129 if (ta
->pushback_state
== transform_attribute::pb_repush
) {
1130 ta
->pushback_state
= transform_attribute::pb_empty
;
1133 // NOTE: a successful repush needs the queue unsuspended so it can run.
1134 // A failed repush has suspended the queue an additional time, so we
1135 // still need to resume it.
1136 dispatch_resume(ta
->q
);
1141 if (succeeded
&& CFArrayGetCount(mPushedback
)) {
1142 // some attribute changed while we proceeded the last batch of pushbacks, so any "new" pushbacks are eligible to run again.
1143 // 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.
1144 dispatch_async(mDispatchQueue
, ^{ try_pushbacks(); });
1146 mProcessingPushbacks
= FALSE
;
1150 void Transform::Debug(const char *cfmt
, ...) {
1151 CFTypeRef d
= ah2ta(DebugAH
)->value
;
1153 CFWriteStreamRef out
= NULL
;
1154 if (CFGetTypeID(d
) == CFWriteStreamGetTypeID()) {
1155 out
= (CFWriteStreamRef
)d
;
1157 static dispatch_once_t once
;
1158 static CFWriteStreamRef StdErrWriteStream
;
1159 dispatch_once(&once
, ^{
1160 CFURLRef p
= CFURLCreateWithFileSystemPath(NULL
, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle
, FALSE
);
1161 StdErrWriteStream
= CFWriteStreamCreateWithFile(NULL
, p
);
1162 CFWriteStreamOpen(StdErrWriteStream
);
1165 out
= StdErrWriteStream
;
1171 CFStringRef fmt
= CFStringCreateWithCString(NULL
, cfmt
, kCFStringEncodingUTF8
);
1172 CFStringRef str
= CFStringCreateWithFormatAndArguments(NULL
, NULL
, fmt
, ap
);
1177 CFIndex sz
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(str
), kCFStringEncodingUTF8
);
1181 bool needs_free
= true;
1182 buf
= (unsigned char*)malloc(sz
);
1184 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, '?', FALSE
, buf
, sz
, &used
);
1186 buf
= (unsigned char *)"malloc failure during Transform::Debug\n";
1190 static dispatch_once_t once
;
1191 static dispatch_queue_t print_q
;
1192 dispatch_once(&once
, ^{
1193 print_q
= dispatch_queue_create("com.apple.security.debug.print_queue", 0);
1194 dispatch_set_target_queue((dispatch_object_t
)print_q
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW
, 0));
1197 dispatch_async(print_q
, ^{
1198 CFWriteStreamWrite(out
, buf
, used
);
1208 void Transform::Do(SecTransformAttributeRef ah
, CFTypeRef value
)
1210 transform_attribute
*ta
= ah2ta(ah
);
1211 if (ta
->pushback_state
== transform_attribute::pb_discard
)
1215 (void)transforms_assume(dispatch_get_current_queue() == ((ta
->pushback_state
== transform_attribute::pb_repush
) ? mDispatchQueue
: ta
->q
));
1219 Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value
, ah
, dispatch_queue_get_label(dispatch_get_current_queue()));
1223 SetAttributeNoCallback(ah
, value
);
1224 // While an abort is in progress things can get into bad
1225 // states if we allow normal processing so we throw anything
1226 // on the floor except CFErrorRef or NULL vales sent to
1227 // ABORT or INPUT (we need to process them to let the
1228 // transform shut down correctly)
1229 if (mAbortError
&& (!(ah
== this->AbortAH
|| ah
== getTA(CFSTR("INPUT"), true)) && (value
== NULL
|| CFGetTypeID(value
) != CFErrorGetTypeID())))
1232 Debug("Ignoring value (%@) sent to %@ during abort\n", value
, ah
);
1234 Debug("Ignoring NULL sent to %@ during abort\n", ah
);
1239 if (mIsActive
|| (mAlwaysSelfNotify
&& !ta
->deferred
))
1241 Debug("AttributeChanged: %@ (%s) = %@\n", ah
, mIsActive
? "is executing" : "self notify set", value
? value
: (CFTypeRef
)CFSTR("(NULL)"));
1242 AttributeChanged(ah
, value
);
1245 if (mPushedback
&& CFArrayGetCount(mPushedback
) && !mProcessingPushbacks
)
1247 Debug("will process pushbacks (%@) later\n", mPushedback
);
1248 mProcessingPushbacks
= TRUE
;
1249 dispatch_async(mDispatchQueue
, ^{ try_pushbacks(); });
1256 void Transform::AttributeChanged(CFStringRef name
, CFTypeRef value
)
1260 void Transform::AttributeChanged(SecTransformAttributeRef ah
, CFTypeRef value
)
1262 AttributeChanged(ah2ta(ah
)->name
, value
);
1265 CFArrayRef
Transform::GetAllAH() {
1266 CFIndex cnt
= CFSetGetCount(mAttributes
);
1267 const void **values
= (const void **)alloca(sizeof(void*)*cnt
);
1268 CFSetGetValues(mAttributes
, values
);
1269 return CFArrayCreate(NULL
, values
, cnt
, &kCFTypeArrayCallBacks
);
1272 CFTypeRef
Transform::Execute(dispatch_queue_t deliveryQueue
, SecMessageBlock deliveryBlock
, CFErrorRef
* errorRef
)
1276 CFTypeRef g
= GroupTransform::Make();
1277 mGroup
= (GroupTransform
*)CoreFoundationHolder::ObjectFromCFType(g
);
1278 mGroup
->AddMemberToGroup(this->GetCFObject());
1279 SecMessageBlock smb
= ^(CFTypeRef message
, CFErrorRef error
, Boolean isFinal
)
1281 deliveryBlock(message
, error
, isFinal
);
1284 dispatch_async(this->mDispatchQueue
, ^{
1290 CFTypeRef ret
= this->Execute(deliveryQueue
, deliveryBlock
? smb
: (SecMessageBlock
) NULL
, errorRef
);
1304 *errorRef
= CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform has already executed, it may not be executed again.", GetName());
1310 // Do a retain on our parent since we are using it
1311 GroupTransform
*rootGroup
= GetRootGroup();
1312 CFRetainSafe(rootGroup
->GetCFObject());
1314 CFTypeRef result
= NULL
;
1316 CFTypeRef monitorRef
= BlockMonitor::Make(deliveryQueue
, deliveryBlock
);
1318 __block CFStringRef outputAttached
= NULL
;
1320 dispatch_queue_t p2
= dispatch_queue_create("activate phase2", NULL
);
1321 dispatch_queue_t p3
= dispatch_queue_create("activate phase3", NULL
);
1322 dispatch_suspend(p2
);
1323 dispatch_suspend(p3
);
1324 // walk the transform, doing phase1 activating as we go, and queueing phase2 and phase3 work
1325 CFErrorRef temp
= TraverseTransform(NULL
, ^(Transform
*t
){
1326 return t
->ExecuteOperation(outputAttached
, (SecMonitorRef
)monitorRef
, p2
, p3
);
1328 // ExecuteOperation is not called for the outer group, so we need to manually set mISActive for it.
1329 rootGroup
->mIsActive
= true;
1330 rootGroup
->StartingExecutionInGroup();
1331 dispatch_resume(p2
);
1332 dispatch_sync(p2
, ^{ dispatch_resume(p3
); });
1333 dispatch_sync(p3
, ^{ dispatch_release(p2
); });
1334 dispatch_release(p3
);
1341 // It is safe to keep the monitors attached, because it is invalid to try to execute again, BUT
1342 // we do need to release the reference to the group that the monitor would normally release
1343 // when it processes the final message.
1344 CFReleaseSafe(rootGroup
->GetCFObject());
1345 CFReleaseNull(monitorRef
);
1346 rootGroup
->StartedExecutionInGroup(false);
1350 dispatch_group_t initialized
= dispatch_group_create();
1351 rootGroup
->ForAllNodesAsync(true, initialized
, ^(Transform
*t
) {
1355 dispatch_group_notify(initialized
, rootGroup
->mDispatchQueue
, ^{
1356 dispatch_release(initialized
);
1357 dispatch_group_t activated
= dispatch_group_create();
1358 dispatch_group_enter(activated
);
1359 dispatch_async(rootGroup
->mDispatchQueue
, ^{
1360 rootGroup
->ForAllNodesAsync(true, activated
, ^(Transform
*t
) {
1361 t
->ActivateInputs();
1363 dispatch_group_leave(activated
);
1365 dispatch_group_notify(activated
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
1366 dispatch_release(activated
);
1367 // once we have been activated (but not before!), the monitor belongs to the group, and we can drop our claim
1368 CFReleaseSafe(monitorRef
);
1369 rootGroup
->StartedExecutionInGroup(true);
1377 void Transform::Initialize()
1381 static void ActivateInputs_set(const void *v
, void *unused
) {
1382 transform_attribute
*ta
= static_cast<transform_attribute
*>(ah2ta(const_cast<void *>(v
)));
1383 if (ta
->value
&& internalID
== CFGetTypeID(ta
->value
)) {
1384 Source
* s
= (Source
*) CoreFoundationHolder::ObjectFromCFType(ta
->value
);
1389 void Transform::ActivateInputs()
1391 (void)transforms_assume_zero(mIsActive
&& this != dispatch_get_specific(&dispatchQueueToTransformKey
));
1393 // now run all of the forward links
1394 if (!mIsFinalizing
) {
1395 CFSetApplyFunction(mAttributes
, ActivateInputs_set
, NULL
);
1399 CFErrorRef
Transform::ForAllNodes(bool parallel
, bool includeOwningGroup
, Transform::TransformOperation op
)
1401 GroupTransform
*g
= GetRootGroup();
1403 return g
->ForAllNodes(parallel
, includeOwningGroup
, op
);
1409 CFErrorRef
Transform::TraverseTransform(CFMutableSetRef visited
, TransformOperation t
)
1411 return ForAllNodes(true, true, t
);
1414 CFErrorRef
Transform::ExecuteOperation(CFStringRef
&outputAttached
, SecMonitorRef output
, dispatch_queue_t phase2
, dispatch_queue_t phase3
)
1417 // top level groups are special, and don't go through this path.
1421 if (!TransformCanExecute())
1423 // oops, this transform isn't ready to go
1424 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly
, "The transform %@ was not ready for execution.", GetName());
1427 // check to see if required attributes are connected or set
1428 CFIndex i
, numAttributes
= CFSetGetCount(mAttributes
);
1429 transform_attribute
**attributes
= (transform_attribute
**)alloca(numAttributes
* sizeof(transform_attribute
*));
1430 TAGetAll(attributes
);
1431 CFMutableArrayRef still_need
= NULL
;
1432 for(i
= 0; i
< numAttributes
; ++i
) {
1433 transform_attribute
*ta
= attributes
[i
];
1434 if (ta
->required
&& ta
->value
== NULL
&& !ta
->has_incoming_connection
) {
1436 still_need
= CFArrayCreateMutable(NULL
, i
, &kCFTypeArrayCallBacks
);
1438 CFArrayAppendValue(still_need
, ta
->name
);
1442 CFStringRef elist
= CFStringCreateByCombiningStrings(NULL
, still_need
, CFSTR(", "));
1443 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorMissingParameter
, "Can not execute %@, missing required attributes: %@", GetName(), elist
);
1444 CFReleaseNull(elist
);
1445 CFReleaseNull(still_need
);
1449 // see if we can attach our output here (note mAttributes may have changed)
1450 numAttributes
= CFSetGetCount(mAttributes
);
1451 attributes
= (transform_attribute
**)alloca(numAttributes
* sizeof(transform_attribute
*));
1452 TAGetAll(attributes
);
1453 for (i
= 0; i
< numAttributes
; ++i
)
1455 transform_attribute
*ta
= attributes
[i
];
1456 CFIndex arraySize
= ta
->connections
? CFArrayGetCount(ta
->connections
) : 0;
1457 if (arraySize
== 0 && ta
->requires_outbound_connection
)
1459 if (CFStringCompare(ta
->name
, kSecTransformOutputAttributeName
, 0) == kCFCompareEqualTo
) {
1460 // this is a place where we can hook up our output -- maybe
1463 // oops, we've already done that.
1464 return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput
, "Both %@ and %@ have loose outputs, attach one to something", outputAttached
, ta
->transform
->GetName());
1466 // Delay the connect until after ForAllNodes returns
1467 dispatch_async(phase2
, ^{
1468 SecTransformConnectTransformsInternal(mGroup
->GetCFObject(),
1469 GetCFObject(), kSecTransformOutputAttributeName
,
1470 output
, kSecTransformInputAttributeName
);
1472 outputAttached
= ta
->transform
->GetName();
1474 // activate the attached monitor
1475 Monitor
* m
= (Monitor
*) CoreFoundationHolder::ObjectFromCFType(output
);
1476 m
->mIsActive
= true;
1478 // add the monitor to the output so that it doesn't get activated twice
1480 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly
, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta
->name
, GetName());
1487 // Delay activation until after the Monitor is connected
1488 dispatch_async(phase3
, ^{
1497 void Transform::DoPhase3Activation()
1499 this->mIsActive
= true;
1500 // execution has now truly started ("mIsActive is true")
1501 CFErrorRef initError
= TransformStartingExecution();
1504 // Oops, now execution is about to grind to a halt
1505 this->SendAttribute(AbortAH
, initError
);
1508 dispatch_resume(this->mActivationQueue
);
1509 dispatch_group_async(this->mActivationPending
, this->mActivationQueue
, ^{
1510 dispatch_release(this->mActivationQueue
);
1511 this->mActivationQueue
= NULL
;
1517 // This would be best expressed as a block, but we seem to run into compiler errors
1518 void Transform::phase3Activation()
1520 dispatch_async(this->mDispatchQueue
, ^
1522 DoPhase3Activation();
1527 Boolean
Transform::TransformCanExecute()
1534 CFErrorRef
Transform::TransformStartingExecution()
1541 bool Transform::IsExternalizable()
1546 static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator
, const void *value
) {
1547 return CFRetainSafe(value
);
1550 static void CFTypeOrNULLRelease(CFAllocatorRef allocator
, const void *value
) {
1551 CFReleaseNull(value
);
1554 static CFStringRef
CFTypeOrNULLCopyDescription (const void *value
) {
1555 if (value
!= NULL
) {
1556 return CFCopyDescription(value
);
1558 return CFSTR("NULL");
1562 static Boolean
CFTypeOrNULLEqual(const void *value1
, const void *value2
) {
1563 if (value1
== NULL
&& value2
== NULL
) {
1566 if (value1
== NULL
|| value2
== NULL
) {
1569 return CFEqual(value1
, value2
);
1574 // Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState
1575 CFDictionaryRef
Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key
)
1577 SecTransformMetaAttributeType types
[] =
1579 kSecTransformMetaAttributeRequired
,
1580 kSecTransformMetaAttributeRequiresOutboundConnection
,
1581 kSecTransformMetaAttributeDeferred
,
1582 kSecTransformMetaAttributeStream
,
1583 kSecTransformMetaAttributeCanCycle
,
1584 kSecTransformMetaAttributeValue
1587 CFIndex i
, cnt
= sizeof(types
)/sizeof(SecTransformMetaAttributeType
);
1588 CFTypeRef values
[cnt
];
1589 CFNumberRef keys
[cnt
];
1592 // NOTE: we save meta attributes that are in their "default" state on purpose because the
1593 // default may change in the future and we definitely want to restore the default values at
1594 // time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8
1595 // default we want to load all old transforms with stream=1, the simplest way to do that is
1596 // to store all values, not just non-default values)
1597 for(i
= 0; i
< cnt
; ++i
)
1599 values
[i
] = GetMetaAttribute(key
, types
[i
]);
1600 int tmp
= (int)types
[i
];
1601 keys
[i
] = CFNumberCreate(NULL
, kCFNumberIntType
, &tmp
);
1604 static CFDictionaryValueCallBacks CFTypeOrNULL
;
1605 static dispatch_once_t once
;
1606 dispatch_block_t b
=
1608 CFTypeOrNULL
.version
= 0;
1609 CFTypeOrNULL
.retain
= CFTypeOrNULLRetain
;
1610 CFTypeOrNULL
.release
= CFTypeOrNULLRelease
;
1611 CFTypeOrNULL
.copyDescription
= CFTypeOrNULLCopyDescription
;
1612 CFTypeOrNULL
.equal
= CFTypeOrNULLEqual
;
1614 dispatch_once(&once
, b
);
1616 CFDictionaryRef ret
= CFDictionaryCreate(NULL
, (const void**)&keys
, (const void**)&values
, cnt
, &kCFTypeDictionaryKeyCallBacks
, &CFTypeOrNULL
);
1618 for(i
= 0; i
< cnt
; ++i
)
1620 CFReleaseNull(keys
[i
]);
1626 // return everything that doesn't have ignore_while_externalizing set
1627 CFDictionaryRef
Transform::CopyState()
1629 CFIndex i
, j
, cnt
= CFSetGetCount(mAttributes
);
1630 transform_attribute
**attrs
= (transform_attribute
**)malloc(cnt
*sizeof(transform_attribute
*));
1631 CFStringRef
*names
= (CFStringRef
*)malloc(cnt
*sizeof(CFStringRef
));
1632 CFDictionaryRef
*values
= (CFDictionaryRef
*)malloc(sizeof(CFDictionaryRef
) * cnt
);
1634 if (attrs
== NULL
|| names
== NULL
|| values
== NULL
) {
1642 for(i
= j
= 0; i
< cnt
; ++i
)
1644 transform_attribute
*ta
= attrs
[i
];
1645 if (!ta
->ignore_while_externalizing
)
1647 names
[j
] = ta
->name
;
1648 values
[j
++] = GetAHDictForSaveState(ta
->name
);
1652 CFDictionaryRef result
= CFDictionaryCreate(NULL
, (const void**)&names
, (const void**)&values
, j
, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1656 for(i
= j
= 0; i
< cnt
; ++i
)
1658 transform_attribute
*ta
= attrs
[i
];
1659 if (!ta
->ignore_while_externalizing
)
1661 CFReleaseNull(values
[j
++]);
1672 void Transform::RestoreState(CFDictionaryRef state
)
1674 CFIndex i
, cnt
= CFDictionaryGetCount(state
);
1676 **keys
= (const void **)alloca(sizeof(void*)*cnt
),
1677 **values
= (const void **)alloca(sizeof(void*)*cnt
);
1679 CFDictionaryGetKeysAndValues(state
, keys
, values
);
1681 // Open issue -- do we need to do anything to values that are already set, but are not in "state"?
1682 // this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects,
1683 // it only becomes an issue if we add a ResetFromState, or use it internally in that role.
1685 for(i
= 0; i
< cnt
; i
++)
1687 SecTransformAttributeRef ah
= getAH(keys
[i
]);
1694 CFIndex j
, meta_cnt
= CFDictionaryGetCount((CFDictionaryRef
)values
[i
]);
1695 const void **types
= (const void**)alloca(sizeof(void*)*meta_cnt
), **meta_values
= (const void**)alloca(sizeof(void*)*meta_cnt
);
1696 CFDictionaryGetKeysAndValues((CFDictionaryRef
)values
[i
], types
, meta_values
);
1699 for(j
= 0; j
< meta_cnt
; ++j
)
1701 CFNumberGetValue((CFNumberRef
)types
[j
], kCFNumberIntType
, &t
);
1702 if (t
== kSecTransformMetaAttributeValue
)
1704 if (meta_values
[j
]) {
1705 // SendMetaAttribute doesn't activate the callbacks
1706 SetAttribute(ah
, meta_values
[j
]);
1711 CFErrorRef result
= SendMetaAttribute(ah
, (SecTransformMetaAttributeType
)t
, meta_values
[j
]);
1712 CFReleaseNull(result
); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1716 CFErrorRef result
= SendMetaAttribute(ah
, kSecTransformMetaAttributeExternalize
, kCFBooleanTrue
);
1717 CFReleaseNull(result
); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1721 GroupTransform
* Transform::GetRootGroup()
1723 GroupTransform
*g
= mGroup
;
1729 if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
1730 return (GroupTransform
*)this;
1736 CFDictionaryRef
Transform::GetCustomExternalData()
1741 void Transform::SetCustomExternalData(CFDictionaryRef customData
)
1746 CFDictionaryRef
Transform::Externalize(CFErrorRef
* error
)
1750 return (CFDictionaryRef
)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
1753 // make arrays to hold the transforms and the connections
1754 __block CFMutableArrayRef transforms
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1755 __block CFMutableArrayRef connections
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1756 GroupTransform
*root
= GetRootGroup();
1758 CFErrorRef err
= ForAllNodes(false, true, ^(Transform
*t
) {
1760 return t
->ProcessExternalize(transforms
, connections
);
1762 return (CFErrorRef
)NULL
;
1767 // Really? This just seems like a bad idea
1777 // make a dictionary to hold the output
1778 CFMutableDictionaryRef output
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1779 CFDictionaryAddValue(output
, EXTERN_TRANSFORM_TRANSFORM_ARRAY
, transforms
);
1780 CFDictionaryAddValue(output
, EXTERN_TRANSFORM_CONNECTION_ARRAY
, connections
);
1783 CFReleaseNull(connections
);
1784 CFReleaseNull(transforms
);
1789 CFErrorRef
Transform::ProcessExternalize(CFMutableArrayRef transforms
, CFMutableArrayRef connections
)
1791 if (!IsExternalizable()) {
1795 CFDictionaryRef state
= CopyState();
1796 if (state
&& CFGetTypeID(state
) == CFErrorGetTypeID()) {
1797 return (CFErrorRef
)state
;
1800 // make a dictionary to hold the name, type, and state of this node
1801 CFMutableDictionaryRef node
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1802 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_NAME
, GetName());
1804 CFTypeRef type
= CFStringCreateCopy(NULL
, mTypeName
);
1805 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_TYPE
, type
);
1806 CFReleaseNull(type
);
1810 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_STATE
, state
);
1811 CFReleaseNull(state
);
1814 CFDictionaryRef customItems
= GetCustomExternalData();
1815 if (NULL
!= customItems
)
1817 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY
, customItems
);
1818 CFReleaseNull(customItems
);
1821 // append the resulting dictionary to the node list
1822 CFArrayAppendValue(transforms
, node
);
1823 CFReleaseNull(node
);
1825 // now walk the attribute list
1826 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
1827 transform_attribute
**attributes
= (transform_attribute
**)malloc(numAttributes
*sizeof(transform_attribute
*));
1829 if (attributes
== NULL
) {
1830 return GetNoMemoryErrorAndRetain();
1833 TAGetAll(attributes
);
1837 // walk the forward links
1838 for (i
= 0; i
< numAttributes
; ++i
)
1840 CFIndex arraySize
= attributes
[i
]->connections
? CFArrayGetCount(attributes
[i
]->connections
) : 0;
1844 for (j
= 0; j
< arraySize
; ++j
)
1846 transform_attribute
*ta
= ah2ta((SecTransformAttributeRef
)CFArrayGetValueAtIndex(attributes
[i
]->connections
, j
));
1848 if (!ta
->transform
->IsExternalizable()) {
1849 // just pretend non-externalizable transforms don't even exist. Don't write out connections, and don't talk to them about externalizing.
1853 // add this forward connection to the array -- make a dictionary
1854 CFMutableDictionaryRef connection
=
1855 CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1857 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_FROM_NAME
, GetName());
1858 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_FROM_ATTRIBUTE
, attributes
[i
]->name
);
1859 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_TO_NAME
, ta
->transform
->GetName());
1860 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_TO_ATTRIBUTE
, ta
->name
);
1862 CFArrayAppendValue(connections
, connection
);
1863 CFReleaseNull(connection
);