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"
16 static const int kMaxPendingTransactions
= 20;
18 static CFTypeID internalID
= _kCFRuntimeNotATypeID
;
20 // Use &dispatchQueueToTransformKey as a key to dispatch_get_specific to map from
21 // a transforms master, activation, or any attribute queue to the Transform*
22 static unsigned char dispatchQueueToTransformKey
;
24 static char RandomChar()
26 return arc4random() % 26 + 'A'; // good enough
30 static CFStringRef
ah_set_describe(const void *v
) {
31 transform_attribute
*ta
= ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v
)));
32 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")));
35 static CFStringRef
AttributeHandleFormat(CFTypeRef ah
, CFDictionaryRef dict
) {
36 transform_attribute
*ta
= ah2ta(ah
);
37 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@"), ta
->transform
->GetName(), ta
->name
);
40 static CFStringRef
AttributeHandleDebugFormat(CFTypeRef ah
) {
41 transform_attribute
*ta
= ah2ta(ah
);
42 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@ (%p)"), ta
->transform
->GetName(), ta
->name
, ta
);
45 static void AttributeHandleFinalize(CFTypeRef ah
)
47 transform_attribute
*ta
= ah2ta(ah
);
55 // When we release AH's we clear out the transform pointer, so if we get here with transform!=NULL somebody
56 // has released an AH we are still using and we will crash very very soon (in our code) if we don't abort here
57 syslog(LOG_ERR
, "over release of SecTransformAttributeRef at %p\n", ah
);
66 // ta->q already released
70 CFRelease(ta
->connections
);
75 dispatch_release(ta
->semaphore
);
78 if (ta
->attribute_changed_block
)
80 Block_release(ta
->attribute_changed_block
);
83 if (ta
->attribute_validate_block
)
85 Block_release(ta
->attribute_validate_block
);
93 static CFHashCode
ah_set_hash(const void *v
) {
94 return CFHash(ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v
)))->name
);
97 static Boolean
ah_set_equal(const void *v1
, const void *v2
) {
98 return CFEqual(ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v1
)))->name
, ah2ta(static_cast<SecTransformAttributeRef
>(const_cast<void*>(v2
)))->name
);
101 CFTypeID
transform_attribute::cftype
;
103 SecTransformAttributeRef
Transform::makeAH(transform_attribute
*ta
) {
105 SecTransformAttributeRef ah
= _CFRuntimeCreateInstance(NULL
, transform_attribute::cftype
, sizeof(struct transform_attribute
*), NULL
);
109 *(struct transform_attribute
**)(1 + (CFRuntimeBase
*)ah
) = ta
;
116 static pthread_key_t ah_search_key_slot
;
118 static void destroy_ah_search_key(void *ah
) {
120 pthread_setspecific(ah_search_key_slot
, NULL
);
125 SecTransformAttributeRef
Transform::getAH(SecTransformStringOrAttributeRef attrib
, bool create_ok
, bool create_underscore_ok
)
127 if (CFGetTypeID(attrib
) == transform_attribute::cftype
)
129 return (SecTransformAttributeRef
)attrib
;
132 CFStringRef label
= (CFStringRef
)attrib
;
133 static dispatch_once_t once
= 0;
134 const char *name
= (const char *)"SecTransformAttributeRef";
135 static CFRuntimeClass ahclass
;
136 static CFSetCallBacks tasetcb
;
138 dispatch_once(&once
, ^{
139 ahclass
.className
= name
;
140 ahclass
.copyFormattingDesc
= AttributeHandleFormat
;
141 ahclass
.copyDebugDesc
= AttributeHandleDebugFormat
;
142 ahclass
.finalize
= AttributeHandleFinalize
;
143 transform_attribute::cftype
= _CFRuntimeRegisterClass(&ahclass
);
144 if (transform_attribute::cftype
== _kCFRuntimeNotATypeID
) {
148 tasetcb
.equal
= ah_set_equal
;
149 tasetcb
.hash
= ah_set_hash
;
150 tasetcb
.copyDescription
= ah_set_describe
;
152 pthread_key_create(&ah_search_key_slot
, destroy_ah_search_key
);
155 SecTransformAttributeRef search_for
= pthread_getspecific(ah_search_key_slot
);
158 search_for
= makeAH((transform_attribute
*)malloc(sizeof(transform_attribute
)));
164 bzero(ah2ta(search_for
), sizeof(transform_attribute
));
165 pthread_setspecific(ah_search_key_slot
, search_for
);
170 mAttributes
= CFSetCreateMutable(NULL
, 0, &tasetcb
);
173 ah2ta(search_for
)->name
= label
;
174 SecTransformAttributeRef ah
= static_cast<SecTransformAttributeRef
>(const_cast<void*>(CFSetGetValue(mAttributes
, search_for
)));
175 if (ah
== NULL
&& create_ok
)
177 if (CFStringGetLength(label
) && L
'_' == CFStringGetCharacterAtIndex(label
, 0) && !create_underscore_ok
)
179 // Attributes with a leading _ belong to the Transform system only, not to random 3rd party transforms.
183 transform_attribute
*ta
= static_cast<transform_attribute
*>(malloc(sizeof(transform_attribute
)));
190 ta
->name
= CFStringCreateCopy(NULL
, label
);
196 CFIndex cnt
= CFSetGetCount(mAttributes
);
197 CFSetAddValue(mAttributes
, ah
);
198 if (CFSetGetCount(mAttributes
) != cnt
+1)
205 CFStringRef qname
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s/%@"), dispatch_queue_get_label(this->mDispatchQueue
), label
);
206 CFIndex used
, sz
= 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(qname
), kCFStringEncodingUTF8
);
207 UInt8
*qnbuf
= (UInt8
*)alloca(sz
);
208 CFStringGetBytes(qname
, CFRangeMake(0, CFStringGetLength(qname
)), kCFStringEncodingUTF8
, '?', FALSE
, qnbuf
, sz
, &used
);
210 ta
->q
= dispatch_queue_create((char*)qnbuf
, NULL
);
212 ta
->semaphore
= dispatch_semaphore_create(kMaxPendingTransactions
);
215 ta
->pushback_state
= transform_attribute::pb_empty
;
216 ta
->pushback_value
= NULL
;
218 ta
->connections
= NULL
;
219 ta
->transform
= this;
221 dispatch_set_target_queue(ta
->q
, mDispatchQueue
);
223 ta
->requires_outbound_connection
= 0;
226 ta
->ignore_while_externalizing
= 0;
227 ta
->has_incoming_connection
= 0;
228 ta
->direct_error_handling
= 0;
229 ta
->allow_external_sets
= 0;
230 ta
->has_been_deferred
= 0;
231 ta
->attribute_changed_block
= NULL
;
232 ta
->attribute_validate_block
= NULL
;
238 transform_attribute
*Transform::getTA(SecTransformStringOrAttributeRef attrib
, bool create_ok
)
240 SecTransformAttributeRef ah
= getAH(attrib
, create_ok
);
253 void Transform::TAGetAll(transform_attribute
**attributes
) {
254 CFSetGetValues(mAttributes
, (const void**)attributes
);
255 CFIndex i
, n
= CFSetGetCount(mAttributes
);
256 for(i
= 0; i
< n
; ++i
) {
257 attributes
[i
] = ah2ta(attributes
[i
]);
263 bool Transform::HasNoOutboundConnections()
265 // make an array big enough to hold all of the attributes
266 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
267 transform_attribute
* attributes
[numAttributes
];
269 TAGetAll(attributes
);
271 // check all of the attributes
273 for (i
= 0; i
< numAttributes
; ++i
)
275 if (attributes
[i
]->connections
&& CFArrayGetCount(attributes
[i
]->connections
) != 0)
286 bool Transform::HasNoInboundConnections()
288 // make an array big enough to hold all of the attributes
289 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
290 transform_attribute
* attributes
[numAttributes
];
292 TAGetAll(attributes
);
294 // check all of the attributes
296 for (i
= 0; i
< numAttributes
; ++i
)
298 if (attributes
[i
]->has_incoming_connection
)
309 CFIndex
Transform::GetAttributeCount()
311 return CFSetGetCount(mAttributes
);
314 Transform::Transform(CFStringRef transformType
, CFStringRef CFobjectType
) :
315 CoreFoundationObject(CFobjectType
),
317 mIsFinalizing(false),
318 mAlwaysSelfNotify(false),
321 mTypeName(CFStringCreateCopy(NULL
, transformType
))
325 mProcessingPushbacks
= FALSE
;
327 if (internalID
== _kCFRuntimeNotATypeID
) {
328 (void)SecTransformNoData();
329 internalID
= CoreFoundationObject::FindObjectType(gInternalCFObjectName
);
332 // create a name for the transform
335 for (i
= 0; i
< sizeof(rname
) - 1; ++i
)
337 rname
[i
] = RandomChar();
342 char *tname
= const_cast<char*>(CFStringGetCStringPtr(transformType
, kCFStringEncodingUTF8
));
344 CFIndex sz
= 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType
), kCFStringEncodingUTF8
);
345 tname
= static_cast<typeof(tname
)>(alloca(sz
));
347 CFStringGetCString(transformType
, tname
, sz
, kCFStringEncodingUTF8
);
349 tname
= const_cast<char*>("-");
354 asprintf(&name
, "%s-%s", rname
, tname
);
357 asprintf(&dqName
, "%s-%s", rname
, tname
);
360 asprintf(&aqName
, "aq-%s-%s", rname
, tname
);
362 mDispatchQueue
= dispatch_queue_create(dqName
, NULL
);
363 dispatch_queue_set_specific(mDispatchQueue
, &dispatchQueueToTransformKey
, this, NULL
);
364 // mActivationQueue's job in life is to be suspended until just after this transform is made active.
365 // It's primary use is when a value flowing across a connection isn't sure if the target transform is active yet.
366 mActivationQueue
= dispatch_queue_create(aqName
, NULL
);
367 dispatch_set_target_queue(mActivationQueue
, mDispatchQueue
);
368 dispatch_suspend(mActivationQueue
);
369 mActivationPending
= dispatch_group_create();
371 // set up points for ABORT, DEBUG, INPUT, and OUTPUT
372 AbortAH
= getAH(kSecTransformAbortAttributeName
, true);
373 transform_attribute
*ta
= ah2ta(AbortAH
);
374 ta
->ignore_while_externalizing
= 1;
375 CFStringRef attributeName
= CFStringCreateWithCStringNoCopy(NULL
, name
, 0, kCFAllocatorMalloc
);
376 SetAttributeNoCallback(kSecTransformTransformName
, attributeName
);
377 CFRelease(attributeName
);
382 DebugAH
= getAH(kSecTransformDebugAttributeName
, true);
383 ah2ta(DebugAH
)->ignore_while_externalizing
= 1;
385 ta
= getTA(kSecTransformInputAttributeName
, true);
386 ta
->required
= ta
->deferred
= ta
->stream
= 1;
387 ta
->allow_external_sets
= 0;
389 ta
->has_been_deferred
= 0;
390 ta
= getTA(kSecTransformOutputAttributeName
, true);
391 ta
->requires_outbound_connection
= ta
->stream
= 1;
394 static void run_and_release_finalizer(void *finalizer_block
)
396 ((dispatch_block_t
)finalizer_block
)();
397 Block_release(finalizer_block
);
400 static void set_dispatch_finalizer(dispatch_object_t object
, dispatch_block_t finalizer
)
402 finalizer
= Block_copy(finalizer
);
403 dispatch_set_context(object
, finalizer
);
404 dispatch_set_finalizer_f(object
, run_and_release_finalizer
);
407 void Transform::FinalizePhase2()
412 void Transform::FinalizeForClang()
414 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
415 SecTransformAttributeRef handles
[numAttributes
];
416 CFSetGetValues(mAttributes
, (const void**)&handles
);
418 for(CFIndex i
= 0; i
< numAttributes
; ++i
) {
419 SecTransformAttributeRef ah
= handles
[i
];
420 transform_attribute
*ta
= ah2ta(ah
);
422 set_dispatch_finalizer(ta
->q
, ^{
423 // NOTE: not done until all pending use of the attribute queue has ended AND retain count is zero
424 ta
->transform
= NULL
;
427 // If there is a pending pushback the attribute queue will be suspended, and needs a kick before it can be destructed.
428 if (__sync_bool_compare_and_swap(&ta
->pushback_state
, transform_attribute::pb_value
, transform_attribute::pb_discard
)) {
429 dispatch_resume(ta
->q
);
431 dispatch_release(ta
->q
);
434 // We might be finalizing a transform as it is being activated, make sure that is complete before we do the rest
435 dispatch_group_notify(mActivationPending
, mDispatchQueue
, ^{
436 if (mActivationQueue
!= NULL
) {
437 // 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
438 dispatch_resume(mActivationQueue
);
439 dispatch_release(mActivationQueue
);
442 set_dispatch_finalizer(mDispatchQueue
, ^{
443 // 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
446 dispatch_release(mDispatchQueue
);
450 void Transform::Finalize()
452 // 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
453 // (NOTE: moved block into member function as clang hits an internal error and declines to compile)
454 dispatch_block_t continue_finalization
= ^{ this->FinalizeForClang(); };
455 dispatch_block_t mark_as_finalizing
= ^{ this->mIsFinalizing
= true; };
457 // Mark the transform as "finalizing" so it knows not to propagate values across connections
458 if (this == dispatch_get_specific(&dispatchQueueToTransformKey
)) {
459 mark_as_finalizing();
461 dispatch_sync(mDispatchQueue
, mark_as_finalizing
);
465 (void)transforms_assume(mGroup
->mIsFinalizing
); // under retain?
466 mGroup
->AddAllChildrenFinalizedCallback(mDispatchQueue
, continue_finalization
);
467 mGroup
->ChildStartedFinalization(this);
469 // a "bare" transform (normally itself a group) still needs to be deconstructed
470 dispatch_async(mDispatchQueue
, continue_finalization
);
474 Transform::~Transform()
476 CFRelease(mAttributes
);
478 CFRelease(mAbortError
);
482 // See if we can catch anything using us after our death
483 mDispatchQueue
= (dispatch_queue_t
)0xdeadbeef;
485 CFRelease(mTypeName
);
487 if (NULL
!= mPushedback
)
489 CFRelease(mPushedback
);
491 dispatch_release(mActivationPending
);
494 CFStringRef
Transform::GetName() {
495 return (CFStringRef
)GetAttribute(kSecTransformTransformName
);
498 CFTypeID
Transform::GetCFTypeID()
500 return CoreFoundationObject::FindObjectType(CFSTR("SecTransform"));
503 std::string
Transform::DebugDescription()
505 return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName());
508 CFErrorRef
Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key
, SecTransformMetaAttributeType type
, CFTypeRef value
)
510 SecTransformAttributeRef ah
= getAH(key
, true);
511 transform_attribute
*ta
= ah2ta(ah
);
514 case kSecTransformMetaAttributeRequired
:
515 ta
->required
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
518 case kSecTransformMetaAttributeRequiresOutboundConnection
:
519 ta
->requires_outbound_connection
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
522 case kSecTransformMetaAttributeDeferred
:
523 ta
->deferred
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
526 case kSecTransformMetaAttributeStream
:
527 ta
->stream
= CFBooleanGetValue((CFBooleanRef
)value
) ? 1 : 0;
530 case kSecTransformMetaAttributeHasOutboundConnections
:
531 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah
);
533 case kSecTransformMetaAttributeHasInboundConnection
:
534 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah
);
536 case kSecTransformMetaAttributeCanCycle
:
537 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah
);
539 case kSecTransformMetaAttributeExternalize
:
540 ta
->ignore_while_externalizing
= CFBooleanGetValue((CFBooleanRef
)value
) ? 0 : 1;
543 case kSecTransformMetaAttributeValue
:
544 return SetAttributeNoCallback(ah
, value
);
546 case kSecTransformMetaAttributeRef
:
547 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah
);
549 case kSecTransformMetaAttributeName
:
550 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah
);
553 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't set unknown meta attribute #%d to %@ on %@", type
, value
, key
);
559 CFTypeRef
Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key
, SecTransformMetaAttributeType type
) {
560 SecTransformAttributeRef ah
= getAH(key
, true);
561 transform_attribute
*ta
= ah2ta(ah
);
563 case kSecTransformMetaAttributeRequired
:
564 return (CFTypeRef
)(ta
->required
? kCFBooleanTrue
: kCFBooleanFalse
);
565 case kSecTransformMetaAttributeRequiresOutboundConnection
:
566 return (CFTypeRef
)(ta
->requires_outbound_connection
? kCFBooleanTrue
: kCFBooleanFalse
);
567 case kSecTransformMetaAttributeDeferred
:
568 return (CFTypeRef
)(ta
->deferred
? kCFBooleanTrue
: kCFBooleanFalse
);
569 case kSecTransformMetaAttributeStream
:
570 return (CFTypeRef
)(ta
->stream
? kCFBooleanTrue
: kCFBooleanFalse
);
571 case kSecTransformMetaAttributeHasOutboundConnections
:
572 return (CFTypeRef
)((ta
->connections
&& CFArrayGetCount(ta
->connections
)) ? kCFBooleanTrue
: kCFBooleanFalse
);
573 case kSecTransformMetaAttributeHasInboundConnection
:
574 return (CFTypeRef
)(ta
->has_incoming_connection
? kCFBooleanTrue
: kCFBooleanFalse
);
575 case kSecTransformMetaAttributeCanCycle
:
576 return (CFTypeRef
)kCFBooleanFalse
;
577 case kSecTransformMetaAttributeExternalize
:
578 return (CFTypeRef
)(ta
->ignore_while_externalizing
? kCFBooleanFalse
: kCFBooleanTrue
);
579 case kSecTransformMetaAttributeRef
:
581 case kSecTransformMetaAttributeValue
:
583 case kSecTransformMetaAttributeName
:
586 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Can't get unknown meta attribute #%d from %@", type
, key
);
595 CFErrorRef
Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError
)
597 // pull apart the error
598 CFIndex code
= CFErrorGetCode(sourceError
);
599 CFStringRef domain
= CFErrorGetDomain(sourceError
);
600 CFDictionaryRef oldUserInfo
= CFErrorCopyUserInfo(sourceError
);
601 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutableCopy(NULL
, 0, oldUserInfo
);
602 CFRelease(oldUserInfo
);
604 // add the new key and value to the dictionary
605 CFDictionaryAddValue(userInfo
, kSecTransformAbortOriginatorKey
, GetCFObject());
607 // make a new CFError
608 CFErrorRef newError
= CFErrorCreate(NULL
, domain
, code
, userInfo
);
613 // NOTE: If called prior to execution will schedule a later call to AbortAllTransforms
614 void Transform::AbortJustThisTransform(CFErrorRef abortErr
)
616 (void)transforms_assume(abortErr
);
617 (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey
) == this);
619 Boolean wasActive
= mIsActive
;
621 if (OSAtomicCompareAndSwapPtr(NULL
, abortErr
, (void**)&mAbortError
)) {
622 // send an abort message to the attribute so that it can shut down
623 // note that this bypasses the normal processes. The message sent is a notification
624 // that things aren't working well any more, the transform cannot make any other assumption.
626 // mAbortError is released in the destructor which is triggered (in part)
627 // by the dispatch queue finalizer so we don't need a retain/release of
628 // abortErr for the abortAction block, but we do need to retain it
629 // here to match with the release by the destructor.
632 dispatch_block_t abortAction
= ^{
633 // This actually makes the abort happen, it needs to run on the transform's queue while the
634 // transform is executing.
637 // When this abort was first processed we were not executing, so
638 // additional transforms may have been added to our group (indeed,
639 // we may not have had a group at all), so we need to let everyone
640 // know about the problem. This will end up letting ourself (and
641 // maybe some others) know an additional time, but the CompareAndSwap
642 // prevents that from being an issue.
643 this->AbortAllTransforms(abortErr
);
646 SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle
= getAH(kSecTransformInputAttributeName
, false);
647 // Calling AttributeChanged directly lets an error "skip ahead" of the input queue,
648 // and even execute if the input queue is suspended pending pushback retries.
649 AttributeChanged(inputAttributeHandle
, abortErr
);
654 // This transform is running, so we use the normal queue (which we are
655 // already executing on)
658 // This transform hasn't run yet, do the work on the activation queue
659 // so it happens as soon as the transforms starts executing.
660 dispatch_async(mActivationQueue
, abortAction
);
663 Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH
, abortErr
, mAbortError
);
667 // abort all transforms in the root group & below
668 void Transform::AbortAllTransforms(CFTypeRef err
)
670 Debug("%@ set to %@, aborting\n", AbortAH
, err
);
671 CFErrorRef error
= NULL
;
673 CFTypeRef replacementErr
= NULL
;
675 if (CFGetTypeID(err
) != CFErrorGetTypeID())
677 replacementErr
= err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidType
, "ABORT set to a %@ (%@) not a %@", CFCopyTypeIDDescription(CFGetTypeID(err
)), err
, CFCopyTypeIDDescription(CFErrorGetTypeID()));
680 error
= RefactorErrorToIncludeAbortingTransform((CFErrorRef
)err
);
684 CFRelease(replacementErr
);
687 GroupTransform
*root
= GetRootGroup();
690 // tell everyone in the (root) group to "AbortJustThisTransform"
691 dispatch_group_t all_aborted
= dispatch_group_create();
692 root
->ForAllNodesAsync(false, all_aborted
, ^(Transform
* t
){
693 t
->AbortJustThisTransform(error
);
695 dispatch_group_notify(all_aborted
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH
, 0), ^(void) {
697 dispatch_release(all_aborted
);
702 // We are everyone so we AbortJustThisTransform "directly"
703 // NOTE: this can only happen prior to execution (execution always happens in a group)
704 (void)transforms_assume_zero(mIsActive
);
705 this->AbortJustThisTransform(error
);
711 CFErrorRef
Transform::Disconnect(Transform
* destinationTransform
, CFStringRef myKey
, CFStringRef hisKey
)
713 //CFTypeRef thisTransform = (SecTransformRef) GetCFObject();
715 // find this transform in the backlinks for the destination
718 // now remove the link in the transform dictionary
719 transform_attribute
*src
= getTA(myKey
, true);
720 SecTransformAttributeRef dst
= destinationTransform
->getAH(hisKey
);
722 if (src
->connections
== NULL
)
724 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation
, "Cannot find transform in destination.");
727 CFIndex numConnections
= CFArrayGetCount(src
->connections
);
728 for (i
= 0; i
< numConnections
; ++i
)
730 if (CFArrayGetValueAtIndex(src
->connections
, i
) == dst
)
732 CFArrayRemoveValueAtIndex(src
->connections
, i
);
733 numConnections
= CFArrayGetCount(src
->connections
);
736 // clear the has_incoming_connection bit in the destination. We can do this because inputs can have only one connection.
737 transform_attribute
* dstTA
= ah2ta(dst
);
738 dstTA
->has_incoming_connection
= false;
741 if (HasNoInboundConnections() && HasNoOutboundConnections())
743 // we have been orphaned, just remove us
744 mGroup
->RemoveMemberFromGroup(GetCFObject());
753 CFErrorRef
Transform::Connect(GroupTransform
*group
, Transform
* destinationTransform
, CFStringRef destAttr
, CFStringRef srcAttr
)
757 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make connections without a specific group (do not call with group = NULL)");
761 GroupTransform
*newSourceGroup
= mGroup
;
762 GroupTransform
*newDestinationGroup
= destinationTransform
->mGroup
;
764 if (mGroup
== NULL
|| mGroup
== this)
766 newSourceGroup
= group
;
769 if (destinationTransform
->mGroup
== NULL
|| destinationTransform
->mGroup
== destinationTransform
)
771 newDestinationGroup
= group
;
774 if (newSourceGroup
!= newDestinationGroup
&& mGroup
)
776 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup
->GetName(), destinationTransform
->GetName(), newDestinationGroup
->GetName());
780 if (!validConnectionPoint(srcAttr
)) {
781 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make a connection from non-exported attribute %@ of %@", srcAttr
, this->GetName());
784 if (!destinationTransform
->validConnectionPoint(destAttr
)) {
785 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection
, "Can not make a connection to non-exported attribute %@ of %@", destAttr
, destinationTransform
->GetName());
789 mGroup
= newSourceGroup
;
790 destinationTransform
->mGroup
= newDestinationGroup
;
792 // NOTE: this fails on OOM
793 group
->AddMemberToGroup(this->GetCFObject());
794 group
->AddMemberToGroup(destinationTransform
->GetCFObject());
796 transform_attribute
*src
= this->getTA(srcAttr
, true);
797 SecTransformAttributeRef dst
= destinationTransform
->getAH(destAttr
);
799 if (!src
->connections
)
801 src
->connections
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
803 CFArrayAppendValue(src
->connections
, dst
);
805 ah2ta(dst
)->has_incoming_connection
= 1;
811 bool Transform::validConnectionPoint(CFStringRef attributeName
)
816 // NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes
817 // SetAttribute eventually calls SetAttributeNoCallback
818 CFErrorRef
Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key
, CFTypeRef value
)
820 SecTransformAttributeRef ah
= getAH(key
, true);
825 transform_attribute
*ta
= ah2ta(ah
);
827 if (ah
== AbortAH
&& value
&& (mIsActive
|| !ta
->deferred
))
829 AbortAllTransforms(value
);
830 return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress
, "Abort started");
833 bool do_propagate
= true;
835 if (!ta
->has_been_deferred
)
837 bool doNotRetain
= false;
841 CFStringRef name
= ta
->name
;
842 if (CFGetTypeID(value
) == CFReadStreamGetTypeID())
844 CFTypeRef src
= StreamSource::Make((CFReadStreamRef
) value
, this, name
);
846 do_propagate
= false;
847 ta
->has_been_deferred
= 1;
850 else if (ta
->deferred
&& !mIsActive
)
854 Debug("%@ deferred value=%p\n", ah
, value
);
857 CFTypeRef src
= SingleShotSource::Make(value
, this, name
);
858 ta
->has_been_deferred
= 1;
860 // the old value will be release when Transform::Do terminates
863 do_propagate
= false;
868 ta
->has_been_deferred
= 0;
872 if (ta
->value
!= value
) {
873 if (value
&& !doNotRetain
) {
877 CFRelease(ta
->value
);
884 // propagate the changes out to all connections
885 if (ta
->connections
&& mIsActive
&& do_propagate
&& !(mAbortError
|| mIsFinalizing
))
887 Debug("Propagating from %@ to %@\n", ah
, ta
->connections
);
888 CFIndex i
, numConnections
= CFArrayGetCount(ta
->connections
);
889 for(i
= 0; i
< numConnections
; ++i
) {
890 SecTransformAttributeRef ah
= static_cast<SecTransformAttributeRef
>(const_cast<void *>(CFArrayGetValueAtIndex(ta
->connections
, i
)));
891 Transform
*tt
= ah2ta(ah
)->transform
;
896 tt
->SetAttribute(ah
, value
);
900 dispatch_block_t setAttribute
= ^{
901 tt
->SetAttribute(ah
, value
);
903 // Here the target queue might not be activated yet, we can't
904 // look directly at the other transform's ActivationQueue as
905 // it might activate (or Finalize!) as we look, so just ask
906 // the other transform to deal with it.
907 dispatch_async(ah2ta(ah
)->q
, ^(void) {
908 // This time we are on the right queue to know this is the real deal
912 dispatch_async(ah2ta(ah
)->transform
->mActivationQueue
, setAttribute
);
923 // external sets normally fail if the transform is running
924 CFErrorRef
Transform::ExternalSetAttribute(CFTypeRef key
, CFTypeRef value
)
928 return this->SetAttribute(key
, value
);
932 SecTransformAttributeRef ah
= getAH(key
, false);
933 if (ah
!= NULL
&& ah2ta(ah
)->allow_external_sets
)
935 return this->SetAttribute(static_cast<CFTypeRef
>(ah
), value
);
939 return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "%@ can not be set while %@ is executing", ah
, this->GetName());
945 // queue up the setting of the key and value
946 CFErrorRef
Transform::SetAttribute(CFTypeRef key
, CFTypeRef value
)
950 return CreateSecTransformErrorRef(kSecTransformErrorAborted
, "ABORT has been sent to the transform (%@)", mAbortError
);
953 // queue up the setting of the key and value
954 SecTransformAttributeRef ah
;
955 if (CFGetTypeID(key
) == transform_attribute::cftype
)
959 else if (CFGetTypeID(key
) == CFStringGetTypeID())
961 ah
= getAH(static_cast<CFStringRef
>(key
));
964 return CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute
, "Can't set attribute %@ in transform %@", key
, GetName());
969 return CreateSecTransformErrorRef(kSecTransformErrorInvalidType
, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key
);
972 // Do this after the error check above so we don't leak
975 CFRetain(value
); // if we use dispatch_async we need to own the value (the matching release is in the set block)
979 transform_attribute
*ta
= ah2ta(ah
);
981 dispatch_block_t set
= ^{
984 dispatch_semaphore_signal(ta
->semaphore
);
993 // when the transform is active, set attributes asynchronously. Otherwise, we are doing
994 // initialization and must wait for the operation to complete.
997 dispatch_async(ta
->q
, set
);
1001 dispatch_sync(ta
->q
, set
);
1003 if (dispatch_semaphore_wait(ta
->semaphore
, dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
))) {
1004 Debug("Send from %@ to %@ is still waiting\n", GetName(), ah
);
1005 dispatch_semaphore_wait(ta
->semaphore
, DISPATCH_TIME_FOREVER
);
1008 // Return the best available status (which will be NULL if we haven't aborted, or stated an
1009 // intent to abort when execution starts)
1011 // The value of the ABORT attribute can differ from mAbortError, first if a transform is aborted
1012 // prior to running the general abort mechanic is deferred until execution. Second during
1013 // execution the abort logic avoids most of the normal processing. Third, and most importantly
1014 // during an abort the exact error that gets generated will differ from the value sent to ABORT
1015 // (for example if a non-CFError was sent...plus even if it was a CFError we annotate that error).
1020 CFErrorRef
Transform::SendAttribute(SecTransformStringOrAttributeRef key
, CFTypeRef value
)
1022 return SetAttributeNoCallback(key
, value
);
1027 CFTypeRef
Transform::GetAttribute(SecTransformStringOrAttributeRef key
)
1029 struct transform_attribute
*ta
= getTA(key
, false);
1030 if (ta
== NULL
|| ta
->value
== NULL
) {
1034 if (CFGetTypeID(ta
->value
) == internalID
)
1036 // this is one of our internal objects, so get the value from it
1037 Source
* source
= (Source
*) CoreFoundationHolder::ObjectFromCFType(ta
->value
);
1038 return source
->GetValue();
1046 CFErrorRef
Transform::Pushback(SecTransformAttributeRef ah
, CFTypeRef value
)
1048 CFErrorRef result
= NULL
;
1049 transform_attribute
*ta
= ah2ta(ah
);
1050 if (!(ta
->pushback_state
== transform_attribute::pb_empty
|| ta
->pushback_state
== transform_attribute::pb_repush
))
1052 CFErrorRef error
= fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidOperation
, CFSTR("Can not pushback new value until old value has been processed"));
1053 SetAttribute(kSecTransformAbortAttributeName
, error
);
1056 if (value
== NULL
&& ta
->pushback_value
== NULL
&& ta
->pushback_state
== transform_attribute::pb_repush
)
1058 ta
->pushback_state
= transform_attribute::pb_presented_once
;
1061 ta
->pushback_state
= transform_attribute::pb_value
;
1067 ta
->pushback_value
= value
;
1068 dispatch_suspend(ta
->q
);
1071 mPushedback
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1073 CFArrayAppendValue(mPushedback
, ah
);
1077 void Transform::try_pushbacks() {
1078 if (!mPushedback
|| !CFArrayGetCount(mPushedback
)) {
1079 mProcessingPushbacks
= FALSE
;
1083 CFArrayRef pb
= (CFArrayRef
)mPushedback
;
1084 mPushedback
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1085 CFIndex i
, n
= CFArrayGetCount(pb
);
1087 for(i
= 0; i
< n
; ++i
)
1089 SecTransformAttributeRef ah
= CFArrayGetValueAtIndex(pb
, i
);
1090 transform_attribute
*ta
= ah2ta(ah
);
1091 ta
->pushback_state
= transform_attribute::pb_repush
;
1092 CFTypeRef v
= ta
->pushback_value
;
1093 ta
->pushback_value
= NULL
;
1099 if (ta
->pushback_state
== transform_attribute::pb_repush
) {
1100 ta
->pushback_state
= transform_attribute::pb_empty
;
1103 // NOTE: a successful repush needs the queue unsuspended so it can run.
1104 // A failed repush has suspended the queue an additional time, so we
1105 // still need to resume it.
1106 dispatch_resume(ta
->q
);
1111 if (succeeded
&& CFArrayGetCount(mPushedback
)) {
1112 // some attribute changed while we proceeded the last batch of pushbacks, so any "new" pushbacks are eligible to run again.
1113 // 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.
1114 dispatch_async(mDispatchQueue
, ^{ try_pushbacks(); });
1116 mProcessingPushbacks
= FALSE
;
1120 void Transform::Debug(const char *cfmt
, ...) {
1121 CFTypeRef d
= ah2ta(DebugAH
)->value
;
1123 CFWriteStreamRef out
= NULL
;
1124 if (CFGetTypeID(d
) == CFWriteStreamGetTypeID()) {
1125 out
= (CFWriteStreamRef
)d
;
1127 static dispatch_once_t once
;
1128 static CFWriteStreamRef StdErrWriteStream
;
1129 dispatch_once(&once
, ^{
1130 auto GCC_BUG_WORKAROUND CFURLRef GCC_BUG_WORKAROUND p
= CFURLCreateWithFileSystemPath(NULL
, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle
, FALSE
);
1131 StdErrWriteStream
= CFWriteStreamCreateWithFile(NULL
, p
);
1132 CFWriteStreamOpen(StdErrWriteStream
);
1135 out
= StdErrWriteStream
;
1141 CFStringRef fmt
= CFStringCreateWithCString(NULL
, cfmt
, kCFStringEncodingUTF8
);
1142 CFStringRef str
= CFStringCreateWithFormatAndArguments(NULL
, NULL
, fmt
, ap
);
1147 CFIndex sz
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(str
), kCFStringEncodingUTF8
);
1151 bool needs_free
= true;
1152 buf
= (unsigned char*)malloc(sz
);
1154 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, '?', FALSE
, buf
, sz
, &used
);
1156 buf
= (unsigned char *)"malloc failure during Transform::Debug\n";
1160 static dispatch_once_t once
;
1161 static dispatch_queue_t print_q
;
1162 dispatch_once(&once
, ^{
1163 print_q
= dispatch_queue_create("com.apple.security.debug.print_queue", 0);
1164 dispatch_set_target_queue((dispatch_object_t
)print_q
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW
, 0));
1167 dispatch_async(print_q
, ^{
1168 CFWriteStreamWrite(out
, buf
, used
);
1178 void Transform::Do(SecTransformAttributeRef ah
, CFTypeRef value
)
1180 transform_attribute
*ta
= ah2ta(ah
);
1181 if (ta
->pushback_state
== transform_attribute::pb_discard
)
1185 (void)transforms_assume(dispatch_get_current_queue() == ((ta
->pushback_state
== transform_attribute::pb_repush
) ? mDispatchQueue
: ta
->q
));
1189 Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value
, ah
, dispatch_queue_get_label(dispatch_get_current_queue()));
1193 SetAttributeNoCallback(ah
, value
);
1194 // While an abort is in progress things can get into bad
1195 // states if we allow normal processing so we throw anything
1196 // on the floor except CFErrorRef or NULL vales sent to
1197 // ABORT or INPUT (we need to process them to let the
1198 // transform shut down correctly)
1199 if (mAbortError
&& (!(ah
== this->AbortAH
|| ah
== getTA(CFSTR("INPUT"), true)) && (value
== NULL
|| CFGetTypeID(value
) != CFErrorGetTypeID())))
1202 Debug("Ignoring value (%@) sent to %@ during abort\n", value
, ah
);
1204 Debug("Ignoring NULL sent to %@ during abort\n", ah
);
1209 if (mIsActive
|| (mAlwaysSelfNotify
&& !ta
->deferred
))
1211 Debug("AttributeChanged: %@ (%s) = %@\n", ah
, mIsActive
? "is executing" : "self notify set", value
? value
: (CFTypeRef
)CFSTR("(NULL)"));
1212 AttributeChanged(ah
, value
);
1215 if (mPushedback
&& CFArrayGetCount(mPushedback
) && !mProcessingPushbacks
)
1217 Debug("will process pushbacks (%@) later\n", mPushedback
);
1218 mProcessingPushbacks
= TRUE
;
1219 dispatch_async(mDispatchQueue
, ^{ try_pushbacks(); });
1226 void Transform::AttributeChanged(CFStringRef name
, CFTypeRef value
)
1230 void Transform::AttributeChanged(SecTransformAttributeRef ah
, CFTypeRef value
)
1232 AttributeChanged(ah2ta(ah
)->name
, value
);
1235 CFArrayRef
Transform::GetAllAH() {
1236 CFIndex cnt
= CFSetGetCount(mAttributes
);
1237 const void **values
= (const void **)alloca(sizeof(void*)*cnt
);
1238 CFSetGetValues(mAttributes
, values
);
1239 return CFArrayCreate(NULL
, values
, cnt
, &kCFTypeArrayCallBacks
);
1242 CFTypeRef
Transform::Execute(dispatch_queue_t deliveryQueue
, SecMessageBlock deliveryBlock
, CFErrorRef
* errorRef
)
1246 CFTypeRef g
= GroupTransform::Make();
1247 mGroup
= (GroupTransform
*)CoreFoundationHolder::ObjectFromCFType(g
);
1248 mGroup
->AddMemberToGroup(this->GetCFObject());
1249 SecMessageBlock smb
= ^(CFTypeRef message
, CFErrorRef error
, Boolean isFinal
)
1251 deliveryBlock(message
, error
, isFinal
);
1254 dispatch_async(this->mDispatchQueue
, ^{
1260 CFTypeRef ret
= this->Execute(deliveryQueue
, deliveryBlock
? smb
: (SecMessageBlock
) NULL
, errorRef
);
1274 *errorRef
= CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform has already executed, it may not be executed again.", GetName());
1280 // Do a retain on our parent since we are using it
1281 GroupTransform
*rootGroup
= GetRootGroup();
1282 CFRetain(rootGroup
->GetCFObject());
1284 CFTypeRef result
= NULL
;
1286 CFTypeRef monitorRef
= BlockMonitor::Make(deliveryQueue
, deliveryBlock
);
1288 __block CFStringRef outputAttached
= NULL
;
1290 dispatch_queue_t p2
= dispatch_queue_create("activate phase2", NULL
);
1291 dispatch_queue_t p3
= dispatch_queue_create("activate phase3", NULL
);
1292 dispatch_suspend(p2
);
1293 dispatch_suspend(p3
);
1294 // walk the transform, doing phase1 activating as we go, and queueing phase2 and phase3 work
1295 CFErrorRef temp
= TraverseTransform(NULL
, ^(Transform
*t
){
1296 return t
->ExecuteOperation(outputAttached
, (SecMonitorRef
)monitorRef
, p2
, p3
);
1298 // ExecuteOperation is not called for the outer group, so we need to manually set mISActive for it.
1299 rootGroup
->mIsActive
= true;
1300 rootGroup
->StartingExecutionInGroup();
1301 dispatch_resume(p2
);
1302 dispatch_sync(p2
, ^{ dispatch_resume(p3
); });
1303 dispatch_sync(p3
, ^{ dispatch_release(p2
); });
1304 dispatch_release(p3
);
1311 // It is safe to keep the monitors attached, because it is invalid to try to execute again, BUT
1312 // we do need to release the reference to the group that the monitor would normally release
1313 // when it processes the final message.
1314 CFRelease(rootGroup
->GetCFObject());
1315 CFRelease(monitorRef
);
1316 rootGroup
->StartedExecutionInGroup(false);
1320 dispatch_group_t initialized
= dispatch_group_create();
1321 rootGroup
->ForAllNodesAsync(true, initialized
, ^(Transform
*t
) {
1325 dispatch_group_notify(initialized
, rootGroup
->mDispatchQueue
, ^{
1326 dispatch_release(initialized
);
1327 dispatch_group_t activated
= dispatch_group_create();
1328 dispatch_group_enter(activated
);
1329 dispatch_async(rootGroup
->mDispatchQueue
, ^{
1330 rootGroup
->ForAllNodesAsync(true, activated
, ^(Transform
*t
) {
1331 t
->ActivateInputs();
1333 dispatch_group_leave(activated
);
1335 dispatch_group_notify(activated
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
1336 dispatch_release(activated
);
1337 // once we have been activated (but not before!), the monitor belongs to the group, and we can drop our claim
1338 CFRelease(monitorRef
);
1339 rootGroup
->StartedExecutionInGroup(true);
1347 void Transform::Initialize()
1351 static void ActivateInputs_set(const void *v
, void *unused
) {
1352 transform_attribute
*ta
= static_cast<transform_attribute
*>(ah2ta(const_cast<void *>(v
)));
1353 if (ta
->value
&& internalID
== CFGetTypeID(ta
->value
)) {
1354 Source
* s
= (Source
*) CoreFoundationHolder::ObjectFromCFType(ta
->value
);
1359 void Transform::ActivateInputs()
1361 (void)transforms_assume_zero(mIsActive
&& this != dispatch_get_specific(&dispatchQueueToTransformKey
));
1363 // now run all of the forward links
1364 if (!mIsFinalizing
) {
1365 CFSetApplyFunction(mAttributes
, ActivateInputs_set
, NULL
);
1369 CFErrorRef
Transform::ForAllNodes(bool parallel
, bool includeOwningGroup
, Transform::TransformOperation op
)
1371 GroupTransform
*g
= GetRootGroup();
1373 return g
->ForAllNodes(parallel
, includeOwningGroup
, op
);
1379 CFErrorRef
Transform::TraverseTransform(CFMutableSetRef visited
, TransformOperation t
)
1381 return ForAllNodes(true, true, t
);
1384 CFErrorRef
Transform::ExecuteOperation(CFStringRef
&outputAttached
, SecMonitorRef output
, dispatch_queue_t phase2
, dispatch_queue_t phase3
)
1387 // top level groups are special, and don't go through this path.
1391 if (!TransformCanExecute())
1393 // oops, this transform isn't ready to go
1394 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly
, "The transform %@ was not ready for execution.", GetName());
1397 // check to see if required attributes are connected or set
1398 CFIndex i
, numAttributes
= CFSetGetCount(mAttributes
);
1399 transform_attribute
**attributes
= (transform_attribute
**)alloca(numAttributes
* sizeof(transform_attribute
*));
1400 TAGetAll(attributes
);
1401 CFMutableArrayRef still_need
= NULL
;
1402 for(i
= 0; i
< numAttributes
; ++i
) {
1403 transform_attribute
*ta
= attributes
[i
];
1404 if (ta
->required
&& ta
->value
== NULL
&& !ta
->has_incoming_connection
) {
1406 still_need
= CFArrayCreateMutable(NULL
, i
, &kCFTypeArrayCallBacks
);
1408 CFArrayAppendValue(still_need
, ta
->name
);
1412 CFStringRef elist
= CFStringCreateByCombiningStrings(NULL
, still_need
, CFSTR(", "));
1413 CFErrorRef err
= CreateSecTransformErrorRef(kSecTransformErrorMissingParameter
, "Can not execute %@, missing required attributes: %@", GetName(), elist
);
1415 CFRelease(still_need
);
1419 // see if we can attach our output here (note mAttributes may have changed)
1420 numAttributes
= CFSetGetCount(mAttributes
);
1421 attributes
= (transform_attribute
**)alloca(numAttributes
* sizeof(transform_attribute
*));
1422 TAGetAll(attributes
);
1423 for (i
= 0; i
< numAttributes
; ++i
)
1425 transform_attribute
*ta
= attributes
[i
];
1426 int arraySize
= ta
->connections
? CFArrayGetCount(ta
->connections
) : 0;
1427 if (arraySize
== 0 && ta
->requires_outbound_connection
)
1429 if (CFStringCompare(ta
->name
, kSecTransformOutputAttributeName
, 0) == kCFCompareEqualTo
) {
1430 // this is a place where we can hook up our output -- maybe
1433 // oops, we've already done that.
1434 return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput
, "Both %@ and %@ have loose outputs, attach one to something", outputAttached
, ta
->transform
->GetName());
1436 // Delay the connect until after ForAllNodes returns
1437 dispatch_async(phase2
, ^{
1438 SecTransformConnectTransformsInternal(mGroup
->GetCFObject(),
1439 GetCFObject(), kSecTransformOutputAttributeName
,
1440 output
, kSecTransformInputAttributeName
);
1442 outputAttached
= ta
->transform
->GetName();
1444 // activate the attached monitor
1445 Monitor
* m
= (Monitor
*) CoreFoundationHolder::ObjectFromCFType(output
);
1446 m
->mIsActive
= true;
1448 // add the monitor to the output so that it doesn't get activated twice
1450 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly
, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta
->name
, GetName());
1457 // Delay activation until after the Monitor is connected
1458 dispatch_async(phase3
, ^{
1467 void Transform::DoPhase3Activation()
1469 this->mIsActive
= true;
1470 // execution has now truly started ("mIsActive is true")
1471 CFErrorRef initError
= TransformStartingExecution();
1474 // Oops, now execution is about to grind to a halt
1475 this->SendAttribute(AbortAH
, initError
);
1478 dispatch_resume(this->mActivationQueue
);
1479 dispatch_group_async(this->mActivationPending
, this->mActivationQueue
, ^{
1480 dispatch_release(this->mActivationQueue
);
1481 this->mActivationQueue
= NULL
;
1487 // This would be best expressed as a block, but we seem to run into compiler errors
1488 void Transform::phase3Activation()
1490 dispatch_async(this->mDispatchQueue
, ^
1492 DoPhase3Activation();
1497 Boolean
Transform::TransformCanExecute()
1504 CFErrorRef
Transform::TransformStartingExecution()
1511 bool Transform::IsExternalizable()
1516 static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator
, const void *value
) {
1517 if (value
!= NULL
) {
1518 return CFRetain(value
);
1524 static void CFTypeOrNULLRelease(CFAllocatorRef allocator
, const void *value
) {
1525 if (value
!= NULL
) {
1530 static CFStringRef
CFTypeOrNULLCopyDescription (const void *value
) {
1531 if (value
!= NULL
) {
1532 return CFCopyDescription(value
);
1534 return CFSTR("NULL");
1538 static Boolean
CFTypeOrNULLEqual(const void *value1
, const void *value2
) {
1539 if (value1
== NULL
&& value2
== NULL
) {
1542 if (value1
== NULL
|| value2
== NULL
) {
1545 return CFEqual(value1
, value2
);
1550 CFHashCode
CFTypeOrNULLHash(const void *value
) {
1551 if (value
!= NULL
) {
1552 return CFHash(value
);
1559 // Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState
1560 CFDictionaryRef
Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key
)
1562 SecTransformMetaAttributeType types
[] =
1564 kSecTransformMetaAttributeRequired
,
1565 kSecTransformMetaAttributeRequiresOutboundConnection
,
1566 kSecTransformMetaAttributeDeferred
,
1567 kSecTransformMetaAttributeStream
,
1568 kSecTransformMetaAttributeCanCycle
,
1569 kSecTransformMetaAttributeValue
1572 CFIndex i
, cnt
= sizeof(types
)/sizeof(SecTransformMetaAttributeType
);
1573 CFTypeRef values
[cnt
];
1574 CFNumberRef keys
[cnt
];
1577 // NOTE: we save meta attributes that are in their "default" state on purpose because the
1578 // default may change in the future and we definitely want to restore the default values at
1579 // time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8
1580 // default we want to load all old transforms with stream=1, the simplest way to do that is
1581 // to store all values, not just non-default values)
1582 for(i
= 0; i
< cnt
; ++i
)
1584 values
[i
] = GetMetaAttribute(key
, types
[i
]);
1585 int tmp
= (int)types
[i
];
1586 keys
[i
] = CFNumberCreate(NULL
, kCFNumberIntType
, &tmp
);
1589 static CFDictionaryValueCallBacks CFTypeOrNULL
;
1590 static dispatch_once_t once
;
1591 dispatch_block_t b
=
1593 CFTypeOrNULL
.version
= 0;
1594 CFTypeOrNULL
.retain
= CFTypeOrNULLRetain
;
1595 CFTypeOrNULL
.release
= CFTypeOrNULLRelease
;
1596 CFTypeOrNULL
.copyDescription
= CFTypeOrNULLCopyDescription
;
1597 CFTypeOrNULL
.equal
= CFTypeOrNULLEqual
;
1599 dispatch_once(&once
, b
);
1601 CFDictionaryRef ret
= CFDictionaryCreate(NULL
, (const void**)&keys
, (const void**)&values
, cnt
, &kCFTypeDictionaryKeyCallBacks
, &CFTypeOrNULL
);
1603 for(i
= 0; i
< cnt
; ++i
)
1611 // return everything that doesn't have ignore_while_externalizing set
1612 CFDictionaryRef
Transform::CopyState()
1614 CFIndex i
, j
, cnt
= CFSetGetCount(mAttributes
);
1615 transform_attribute
*attrs
[cnt
];
1616 CFStringRef names
[cnt
];
1617 CFDictionaryRef values
[cnt
];
1619 for(i
= j
= 0; i
< cnt
; ++i
)
1621 transform_attribute
*ta
= attrs
[i
];
1622 if (!ta
->ignore_while_externalizing
)
1624 names
[j
] = ta
->name
;
1625 values
[j
++] = GetAHDictForSaveState(ta
->name
);
1629 CFDictionaryRef result
= CFDictionaryCreate(NULL
, (const void**)&names
, (const void**)&values
, j
, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1631 for(i
= j
= 0; i
< cnt
; ++i
)
1633 transform_attribute
*ta
= attrs
[i
];
1634 if (!ta
->ignore_while_externalizing
)
1636 CFRelease(values
[j
++]);
1645 void Transform::RestoreState(CFDictionaryRef state
)
1647 CFIndex i
, cnt
= CFDictionaryGetCount(state
);
1649 **keys
= (const void **)alloca(sizeof(void*)*cnt
),
1650 **values
= (const void **)alloca(sizeof(void*)*cnt
);
1652 CFDictionaryGetKeysAndValues(state
, keys
, values
);
1654 // Open issue -- do we need to do anything to values that are already set, but are not in "state"?
1655 // this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects,
1656 // it only becomes an issue if we add a ResetFromState, or use it internally in that role.
1658 for(i
= 0; i
< cnt
; i
++)
1660 SecTransformAttributeRef ah
= getAH(keys
[i
]);
1667 CFIndex j
, meta_cnt
= CFDictionaryGetCount((CFDictionaryRef
)values
[i
]);
1668 const void **types
= (const void**)alloca(sizeof(void*)*meta_cnt
), **meta_values
= (const void**)alloca(sizeof(void*)*meta_cnt
);
1669 CFDictionaryGetKeysAndValues((CFDictionaryRef
)values
[i
], types
, meta_values
);
1672 for(j
= 0; j
< meta_cnt
; ++j
)
1674 CFNumberGetValue((CFNumberRef
)types
[j
], kCFNumberIntType
, &t
);
1675 if (t
== kSecTransformMetaAttributeValue
)
1677 if (meta_values
[j
]) {
1678 // SendMetaAttribute doesn't activate the callbacks
1679 SetAttribute(ah
, meta_values
[j
]);
1684 CFErrorRef result
= SendMetaAttribute(ah
, (SecTransformMetaAttributeType
)t
, meta_values
[j
]);
1687 CFRelease(result
); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1692 CFErrorRef result
= SendMetaAttribute(ah
, kSecTransformMetaAttributeExternalize
, kCFBooleanTrue
);
1695 CFRelease(result
); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1700 GroupTransform
* Transform::GetRootGroup()
1702 GroupTransform
*g
= mGroup
;
1708 if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
1709 return (GroupTransform
*)this;
1715 CFDictionaryRef
Transform::GetCustomExternalData()
1720 void Transform::SetCustomExternalData(CFDictionaryRef customData
)
1725 CFDictionaryRef
Transform::Externalize(CFErrorRef
* error
)
1729 return (CFDictionaryRef
)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
1732 // make arrays to hold the transforms and the connections
1733 __block CFMutableArrayRef transforms
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1734 __block CFMutableArrayRef connections
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1735 GroupTransform
*root
= GetRootGroup();
1737 CFErrorRef err
= ForAllNodes(false, true, ^(Transform
*t
) {
1739 return t
->ProcessExternalize(transforms
, connections
);
1741 return (CFErrorRef
)NULL
;
1746 // Really? This just seems like a bad idea
1755 // make a dictionary to hold the output
1756 CFMutableDictionaryRef output
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1757 CFDictionaryAddValue(output
, EXTERN_TRANSFORM_TRANSFORM_ARRAY
, transforms
);
1758 CFDictionaryAddValue(output
, EXTERN_TRANSFORM_CONNECTION_ARRAY
, connections
);
1761 CFRelease(connections
);
1762 CFRelease(transforms
);
1767 CFErrorRef
Transform::ProcessExternalize(CFMutableArrayRef transforms
, CFMutableArrayRef connections
)
1769 if (!IsExternalizable()) {
1773 CFDictionaryRef state
= CopyState();
1774 if (state
&& CFGetTypeID(state
) == CFErrorGetTypeID()) {
1775 return (CFErrorRef
)state
;
1778 // make a dictionary to hold the name, type, and state of this node
1779 CFMutableDictionaryRef node
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1780 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_NAME
, GetName());
1782 CFTypeRef type
= CFStringCreateCopy(NULL
, mTypeName
);
1783 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_TYPE
, type
);
1788 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_STATE
, state
);
1792 CFDictionaryRef customItems
= GetCustomExternalData();
1793 if (NULL
!= customItems
)
1795 CFDictionaryAddValue(node
, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY
, customItems
);
1796 CFRelease(customItems
);
1799 // append the resulting dictionary to the node list
1800 CFArrayAppendValue(transforms
, node
);
1803 // now walk the attribute list
1804 CFIndex numAttributes
= CFSetGetCount(mAttributes
);
1805 transform_attribute
*attributes
[numAttributes
];
1806 TAGetAll(attributes
);
1810 // walk the forward links
1811 for (i
= 0; i
< numAttributes
; ++i
)
1813 int arraySize
= attributes
[i
]->connections
? CFArrayGetCount(attributes
[i
]->connections
) : 0;
1817 for (j
= 0; j
< arraySize
; ++j
)
1819 transform_attribute
*ta
= ah2ta((SecTransformAttributeRef
)CFArrayGetValueAtIndex(attributes
[i
]->connections
, j
));
1821 if (!ta
->transform
->IsExternalizable()) {
1822 // just pretend non-externalizable transforms don't even exist. Don't write out connections, and don't talk to them about externalizing.
1826 // add this forward connection to the array -- make a dictionary
1827 CFMutableDictionaryRef connection
=
1828 CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1830 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_FROM_NAME
, GetName());
1831 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_FROM_ATTRIBUTE
, attributes
[i
]->name
);
1832 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_TO_NAME
, ta
->transform
->GetName());
1833 CFDictionaryAddValue(connection
, EXTERN_TRANSFORM_TO_ATTRIBUTE
, ta
->name
);
1835 CFArrayAppendValue(connections
, connection
);
1836 CFRelease(connection
);