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
) {
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
) {
37 transform_attribute
*ta
= ah2ta(ah
);
38 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@"), ta
->transform
->GetName(), ta
->name
);
41 static CFStringRef
AttributeHandleDebugFormat(CFTypeRef ah
) {
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
);
67 // ta->q already released
71 CFRelease(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 search_for
= makeAH((transform_attribute
*)malloc(sizeof(transform_attribute
)));
165 bzero(ah2ta(search_for
), sizeof(transform_attribute
));
166 pthread_setspecific(ah_search_key_slot
, search_for
);
171 mAttributes
= CFSetCreateMutable(NULL
, 0, &tasetcb
);
174 ah2ta(search_for
)->name
= label
;
175 SecTransformAttributeRef ah
= static_cast<SecTransformAttributeRef
>(const_cast<void*>(CFSetGetValue(mAttributes
, search_for
)));
176 if (ah
== NULL
&& create_ok
)
178 if (CFStringGetLength(label
) && L
'_' == CFStringGetCharacterAtIndex(label
, 0) && !create_underscore_ok
)
180 // Attributes with a leading _ belong to the Transform system only, not to random 3rd party transforms.
184 transform_attribute
*ta
= static_cast<transform_attribute
*>(malloc(sizeof(transform_attribute
)));
191 ta
->name
= CFStringCreateCopy(NULL
, label
);
197 CFIndex cnt
= CFSetGetCount(mAttributes
);
198 CFSetAddValue(mAttributes
, ah
);
199 if (CFSetGetCount(mAttributes
) != cnt
+1)
206 CFStringRef qname
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s/%@"), dispatch_queue_get_label(this->mDispatchQueue
), label
);
207 CFIndex used
, sz
= 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(qname
), kCFStringEncodingUTF8
);
208 UInt8
*qnbuf
= (UInt8
*)alloca(sz
);
209 CFStringGetBytes(qname
, CFRangeMake(0, CFStringGetLength(qname
)), kCFStringEncodingUTF8
, '?', FALSE
, qnbuf
, sz
, &used
);
211 ta
->q
= dispatch_queue_create((char*)qnbuf
, NULL
);
213 ta
->semaphore
= dispatch_semaphore_create(kMaxPendingTransactions
);
216 ta
->pushback_state
= transform_attribute::pb_empty
;
217 ta
->pushback_value
= NULL
;
219 ta
->connections
= NULL
;
220 ta
->transform
= this;
222 dispatch_set_target_queue(ta
->q
, mDispatchQueue
);
224 ta
->requires_outbound_connection
= 0;
227 ta
->ignore_while_externalizing
= 0;
228 ta
->has_incoming_connection
= 0;
229 ta
->direct_error_handling
= 0;
230 ta
->allow_external_sets
= 0;
231 ta
->has_been_deferred
= 0;
232 ta
->attribute_changed_block
= NULL
;
233 ta
->attribute_validate_block
= NULL
;
239 transform_attribute
*Transform::getTA(SecTransformStringOrAttributeRef attrib
, bool create_ok
)
241 SecTransformAttributeRef ah
= getAH(attrib
, create_ok
);
254 void Transform::TAGetAll(transform_attribute
**attributes
) {
255 CFSetGetValues(mAttributes
, (const void**)attributes
);
256 CFIndex i
, n
= CFSetGetCount(mAttributes
);
257 for(i
= 0; i
< n
; ++i
) {
258 attributes
[i
] = ah2ta(attributes
[i
]);
264 bool Transform::HasNoOutboundConnections()
266 // make an array big enough to hold all of the attributes
267 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
268 transform_attribute
* attributes
[numAttributes
];
270 TAGetAll(attributes
);
272 // check all of the attributes
274 for (i
= 0; i
< numAttributes
; ++i
)
276 if (attributes
[i
]->connections
&& CFArrayGetCount(attributes
[i
]->connections
) != 0)
287 bool Transform::HasNoInboundConnections()
289 // make an array big enough to hold all of the attributes
290 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
291 transform_attribute
* attributes
[numAttributes
];
293 TAGetAll(attributes
);
295 // check all of the attributes
297 for (i
= 0; i
< numAttributes
; ++i
)
299 if (attributes
[i
]->has_incoming_connection
)
310 CFIndex
Transform::GetAttributeCount()
312 return CFSetGetCount(mAttributes
);
315 Transform::Transform(CFStringRef transformType
, CFStringRef CFobjectType
) :
316 CoreFoundationObject(CFobjectType
),
318 mIsFinalizing(false),
319 mAlwaysSelfNotify(false),
322 mTypeName(CFStringCreateCopy(NULL
, transformType
))
326 mProcessingPushbacks
= FALSE
;
328 if (internalID
== _kCFRuntimeNotATypeID
) {
329 (void)SecTransformNoData();
330 internalID
= CoreFoundationObject::FindObjectType(gInternalCFObjectName
);
333 // create a name for the transform
336 for (i
= 0; i
< sizeof(rname
) - 1; ++i
)
338 rname
[i
] = RandomChar();
343 char *tname
= const_cast<char*>(CFStringGetCStringPtr(transformType
, kCFStringEncodingUTF8
));
345 CFIndex sz
= 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType
), kCFStringEncodingUTF8
);
346 tname
= static_cast<typeof(tname
)>(alloca(sz
));
348 CFStringGetCString(transformType
, tname
, sz
, kCFStringEncodingUTF8
);
350 tname
= const_cast<char*>("-");
355 asprintf(&name
, "%s-%s", rname
, tname
);
358 asprintf(&dqName
, "%s-%s", rname
, tname
);
361 asprintf(&aqName
, "aq-%s-%s", rname
, tname
);
363 mDispatchQueue
= dispatch_queue_create(dqName
, NULL
);
364 dispatch_queue_set_specific(mDispatchQueue
, &dispatchQueueToTransformKey
, this, NULL
);
365 // mActivationQueue's job in life is to be suspended until just after this transform is made active.
366 // It's primary use is when a value flowing across a connection isn't sure if the target transform is active yet.
367 mActivationQueue
= dispatch_queue_create(aqName
, NULL
);
368 dispatch_set_target_queue(mActivationQueue
, mDispatchQueue
);
369 dispatch_suspend(mActivationQueue
);
370 mActivationPending
= dispatch_group_create();
372 // set up points for ABORT, DEBUG, INPUT, and OUTPUT
373 AbortAH
= getAH(kSecTransformAbortAttributeName
, true);
374 transform_attribute
*ta
= ah2ta(AbortAH
);
375 ta
->ignore_while_externalizing
= 1;
376 CFStringRef attributeName
= CFStringCreateWithCStringNoCopy(NULL
, name
, 0, kCFAllocatorMalloc
);
377 SetAttributeNoCallback(kSecTransformTransformName
, attributeName
);
378 CFRelease(attributeName
);
383 DebugAH
= getAH(kSecTransformDebugAttributeName
, true);
384 ah2ta(DebugAH
)->ignore_while_externalizing
= 1;
386 ta
= getTA(kSecTransformInputAttributeName
, true);
387 ta
->required
= ta
->deferred
= ta
->stream
= 1;
388 ta
->allow_external_sets
= 0;
390 ta
->has_been_deferred
= 0;
391 ta
= getTA(kSecTransformOutputAttributeName
, true);
392 ta
->requires_outbound_connection
= ta
->stream
= 1;
395 static void run_and_release_finalizer(void *finalizer_block
)
397 ((dispatch_block_t
)finalizer_block
)();
398 Block_release(finalizer_block
);
401 static void set_dispatch_finalizer(dispatch_object_t object
, dispatch_block_t finalizer
)
403 finalizer
= Block_copy(finalizer
);
404 dispatch_set_context(object
, finalizer
);
405 dispatch_set_finalizer_f(object
, run_and_release_finalizer
);
408 void Transform::FinalizePhase2()
413 void Transform::FinalizeForClang()
415 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
416 SecTransformAttributeRef handles
[numAttributes
];
417 CFSetGetValues(mAttributes
, (const void**)&handles
);
419 for(CFIndex i
= 0; i
< numAttributes
; ++i
) {
420 SecTransformAttributeRef ah
= handles
[i
];
421 transform_attribute
*ta
= ah2ta(ah
);
423 set_dispatch_finalizer(ta
->q
, ^{
424 // NOTE: not done until all pending use of the attribute queue has ended AND retain count is zero
425 ta
->transform
= NULL
;
428 // If there is a pending pushback the attribute queue will be suspended, and needs a kick before it can be destructed.
429 if (__sync_bool_compare_and_swap(&ta
->pushback_state
, transform_attribute::pb_value
, transform_attribute::pb_discard
)) {
430 dispatch_resume(ta
->q
);
432 dispatch_release(ta
->q
);
435 // We might be finalizing a transform as it is being activated, make sure that is complete before we do the rest
436 dispatch_group_notify(mActivationPending
, mDispatchQueue
, ^{
437 if (mActivationQueue
!= NULL
) {
438 // 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
439 dispatch_resume(mActivationQueue
);
440 dispatch_release(mActivationQueue
);
443 set_dispatch_finalizer(mDispatchQueue
, ^{
444 // 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
447 dispatch_release(mDispatchQueue
);
451 void Transform::Finalize()
453 // 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
454 // (NOTE: moved block into member function as clang hits an internal error and declines to compile)
455 dispatch_block_t continue_finalization
= ^{ this->FinalizeForClang(); };
456 dispatch_block_t mark_as_finalizing
= ^{ this->mIsFinalizing
= true; };
458 // Mark the transform as "finalizing" so it knows not to propagate values across connections
459 if (this == dispatch_get_specific(&dispatchQueueToTransformKey
)) {
460 mark_as_finalizing();
462 dispatch_sync(mDispatchQueue
, mark_as_finalizing
);
466 (void)transforms_assume(mGroup
->mIsFinalizing
); // under retain?
467 mGroup
->AddAllChildrenFinalizedCallback(mDispatchQueue
, continue_finalization
);
468 mGroup
->ChildStartedFinalization(this);
470 // a "bare" transform (normally itself a group) still needs to be deconstructed
471 dispatch_async(mDispatchQueue
, continue_finalization
);
475 Transform::~Transform()
477 CFRelease(mAttributes
);
479 CFRelease(mAbortError
);
483 // See if we can catch anything using us after our death
484 mDispatchQueue
= (dispatch_queue_t
)0xdeadbeef;
486 CFRelease(mTypeName
);
488 if (NULL
!= mPushedback
)
490 CFRelease(mPushedback
);
492 dispatch_release(mActivationPending
);
495 CFStringRef
Transform::GetName() {
496 return (CFStringRef
)GetAttribute(kSecTransformTransformName
);
499 CFTypeID
Transform::GetCFTypeID()
501 return CoreFoundationObject::FindObjectType(CFSTR("SecTransform"));
504 std::string
Transform::DebugDescription()
506 return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName());
509 CFErrorRef
Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key
, SecTransformMetaAttributeType type
, CFTypeRef value
)
511 SecTransformAttributeRef ah
= getAH(key
, true);
512 transform_attribute
*ta
= ah2ta(ah
);
515 case kSecTransformMetaAttributeRequired
:
516 ta
->required
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
519 case kSecTransformMetaAttributeRequiresOutboundConnection
:
520 ta
->requires_outbound_connection
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
523 case kSecTransformMetaAttributeDeferred
:
524 ta
->deferred
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
527 case kSecTransformMetaAttributeStream
:
528 ta
->stream
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
531 case kSecTransformMetaAttributeHasOutboundConnections
:
532 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah
);
534 case kSecTransformMetaAttributeHasInboundConnection
:
535 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah
);
537 case kSecTransformMetaAttributeCanCycle
:
538 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah
);
540 case kSecTransformMetaAttributeExternalize
:
541 ta
->ignore_while_externalizing
= CFBooleanGetValue((CFBooleanRef
)value
) ? 0 : 1;
544 case kSecTransformMetaAttributeValue
:
545 return SetAttributeNoCallback(ah
, value
);
547 case kSecTransformMetaAttributeRef
:
548 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah
);
550 case kSecTransformMetaAttributeName
:
551 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah
);
554 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set unknown meta attribute #%d to %@ on %@", type
, value
, key
);
560 CFTypeRef
Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key
, SecTransformMetaAttributeType type
) {
561 SecTransformAttributeRef ah
= getAH(key
, true);
562 transform_attribute
*ta
= ah2ta(ah
);
564 case kSecTransformMetaAttributeRequired
:
565 return (CFTypeRef
)(ta
->required
? kCFBooleanTrue
: kCFBooleanFalse
);
566 case kSecTransformMetaAttributeRequiresOutboundConnection
:
567 return (CFTypeRef
)(ta
->requires_outbound_connection
? kCFBooleanTrue
: kCFBooleanFalse
);
568 case kSecTransformMetaAttributeDeferred
:
569 return (CFTypeRef
)(ta
->deferred
? kCFBooleanTrue
: kCFBooleanFalse
);
570 case kSecTransformMetaAttributeStream
:
571 return (CFTypeRef
)(ta
->stream
? kCFBooleanTrue
: kCFBooleanFalse
);
572 case kSecTransformMetaAttributeHasOutboundConnections
:
573 return (CFTypeRef
)((ta
->connections
&& CFArrayGetCount(ta
->connections
)) ? kCFBooleanTrue
: kCFBooleanFalse
);
574 case kSecTransformMetaAttributeHasInboundConnection
:
575 return (CFTypeRef
)(ta
->has_incoming_connection
? kCFBooleanTrue
: kCFBooleanFalse
);
576 case kSecTransformMetaAttributeCanCycle
:
577 return (CFTypeRef
)kCFBooleanFalse
;
578 case kSecTransformMetaAttributeExternalize
:
579 return (CFTypeRef
)(ta
->ignore_while_externalizing
? kCFBooleanFalse
: kCFBooleanTrue
);
580 case kSecTransformMetaAttributeRef
:
582 case kSecTransformMetaAttributeValue
:
584 case kSecTransformMetaAttributeName
:
587 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't get unknown meta attribute #%d from %@", type
, key
);
596 CFErrorRef
Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError
)
598 // pull apart the error
599 CFIndex code
= CFErrorGetCode(sourceError
);
600 CFStringRef domain
= CFErrorGetDomain(sourceError
);
601 CFDictionaryRef oldUserInfo
= CFErrorCopyUserInfo(sourceError
);
602 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutableCopy(NULL
, 0, oldUserInfo
);
603 CFRelease(oldUserInfo
);
605 // add the new key and value to the dictionary
606 CFDictionaryAddValue(userInfo
, kSecTransformAbortOriginatorKey
, GetCFObject());
608 // make a new CFError
609 CFErrorRef newError
= CFErrorCreate(NULL
, domain
, code
, userInfo
);
614 // NOTE: If called prior to execution will schedule a later call to AbortAllTransforms
615 void Transform::AbortJustThisTransform(CFErrorRef abortErr
)
617 (void)transforms_assume(abortErr
);
618 (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey
) == this);
620 Boolean wasActive
= mIsActive
;
622 if (OSAtomicCompareAndSwapPtr(NULL
, (void *)abortErr
, (void**)&mAbortError
)) {
623 // send an abort message to the attribute so that it can shut down
624 // note that this bypasses the normal processes. The message sent is a notification
625 // that things aren't working well any more, the transform cannot make any other assumption.
627 // mAbortError is released in the destructor which is triggered (in part)
628 // by the dispatch queue finalizer so we don't need a retain/release of
629 // abortErr for the abortAction block, but we do need to retain it
630 // here to match with the release by the destructor.
633 dispatch_block_t abortAction
= ^{
634 // This actually makes the abort happen, it needs to run on the transform's queue while the
635 // transform is executing.
638 // When this abort was first processed we were not executing, so
639 // additional transforms may have been added to our group (indeed,
640 // we may not have had a group at all), so we need to let everyone
641 // know about the problem. This will end up letting ourself (and
642 // maybe some others) know an additional time, but the CompareAndSwap
643 // prevents that from being an issue.
644 this->AbortAllTransforms(abortErr
);
647 SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle
= getAH(kSecTransformInputAttributeName
, false);
648 // Calling AttributeChanged directly lets an error "skip ahead" of the input queue,
649 // and even execute if the input queue is suspended pending pushback retries.
650 AttributeChanged(inputAttributeHandle
, abortErr
);
655 // This transform is running, so we use the normal queue (which we are
656 // already executing on)
659 // This transform hasn't run yet, do the work on the activation queue
660 // so it happens as soon as the transforms starts executing.
661 dispatch_async(mActivationQueue
, abortAction
);
664 Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH
, abortErr
, mAbortError
);
668 // abort all transforms in the root group & below
669 void Transform::AbortAllTransforms(CFTypeRef err
)
671 Debug("%@ set to %@, aborting\n", AbortAH
, err
);
672 CFErrorRef error
= NULL
;
674 CFTypeRef replacementErr
= NULL
;
676 if (CFGetTypeID(err
) != CFErrorGetTypeID())
678 replacementErr
= err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidType
, "ABORT set to a %@ (%@) not a %@", CFCopyTypeIDDescription(CFGetTypeID(err
)), err
, CFCopyTypeIDDescription(CFErrorGetTypeID()));
681 error
= RefactorErrorToIncludeAbortingTransform((CFErrorRef
)err
);
685 CFRelease(replacementErr
);
688 GroupTransform
*root
= GetRootGroup();
691 // tell everyone in the (root) group to "AbortJustThisTransform"
692 dispatch_group_t all_aborted
= dispatch_group_create();
693 root
->ForAllNodesAsync(false, all_aborted
, ^(Transform
* t
){
694 t
->AbortJustThisTransform(error
);
696 dispatch_group_notify(all_aborted
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH
, 0), ^(void) {
698 dispatch_release(all_aborted
);
703 // We are everyone so we AbortJustThisTransform "directly"
704 // NOTE: this can only happen prior to execution (execution always happens in a group)
705 (void)transforms_assume_zero(mIsActive
);
706 this->AbortJustThisTransform(error
);
712 CFErrorRef
Transform::Disconnect(Transform
* destinationTransform
, CFStringRef myKey
, CFStringRef hisKey
)
714 //CFTypeRef thisTransform = (SecTransformRef) GetCFObject();
716 // find this transform in the backlinks for the destination
719 // now remove the link in the transform dictionary
720 transform_attribute
*src
= getTA(myKey
, true);
721 SecTransformAttributeRef dst
= destinationTransform
->getAH(hisKey
);
723 if (src
->connections
== NULL
)
725 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Cannot find transform in destination.");
728 CFIndex numConnections
= CFArrayGetCount(src
->connections
);
729 for (i
= 0; i
< numConnections
; ++i
)
731 if (CFArrayGetValueAtIndex(src
->connections
, i
) == dst
)
733 CFArrayRemoveValueAtIndex(src
->connections
, i
);
734 numConnections
= CFArrayGetCount(src
->connections
);
737 // clear the has_incoming_connection bit in the destination. We can do this because inputs can have only one connection.
738 transform_attribute
* dstTA
= ah2ta(dst
);
739 dstTA
->has_incoming_connection
= false;
742 if (HasNoInboundConnections() && HasNoOutboundConnections())
744 // we have been orphaned, just remove us
745 mGroup
->RemoveMemberFromGroup(GetCFObject());
754 CFErrorRef
Transform::Connect(GroupTransform
*group
, Transform
* destinationTransform
, CFStringRef destAttr
, CFStringRef srcAttr
)
758 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make connections without a specific group (do not call with group = NULL)");
762 GroupTransform
*newSourceGroup
= mGroup
;
763 GroupTransform
*newDestinationGroup
= destinationTransform
->mGroup
;
765 if (mGroup
== NULL
|| mGroup
== this)
767 newSourceGroup
= group
;
770 if (destinationTransform
->mGroup
== NULL
|| destinationTransform
->mGroup
== destinationTransform
)
772 newDestinationGroup
= group
;
775 if (newSourceGroup
!= newDestinationGroup
&& mGroup
)
777 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup
->GetName(), destinationTransform
->GetName(), newDestinationGroup
->GetName());
781 if (!validConnectionPoint(srcAttr
)) {
782 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make a connection from non-exported attribute %@ of %@", srcAttr
, this->GetName());
785 if (!destinationTransform
->validConnectionPoint(destAttr
)) {
786 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make a connection to non-exported attribute %@ of %@", destAttr
, destinationTransform
->GetName());
790 mGroup
= newSourceGroup
;
791 destinationTransform
->mGroup
= newDestinationGroup
;
793 // NOTE: this fails on OOM
794 group
->AddMemberToGroup(this->GetCFObject());
795 group
->AddMemberToGroup(destinationTransform
->GetCFObject());
797 transform_attribute
*src
= this->getTA(srcAttr
, true);
798 SecTransformAttributeRef dst
= destinationTransform
->getAH(destAttr
);
800 if (!src
->connections
)
802 src
->connections
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
804 CFArrayAppendValue(src
->connections
, dst
);
806 ah2ta(dst
)->has_incoming_connection
= 1;
812 bool Transform::validConnectionPoint(CFStringRef attributeName
)
817 // NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes
818 // SetAttribute eventually calls SetAttributeNoCallback
819 CFErrorRef
Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key
, CFTypeRef value
)
821 SecTransformAttributeRef ah
= getAH(key
, true);
826 transform_attribute
*ta
= ah2ta(ah
);
828 if (ah
== AbortAH
&& value
&& (mIsActive
|| !ta
->deferred
))
830 AbortAllTransforms(value
);
831 return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress
, "Abort started");
834 bool do_propagate
= true;
836 if (!ta
->has_been_deferred
)
838 bool doNotRetain
= false;
842 CFStringRef name
= ta
->name
;
843 if (CFGetTypeID(value
) == CFReadStreamGetTypeID())
845 CFTypeRef src
= StreamSource::Make((CFReadStreamRef
) value
, this, name
);
847 do_propagate
= false;
848 ta
->has_been_deferred
= 1;
851 else if (ta
->deferred
&& !mIsActive
)
855 Debug("%@ deferred value=%p\n", ah
, value
);
858 CFTypeRef src
= SingleShotSource::Make(value
, this, name
);
859 ta
->has_been_deferred
= 1;
861 // the old value will be release when Transform::Do terminates
864 do_propagate
= false;
869 ta
->has_been_deferred
= 0;
873 if (ta
->value
!= value
) {
874 if (value
&& !doNotRetain
) {
878 CFRelease(ta
->value
);
885 // propagate the changes out to all connections
886 if (ta
->connections
&& mIsActive
&& do_propagate
&& !(mAbortError
|| mIsFinalizing
))
888 Debug("Propagating from %@ to %@\n", ah
, ta
->connections
);
889 CFIndex i
, numConnections
= CFArrayGetCount(ta
->connections
);
890 for(i
= 0; i
< numConnections
; ++i
) {
891 SecTransformAttributeRef ah
= static_cast<SecTransformAttributeRef
>(const_cast<void *>(CFArrayGetValueAtIndex(ta
->connections
, i
)));
892 Transform
*tt
= ah2ta(ah
)->transform
;
897 tt
->SetAttribute(ah
, value
);
901 dispatch_block_t setAttribute
= ^{
902 tt
->SetAttribute(ah
, value
);
904 // Here the target queue might not be activated yet, we can't
905 // look directly at the other transform's ActivationQueue as
906 // it might activate (or Finalize!) as we look, so just ask
907 // the other transform to deal with it.
908 dispatch_async(ah2ta(ah
)->q
, ^(void) {
909 // This time we are on the right queue to know this is the real deal
913 dispatch_async(ah2ta(ah
)->transform
->mActivationQueue
, setAttribute
);
924 // external sets normally fail if the transform is running
925 CFErrorRef
Transform::ExternalSetAttribute(CFTypeRef key
, CFTypeRef value
)
929 return this->SetAttribute(key
, value
);
933 SecTransformAttributeRef ah
= getAH(key
, false);
934 if (ah
!= NULL
&& ah2ta(ah
)->allow_external_sets
)
936 return this->SetAttribute(static_cast<CFTypeRef
>(ah
), value
);
940 return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "%@ can not be set while %@ is executing", ah
, this->GetName());
946 // queue up the setting of the key and value
947 CFErrorRef
Transform::SetAttribute(CFTypeRef key
, CFTypeRef value
)
951 return CreateSecTransformErrorRef(kSecTransformErrorAborted
, "ABORT has been sent to the transform (%@)", mAbortError
);
954 // queue up the setting of the key and value
955 SecTransformAttributeRef ah
;
956 if (CFGetTypeID(key
) == transform_attribute::cftype
)
960 else if (CFGetTypeID(key
) == CFStringGetTypeID())
962 ah
= getAH(static_cast<CFStringRef
>(key
));
965 return CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute
, "Can't set attribute %@ in transform %@", key
, GetName());
970 return CreateSecTransformErrorRef(kSecTransformErrorInvalidType
, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key
);
973 // Do this after the error check above so we don't leak
976 CFRetain(value
); // if we use dispatch_async we need to own the value (the matching release is in the set block)
980 transform_attribute
*ta
= ah2ta(ah
);
982 dispatch_block_t set
= ^{
985 dispatch_semaphore_signal(ta
->semaphore
);
994 // when the transform is active, set attributes asynchronously. Otherwise, we are doing
995 // initialization and must wait for the operation to complete.
998 dispatch_async(ta
->q
, set
);
1002 dispatch_sync(ta
->q
, set
);
1004 if (dispatch_semaphore_wait(ta
->semaphore
, dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
))) {
1005 Debug("Send from %@ to %@ is still waiting\n", GetName(), ah
);
1006 dispatch_semaphore_wait(ta
->semaphore
, DISPATCH_TIME_FOREVER
);
1009 // Return the best available status (which will be NULL if we haven't aborted, or stated an
1010 // intent to abort when execution starts)
1012 // The value of the ABORT attribute can differ from mAbortError, first if a transform is aborted
1013 // prior to running the general abort mechanic is deferred until execution. Second during
1014 // execution the abort logic avoids most of the normal processing. Third, and most importantly
1015 // during an abort the exact error that gets generated will differ from the value sent to ABORT
1016 // (for example if a non-CFError was sent...plus even if it was a CFError we annotate that error).
1021 CFErrorRef
Transform::SendAttribute(SecTransformStringOrAttributeRef key
, CFTypeRef value
)
1023 return SetAttributeNoCallback(key
, value
);
1028 CFTypeRef
Transform::GetAttribute(SecTransformStringOrAttributeRef key
)
1030 struct transform_attribute
*ta
= getTA(key
, false);
1031 if (ta
== NULL
|| ta
->value
== NULL
) {
1035 if (CFGetTypeID(ta
->value
) == internalID
)
1037 // this is one of our internal objects, so get the value from it
1038 Source
* source
= (Source
*) CoreFoundationHolder::ObjectFromCFType(ta
->value
);
1039 return source
->GetValue();
1047 CFErrorRef
Transform::Pushback(SecTransformAttributeRef ah
, CFTypeRef value
)
1049 CFErrorRef result
= NULL
;
1050 transform_attribute
*ta
= ah2ta(ah
);
1051 if (!(ta
->pushback_state
== transform_attribute::pb_empty
|| ta
->pushback_state
== transform_attribute::pb_repush
))
1053 CFErrorRef error
= fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidOperation
, CFSTR("Can not pushback new value until old value has been processed"));
1054 SetAttribute(kSecTransformAbortAttributeName
, error
);
1057 if (value
== NULL
&& ta
->pushback_value
== NULL
&& ta
->pushback_state
== transform_attribute::pb_repush
)
1059 ta
->pushback_state
= transform_attribute::pb_presented_once
;
1062 ta
->pushback_state
= transform_attribute::pb_value
;
1068 ta
->pushback_value
= value
;
1069 dispatch_suspend(ta
->q
);
1072 mPushedback
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1074 CFArrayAppendValue(mPushedback
, ah
);
1078 void Transform::try_pushbacks() {
1079 if (!mPushedback
|| !CFArrayGetCount(mPushedback
)) {
1080 mProcessingPushbacks
= FALSE
;
1084 CFArrayRef pb
= (CFArrayRef
)mPushedback
;
1085 mPushedback
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1086 CFIndex i
, n
= CFArrayGetCount(pb
);
1088 for(i
= 0; i
< n
; ++i
)
1090 SecTransformAttributeRef ah
= CFArrayGetValueAtIndex(pb
, i
);
1091 transform_attribute
*ta
= ah2ta(ah
);
1092 ta
->pushback_state
= transform_attribute::pb_repush
;
1093 CFTypeRef v
= ta
->pushback_value
;
1094 ta
->pushback_value
= NULL
;
1100 if (ta
->pushback_state
== transform_attribute::pb_repush
) {
1101 ta
->pushback_state
= transform_attribute::pb_empty
;
1104 // NOTE: a successful repush needs the queue unsuspended so it can run.
1105 // A failed repush has suspended the queue an additional time, so we
1106 // still need to resume it.
1107 dispatch_resume(ta
->q
);
1112 if (succeeded
&& CFArrayGetCount(mPushedback
)) {
1113 // some attribute changed while we proceeded the last batch of pushbacks, so any "new" pushbacks are eligible to run again.
1114 // 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.
1115 dispatch_async(mDispatchQueue
, ^{ try_pushbacks(); });
1117 mProcessingPushbacks
= FALSE
;
1121 void Transform::Debug(const char *cfmt
, ...) {
1122 CFTypeRef d
= ah2ta(DebugAH
)->value
;
1124 CFWriteStreamRef out
= NULL
;
1125 if (CFGetTypeID(d
) == CFWriteStreamGetTypeID()) {
1126 out
= (CFWriteStreamRef
)d
;
1128 static dispatch_once_t once
;
1129 static CFWriteStreamRef StdErrWriteStream
;
1130 dispatch_once(&once
, ^{
1131 auto GCC_BUG_WORKAROUND CFURLRef GCC_BUG_WORKAROUND p
= CFURLCreateWithFileSystemPath(NULL
, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle
, FALSE
);
1132 StdErrWriteStream
= CFWriteStreamCreateWithFile(NULL
, p
);
1133 CFWriteStreamOpen(StdErrWriteStream
);
1136 out
= StdErrWriteStream
;
1142 CFStringRef fmt
= CFStringCreateWithCString(NULL
, cfmt
, kCFStringEncodingUTF8
);
1143 CFStringRef str
= CFStringCreateWithFormatAndArguments(NULL
, NULL
, fmt
, ap
);
1148 CFIndex sz
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(str
), kCFStringEncodingUTF8
);
1152 bool needs_free
= true;
1153 buf
= (unsigned char*)malloc(sz
);
1155 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, '?', FALSE
, buf
, sz
, &used
);
1157 buf
= (unsigned char *)"malloc failure during Transform::Debug\n";
1161 static dispatch_once_t once
;
1162 static dispatch_queue_t print_q
;
1163 dispatch_once(&once
, ^{
1164 print_q
= dispatch_queue_create("com.apple.security.debug.print_queue", 0);
1165 dispatch_set_target_queue((dispatch_object_t
)print_q
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW
, 0));
1168 dispatch_async(print_q
, ^{
1169 CFWriteStreamWrite(out
, buf
, used
);
1179 void Transform::Do(SecTransformAttributeRef ah
, CFTypeRef value
)
1181 transform_attribute
*ta
= ah2ta(ah
);
1182 if (ta
->pushback_state
== transform_attribute::pb_discard
)
1186 (void)transforms_assume(dispatch_get_current_queue() == ((ta
->pushback_state
== transform_attribute::pb_repush
) ? mDispatchQueue
: ta
->q
));
1190 Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value
, ah
, dispatch_queue_get_label(dispatch_get_current_queue()));
1194 SetAttributeNoCallback(ah
, value
);
1195 // While an abort is in progress things can get into bad
1196 // states if we allow normal processing so we throw anything
1197 // on the floor except CFErrorRef or NULL vales sent to
1198 // ABORT or INPUT (we need to process them to let the
1199 // transform shut down correctly)
1200 if (mAbortError
&& (!(ah
== this->AbortAH
|| ah
== getTA(CFSTR("INPUT"), true)) && (value
== NULL
|| CFGetTypeID(value
) != CFErrorGetTypeID())))
1203 Debug("Ignoring value (%@) sent to %@ during abort\n", value
, ah
);
1205 Debug("Ignoring NULL sent to %@ during abort\n", ah
);
1210 if (mIsActive
|| (mAlwaysSelfNotify
&& !ta
->deferred
))
1212 Debug("AttributeChanged: %@ (%s) = %@\n", ah
, mIsActive
? "is executing" : "self notify set", value
? value
: (CFTypeRef
)CFSTR("(NULL)"));
1213 AttributeChanged(ah
, value
);
1216 if (mPushedback
&& CFArrayGetCount(mPushedback
) && !mProcessingPushbacks
)
1218 Debug("will process pushbacks (%@) later\n", mPushedback
);
1219 mProcessingPushbacks
= TRUE
;
1220 dispatch_async(mDispatchQueue
, ^{ try_pushbacks(); });
1227 void Transform::AttributeChanged(CFStringRef name
, CFTypeRef value
)
1231 void Transform::AttributeChanged(SecTransformAttributeRef ah
, CFTypeRef value
)
1233 AttributeChanged(ah2ta(ah
)->name
, value
);
1236 CFArrayRef
Transform::GetAllAH() {
1237 CFIndex cnt
= CFSetGetCount(mAttributes
);
1238 const void **values
= (const void **)alloca(sizeof(void*)*cnt
);
1239 CFSetGetValues(mAttributes
, values
);
1240 return CFArrayCreate(NULL
, values
, cnt
, &kCFTypeArrayCallBacks
);
1243 CFTypeRef
Transform::Execute(dispatch_queue_t deliveryQueue
, SecMessageBlock deliveryBlock
, CFErrorRef
* errorRef
)
1247 CFTypeRef g
= GroupTransform::Make();
1248 mGroup
= (GroupTransform
*)CoreFoundationHolder::ObjectFromCFType(g
);
1249 mGroup
->AddMemberToGroup(this->GetCFObject());
1250 SecMessageBlock smb
= ^(CFTypeRef message
, CFErrorRef error
, Boolean isFinal
)
1252 deliveryBlock(message
, error
, isFinal
);
1255 dispatch_async(this->mDispatchQueue
, ^{
1261 CFTypeRef ret
= this->Execute(deliveryQueue
, deliveryBlock
? smb
: (SecMessageBlock
) NULL
, errorRef
);
1275 *errorRef
= CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform has already executed, it may not be executed again.", GetName());
1281 // Do a retain on our parent since we are using it
1282 GroupTransform
*rootGroup
= GetRootGroup();
1283 CFRetain(rootGroup
->GetCFObject());
1285 CFTypeRef result
= NULL
;
1287 CFTypeRef monitorRef
= BlockMonitor::Make(deliveryQueue
, deliveryBlock
);
1289 __block CFStringRef outputAttached
= NULL
;
1291 dispatch_queue_t p2
= dispatch_queue_create("activate phase2", NULL
);
1292 dispatch_queue_t p3
= dispatch_queue_create("activate phase3", NULL
);
1293 dispatch_suspend(p2
);
1294 dispatch_suspend(p3
);
1295 // walk the transform, doing phase1 activating as we go, and queueing phase2 and phase3 work
1296 CFErrorRef temp
= TraverseTransform(NULL
, ^(Transform
*t
){
1297 return t
->ExecuteOperation(outputAttached
, (SecMonitorRef
)monitorRef
, p2
, p3
);
1299 // ExecuteOperation is not called for the outer group, so we need to manually set mISActive for it.
1300 rootGroup
->mIsActive
= true;
1301 rootGroup
->StartingExecutionInGroup();
1302 dispatch_resume(p2
);
1303 dispatch_sync(p2
, ^{ dispatch_resume(p3
); });
1304 dispatch_sync(p3
, ^{ dispatch_release(p2
); });
1305 dispatch_release(p3
);
1312 // It is safe to keep the monitors attached, because it is invalid to try to execute again, BUT
1313 // we do need to release the reference to the group that the monitor would normally release
1314 // when it processes the final message.
1315 CFRelease(rootGroup
->GetCFObject());
1316 CFRelease(monitorRef
);
1317 rootGroup
->StartedExecutionInGroup(false);
1321 dispatch_group_t initialized
= dispatch_group_create();
1322 rootGroup
->ForAllNodesAsync(true, initialized
, ^(Transform
*t
) {
1326 dispatch_group_notify(initialized
, rootGroup
->mDispatchQueue
, ^{
1327 dispatch_release(initialized
);
1328 dispatch_group_t activated
= dispatch_group_create();
1329 dispatch_group_enter(activated
);
1330 dispatch_async(rootGroup
->mDispatchQueue
, ^{
1331 rootGroup
->ForAllNodesAsync(true, activated
, ^(Transform
*t
) {
1332 t
->ActivateInputs();
1334 dispatch_group_leave(activated
);
1336 dispatch_group_notify(activated
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
1337 dispatch_release(activated
);
1338 // once we have been activated (but not before!), the monitor belongs to the group, and we can drop our claim
1339 CFRelease(monitorRef
);
1340 rootGroup
->StartedExecutionInGroup(true);
1348 void Transform::Initialize()
1352 static void ActivateInputs_set(const void *v
, void *unused
) {
1353 transform_attribute
*ta
= static_cast<transform_attribute
*>(ah2ta(const_cast<void *>(v
)));
1354 if (ta
->value
&& internalID
== CFGetTypeID(ta
->value
)) {
1355 Source
* s
= (Source
*) CoreFoundationHolder::ObjectFromCFType(ta
->value
);
1360 void Transform::ActivateInputs()
1362 (void)transforms_assume_zero(mIsActive
&& this != dispatch_get_specific(&dispatchQueueToTransformKey
));
1364 // now run all of the forward links
1365 if (!mIsFinalizing
) {
1366 CFSetApplyFunction(mAttributes
, ActivateInputs_set
, NULL
);
1370 CFErrorRef
Transform::ForAllNodes(bool parallel
, bool includeOwningGroup
, Transform::TransformOperation op
)
1372 GroupTransform
*g
= GetRootGroup();
1374 return g
->ForAllNodes(parallel
, includeOwningGroup
, op
);
1380 CFErrorRef
Transform::TraverseTransform(CFMutableSetRef visited
, TransformOperation t
)
1382 return ForAllNodes(true, true, t
);
1385 CFErrorRef
Transform::ExecuteOperation(CFStringRef
&outputAttached
, SecMonitorRef output
, dispatch_queue_t phase2
, dispatch_queue_t phase3
)
1388 // top level groups are special, and don't go through this path.
1392 if (!TransformCanExecute())
1394 // oops, this transform isn't ready to go
1395 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly
, "The transform %@ was not ready for execution.", GetName());
1398 // check to see if required attributes are connected or set
1399 CFIndex i
, numAttributes
= CFSetGetCount(mAttributes
);
1400 transform_attribute
**attributes
= (transform_attribute
**)alloca(numAttributes
* sizeof(transform_attribute
*));
1401 TAGetAll(attributes
);
1402 CFMutableArrayRef still_need
= NULL
;
1403 for(i
= 0; i
< numAttributes
; ++i
) {
1404 transform_attribute
*ta
= attributes
[i
];
1405 if (ta
->required
&& ta
->value
== NULL
&& !ta
->has_incoming_connection
) {
1407 still_need
= CFArrayCreateMutable(NULL
, i
, &kCFTypeArrayCallBacks
);
1409 CFArrayAppendValue(still_need
, ta
->name
);
1413 CFStringRef elist
= CFStringCreateByCombiningStrings(NULL
, still_need
, CFSTR(", "));
1414 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorMissingParameter
, "Can not execute %@, missing required attributes: %@", GetName(), elist
);
1416 CFRelease(still_need
);
1420 // see if we can attach our output here (note mAttributes may have changed)
1421 numAttributes
= CFSetGetCount(mAttributes
);
1422 attributes
= (transform_attribute
**)alloca(numAttributes
* sizeof(transform_attribute
*));
1423 TAGetAll(attributes
);
1424 for (i
= 0; i
< numAttributes
; ++i
)
1426 transform_attribute
*ta
= attributes
[i
];
1427 CFIndex arraySize
= ta
->connections
? CFArrayGetCount(ta
->connections
) : 0;
1428 if (arraySize
== 0 && ta
->requires_outbound_connection
)
1430 if (CFStringCompare(ta
->name
, kSecTransformOutputAttributeName
, 0) == kCFCompareEqualTo
) {
1431 // this is a place where we can hook up our output -- maybe
1434 // oops, we've already done that.
1435 return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput
, "Both %@ and %@ have loose outputs, attach one to something", outputAttached
, ta
->transform
->GetName());
1437 // Delay the connect until after ForAllNodes returns
1438 dispatch_async(phase2
, ^{
1439 SecTransformConnectTransformsInternal(mGroup
->GetCFObject(),
1440 GetCFObject(), kSecTransformOutputAttributeName
,
1441 output
, kSecTransformInputAttributeName
);
1443 outputAttached
= ta
->transform
->GetName();
1445 // activate the attached monitor
1446 Monitor
* m
= (Monitor
*) CoreFoundationHolder::ObjectFromCFType(output
);
1447 m
->mIsActive
= true;
1449 // add the monitor to the output so that it doesn't get activated twice
1451 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly
, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta
->name
, GetName());
1458 // Delay activation until after the Monitor is connected
1459 dispatch_async(phase3
, ^{
1468 void Transform::DoPhase3Activation()
1470 this->mIsActive
= true;
1471 // execution has now truly started ("mIsActive is true")
1472 CFErrorRef initError
= TransformStartingExecution();
1475 // Oops, now execution is about to grind to a halt
1476 this->SendAttribute(AbortAH
, initError
);
1479 dispatch_resume(this->mActivationQueue
);
1480 dispatch_group_async(this->mActivationPending
, this->mActivationQueue
, ^{
1481 dispatch_release(this->mActivationQueue
);
1482 this->mActivationQueue
= NULL
;
1488 // This would be best expressed as a block, but we seem to run into compiler errors
1489 void Transform::phase3Activation()
1491 dispatch_async(this->mDispatchQueue
, ^
1493 DoPhase3Activation();
1498 Boolean
Transform::TransformCanExecute()
1505 CFErrorRef
Transform::TransformStartingExecution()
1512 bool Transform::IsExternalizable()
1517 static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator
, const void *value
) {
1518 if (value
!= NULL
) {
1519 return CFRetain(value
);
1525 static void CFTypeOrNULLRelease(CFAllocatorRef allocator
, const void *value
) {
1526 if (value
!= NULL
) {
1531 static CFStringRef
CFTypeOrNULLCopyDescription (const void *value
) {
1532 if (value
!= NULL
) {
1533 return CFCopyDescription(value
);
1535 return CFSTR("NULL");
1539 static Boolean
CFTypeOrNULLEqual(const void *value1
, const void *value2
) {
1540 if (value1
== NULL
&& value2
== NULL
) {
1543 if (value1
== NULL
|| value2
== NULL
) {
1546 return CFEqual(value1
, value2
);
1551 // Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState
1552 CFDictionaryRef
Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key
)
1554 SecTransformMetaAttributeType types
[] =
1556 kSecTransformMetaAttributeRequired
,
1557 kSecTransformMetaAttributeRequiresOutboundConnection
,
1558 kSecTransformMetaAttributeDeferred
,
1559 kSecTransformMetaAttributeStream
,
1560 kSecTransformMetaAttributeCanCycle
,
1561 kSecTransformMetaAttributeValue
1564 CFIndex i
, cnt
= sizeof(types
)/sizeof(SecTransformMetaAttributeType
);
1565 CFTypeRef values
[cnt
];
1566 CFNumberRef keys
[cnt
];
1569 // NOTE: we save meta attributes that are in their "default" state on purpose because the
1570 // default may change in the future and we definitely want to restore the default values at
1571 // time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8
1572 // default we want to load all old transforms with stream=1, the simplest way to do that is
1573 // to store all values, not just non-default values)
1574 for(i
= 0; i
< cnt
; ++i
)
1576 values
[i
] = GetMetaAttribute(key
, types
[i
]);
1577 int tmp
= (int)types
[i
];
1578 keys
[i
] = CFNumberCreate(NULL
, kCFNumberIntType
, &tmp
);
1581 static CFDictionaryValueCallBacks CFTypeOrNULL
;
1582 static dispatch_once_t once
;
1583 dispatch_block_t b
=
1585 CFTypeOrNULL
.version
= 0;
1586 CFTypeOrNULL
.retain
= CFTypeOrNULLRetain
;
1587 CFTypeOrNULL
.release
= CFTypeOrNULLRelease
;
1588 CFTypeOrNULL
.copyDescription
= CFTypeOrNULLCopyDescription
;
1589 CFTypeOrNULL
.equal
= CFTypeOrNULLEqual
;
1591 dispatch_once(&once
, b
);
1593 CFDictionaryRef ret
= CFDictionaryCreate(NULL
, (const void**)&keys
, (const void**)&values
, cnt
, &kCFTypeDictionaryKeyCallBacks
, &CFTypeOrNULL
);
1595 for(i
= 0; i
< cnt
; ++i
)
1603 // return everything that doesn't have ignore_while_externalizing set
1604 CFDictionaryRef
Transform::CopyState()
1606 CFIndex i
, j
, cnt
= CFSetGetCount(mAttributes
);
1607 transform_attribute
*attrs
[cnt
];
1608 CFStringRef names
[cnt
];
1609 CFDictionaryRef values
[cnt
];
1611 for(i
= j
= 0; i
< cnt
; ++i
)
1613 transform_attribute
*ta
= attrs
[i
];
1614 if (!ta
->ignore_while_externalizing
)
1616 names
[j
] = ta
->name
;
1617 values
[j
++] = GetAHDictForSaveState(ta
->name
);
1621 CFDictionaryRef result
= CFDictionaryCreate(NULL
, (const void**)&names
, (const void**)&values
, j
, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1623 for(i
= j
= 0; i
< cnt
; ++i
)
1625 transform_attribute
*ta
= attrs
[i
];
1626 if (!ta
->ignore_while_externalizing
)
1628 CFRelease(values
[j
++]);
1637 void Transform::RestoreState(CFDictionaryRef state
)
1639 CFIndex i
, cnt
= CFDictionaryGetCount(state
);
1641 **keys
= (const void **)alloca(sizeof(void*)*cnt
),
1642 **values
= (const void **)alloca(sizeof(void*)*cnt
);
1644 CFDictionaryGetKeysAndValues(state
, keys
, values
);
1646 // Open issue -- do we need to do anything to values that are already set, but are not in "state"?
1647 // this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects,
1648 // it only becomes an issue if we add a ResetFromState, or use it internally in that role.
1650 for(i
= 0; i
< cnt
; i
++)
1652 SecTransformAttributeRef ah
= getAH(keys
[i
]);
1659 CFIndex j
, meta_cnt
= CFDictionaryGetCount((CFDictionaryRef
)values
[i
]);
1660 const void **types
= (const void**)alloca(sizeof(void*)*meta_cnt
), **meta_values
= (const void**)alloca(sizeof(void*)*meta_cnt
);
1661 CFDictionaryGetKeysAndValues((CFDictionaryRef
)values
[i
], types
, meta_values
);
1664 for(j
= 0; j
< meta_cnt
; ++j
)
1666 CFNumberGetValue((CFNumberRef
)types
[j
], kCFNumberIntType
, &t
);
1667 if (t
== kSecTransformMetaAttributeValue
)
1669 if (meta_values
[j
]) {
1670 // SendMetaAttribute doesn't activate the callbacks
1671 SetAttribute(ah
, meta_values
[j
]);
1676 CFErrorRef result
= SendMetaAttribute(ah
, (SecTransformMetaAttributeType
)t
, meta_values
[j
]);
1679 CFRelease(result
); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1684 CFErrorRef result
= SendMetaAttribute(ah
, kSecTransformMetaAttributeExternalize
, kCFBooleanTrue
);
1687 CFRelease(result
); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1692 GroupTransform
* Transform::GetRootGroup()
1694 GroupTransform
*g
= mGroup
;
1700 if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
1701 return (GroupTransform
*)this;
1707 CFDictionaryRef
Transform::GetCustomExternalData()
1712 void Transform::SetCustomExternalData(CFDictionaryRef customData
)
1717 CFDictionaryRef
Transform::Externalize(CFErrorRef
* error
)
1721 return (CFDictionaryRef
)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
1724 // make arrays to hold the transforms and the connections
1725 __block CFMutableArrayRef transforms
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1726 __block CFMutableArrayRef connections
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1727 GroupTransform
*root
= GetRootGroup();
1729 CFErrorRef err
= ForAllNodes(false, true, ^(Transform
*t
) {
1731 return t
->ProcessExternalize(transforms
, connections
);
1733 return (CFErrorRef
)NULL
;
1738 // Really? This just seems like a bad idea
1747 // make a dictionary to hold the output
1748 CFMutableDictionaryRef output
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1749 CFDictionaryAddValue(output
, EXTERN_TRANSFORM_TRANSFORM_ARRAY
, transforms
);
1750 CFDictionaryAddValue(output
, EXTERN_TRANSFORM_CONNECTION_ARRAY
, connections
);
1753 CFRelease(connections
);
1754 CFRelease(transforms
);
1759 CFErrorRef
Transform::ProcessExternalize(CFMutableArrayRef transforms
, CFMutableArrayRef connections
)
1761 if (!IsExternalizable()) {
1765 CFDictionaryRef state
= CopyState();
1766 if (state
&& CFGetTypeID(state
) == CFErrorGetTypeID()) {
1767 return (CFErrorRef
)state
;
1770 // make a dictionary to hold the name, type, and state of this node
1771 CFMutableDictionaryRef node
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1772 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_NAME
, GetName());
1774 CFTypeRef type
= CFStringCreateCopy(NULL
, mTypeName
);
1775 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_TYPE
, type
);
1780 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_STATE
, state
);
1784 CFDictionaryRef customItems
= GetCustomExternalData();
1785 if (NULL
!= customItems
)
1787 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY
, customItems
);
1788 CFRelease(customItems
);
1791 // append the resulting dictionary to the node list
1792 CFArrayAppendValue(transforms
, node
);
1795 // now walk the attribute list
1796 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
1797 transform_attribute
*attributes
[numAttributes
];
1798 TAGetAll(attributes
);
1802 // walk the forward links
1803 for (i
= 0; i
< numAttributes
; ++i
)
1805 CFIndex arraySize
= attributes
[i
]->connections
? CFArrayGetCount(attributes
[i
]->connections
) : 0;
1809 for (j
= 0; j
< arraySize
; ++j
)
1811 transform_attribute
*ta
= ah2ta((SecTransformAttributeRef
)CFArrayGetValueAtIndex(attributes
[i
]->connections
, j
));
1813 if (!ta
->transform
->IsExternalizable()) {
1814 // just pretend non-externalizable transforms don't even exist. Don't write out connections, and don't talk to them about externalizing.
1818 // add this forward connection to the array -- make a dictionary
1819 CFMutableDictionaryRef connection
=
1820 CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1822 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_FROM_NAME
, GetName());
1823 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_FROM_ATTRIBUTE
, attributes
[i
]->name
);
1824 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_TO_NAME
, ta
->transform
->GetName());
1825 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_TO_ATTRIBUTE
, ta
->name
);
1827 CFArrayAppendValue(connections
, connection
);
1828 CFRelease(connection
);