1#include <CoreServices/CoreServices.h> 2#include <Block.h> 3#include <libkern/OSAtomic.h> 4#include <syslog.h> 5#include "Transform.h" 6#include "StreamSource.h" 7#include "SingleShotSource.h" 8#include "Monitor.h" 9#include "Utilities.h" 10#include "c++utils.h" 11#include "misc.h" 12#include "SecTransformInternal.h" 13#include "GroupTransform.h" 14#include "GroupTransform.h" 15#include <pthread.h> 16 17static const int kMaxPendingTransactions = 20; 18 19static CFTypeID internalID = _kCFRuntimeNotATypeID; 20 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* 23static unsigned char dispatchQueueToTransformKey; 24 25static char RandomChar() 26{ 27 return arc4random() % 26 + 'A'; // good enough 28} 29 30 31static 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"))); 34} 35 36static CFStringRef AttributeHandleFormat(CFTypeRef ah, CFDictionaryRef dict) { 37 transform_attribute *ta = ah2ta(ah); 38 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), ta->transform->GetName(), ta->name); 39} 40 41static CFStringRef AttributeHandleDebugFormat(CFTypeRef ah) { 42 transform_attribute *ta = ah2ta(ah); 43 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@ (%p)"), ta->transform->GetName(), ta->name, ta); 44} 45 46static void AttributeHandleFinalize(CFTypeRef ah) 47{ 48 transform_attribute *ta = ah2ta(ah); 49 if (!ta) 50 { 51 return; 52 } 53 54 if (ta->transform) 55 { 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); 59 abort(); 60 } 61 62 if (ta->value) 63 { 64 CFRelease(ta->value); 65 } 66 67 // ta->q already released 68 69 if (ta->connections) 70 { 71 CFRelease(ta->connections); 72 } 73 74 if (ta->semaphore) 75 { 76 dispatch_release(ta->semaphore); 77 } 78 79 if (ta->attribute_changed_block) 80 { 81 Block_release(ta->attribute_changed_block); 82 } 83 84 if (ta->attribute_validate_block) 85 { 86 Block_release(ta->attribute_validate_block); 87 } 88 89 free(ta); 90} 91 92 93 94static CFHashCode ah_set_hash(const void *v) { 95 return CFHash(ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v)))->name); 96} 97 98static 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); 100} 101 102CFTypeID transform_attribute::cftype; 103 104SecTransformAttributeRef Transform::makeAH(transform_attribute *ta) { 105 if (ta) { 106 SecTransformAttributeRef ah = _CFRuntimeCreateInstance(NULL, transform_attribute::cftype, sizeof(struct transform_attribute*), NULL); 107 if (!ah) { 108 return NULL; 109 } 110 *(struct transform_attribute **)(1 + (CFRuntimeBase*)ah) = ta; 111 return ah; 112 } else { 113 return NULL; 114 } 115} 116 117static pthread_key_t ah_search_key_slot; 118 119static void destroy_ah_search_key(void *ah) { 120 CFRelease(ah); 121 pthread_setspecific(ah_search_key_slot, NULL); 122} 123 124 125 126SecTransformAttributeRef Transform::getAH(SecTransformStringOrAttributeRef attrib, bool create_ok, bool create_underscore_ok) 127{ 128 if (CFGetTypeID(attrib) == transform_attribute::cftype) 129 { 130 return (SecTransformAttributeRef)attrib; 131 } 132 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; 138 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) { 146 abort(); 147 } 148 149 tasetcb.equal = ah_set_equal; 150 tasetcb.hash = ah_set_hash; 151 tasetcb.copyDescription = ah_set_describe; 152 153 pthread_key_create(&ah_search_key_slot, destroy_ah_search_key); 154 }); 155 156 SecTransformAttributeRef search_for = pthread_getspecific(ah_search_key_slot); 157 if (!search_for) 158 { 159 search_for = makeAH((transform_attribute*)malloc(sizeof(transform_attribute))); 160 if (!search_for) 161 { 162 return NULL; 163 } 164 165 bzero(ah2ta(search_for), sizeof(transform_attribute)); 166 pthread_setspecific(ah_search_key_slot, search_for); 167 } 168 169 if (!mAttributes) 170 { 171 mAttributes = CFSetCreateMutable(NULL, 0, &tasetcb); 172 } 173 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) 177 { 178 if (CFStringGetLength(label) && L'_' == CFStringGetCharacterAtIndex(label, 0) && !create_underscore_ok) 179 { 180 // Attributes with a leading _ belong to the Transform system only, not to random 3rd party transforms. 181 return NULL; 182 } 183 184 transform_attribute *ta = static_cast<transform_attribute *>(malloc(sizeof(transform_attribute))); 185 ah = makeAH(ta); 186 if (!ah) 187 { 188 return NULL; 189 } 190 191 ta->name = CFStringCreateCopy(NULL, label); 192 if (!ta->name) 193 { 194 free(ta); 195 return NULL; 196 } 197 CFIndex cnt = CFSetGetCount(mAttributes); 198 CFSetAddValue(mAttributes, ah); 199 if (CFSetGetCount(mAttributes) != cnt+1) 200 { 201 CFRelease(ta->name); 202 free(ta); 203 return NULL; 204 } 205 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); 210 qnbuf[used] = '\0'; 211 ta->q = dispatch_queue_create((char*)qnbuf, NULL); 212 CFRelease(qname); 213 ta->semaphore = dispatch_semaphore_create(kMaxPendingTransactions); 214 215 216 ta->pushback_state = transform_attribute::pb_empty; 217 ta->pushback_value = NULL; 218 ta->value = NULL; 219 ta->connections = NULL; 220 ta->transform = this; 221 222 dispatch_set_target_queue(ta->q, mDispatchQueue); 223 ta->required = 0; 224 ta->requires_outbound_connection = 0; 225 ta->deferred = 0; 226 ta->stream = 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; 234 } 235 236 return ah; 237} 238 239transform_attribute *Transform::getTA(SecTransformStringOrAttributeRef attrib, bool create_ok) 240{ 241 SecTransformAttributeRef ah = getAH(attrib, create_ok); 242 if (ah) 243 { 244 return ah2ta(ah); 245 } 246 else 247 { 248 return NULL; 249 } 250} 251 252 253 254void 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]); 259 } 260} 261 262 263 264bool Transform::HasNoOutboundConnections() 265{ 266 // make an array big enough to hold all of the attributes 267 CFIndex numAttributes = CFSetGetCount(mAttributes); 268 transform_attribute* attributes[numAttributes]; 269 270 TAGetAll(attributes); 271 272 // check all of the attributes 273 CFIndex i; 274 for (i = 0; i < numAttributes; ++i) 275 { 276 if (attributes[i]->connections && CFArrayGetCount(attributes[i]->connections) != 0) 277 { 278 return false; 279 } 280 } 281 282 return true; 283} 284 285 286 287bool Transform::HasNoInboundConnections() 288{ 289 // make an array big enough to hold all of the attributes 290 CFIndex numAttributes = CFSetGetCount(mAttributes); 291 transform_attribute* attributes[numAttributes]; 292 293 TAGetAll(attributes); 294 295 // check all of the attributes 296 CFIndex i; 297 for (i = 0; i < numAttributes; ++i) 298 { 299 if (attributes[i]->has_incoming_connection) 300 { 301 return false; 302 } 303 } 304 305 return true; 306} 307 308 309 310CFIndex Transform::GetAttributeCount() 311{ 312 return CFSetGetCount(mAttributes); 313} 314 315Transform::Transform(CFStringRef transformType, CFStringRef CFobjectType) : 316 CoreFoundationObject(CFobjectType), 317 mIsActive(false), 318 mIsFinalizing(false), 319 mAlwaysSelfNotify(false), 320 mGroup(NULL), 321 mAbortError(NULL), 322 mTypeName(CFStringCreateCopy(NULL, transformType)) 323{ 324 mAttributes = NULL; 325 mPushedback = NULL; 326 mProcessingPushbacks = FALSE; 327 328 if (internalID == _kCFRuntimeNotATypeID) { 329 (void)SecTransformNoData(); 330 internalID = CoreFoundationObject::FindObjectType(gInternalCFObjectName); 331 } 332 333 // create a name for the transform 334 char rname[10]; 335 unsigned i; 336 for (i = 0; i < sizeof(rname) - 1; ++i) 337 { 338 rname[i] = RandomChar(); 339 } 340 341 rname[i] = 0; 342 343 char *tname = const_cast<char*>(CFStringGetCStringPtr(transformType, kCFStringEncodingUTF8)); 344 if (!tname) { 345 CFIndex sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType), kCFStringEncodingUTF8); 346 tname = static_cast<typeof(tname)>(alloca(sz)); 347 if (tname) { 348 CFStringGetCString(transformType, tname, sz, kCFStringEncodingUTF8); 349 } else { 350 tname = const_cast<char*>("-"); 351 } 352 } 353 354 char* name; 355 asprintf(&name, "%s-%s", rname, tname); 356 357 char *dqName; 358 asprintf(&dqName, "%s-%s", rname, tname); 359 360 char *aqName; 361 asprintf(&aqName, "aq-%s-%s", rname, tname); 362 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(); 371 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); 379 380 free(dqName); 381 free(aqName); 382 383 DebugAH = getAH(kSecTransformDebugAttributeName, true); 384 ah2ta(DebugAH)->ignore_while_externalizing = 1; 385 386 ta = getTA(kSecTransformInputAttributeName, true); 387 ta->required = ta->deferred = ta->stream = 1; 388 ta->allow_external_sets = 0; 389 ta->value = NULL; 390 ta->has_been_deferred = 0; 391 ta = getTA(kSecTransformOutputAttributeName, true); 392 ta->requires_outbound_connection = ta->stream = 1; 393} 394 395static void run_and_release_finalizer(void *finalizer_block) 396{ 397 ((dispatch_block_t)finalizer_block)(); 398 Block_release(finalizer_block); 399} 400 401static void set_dispatch_finalizer(dispatch_object_t object, dispatch_block_t finalizer) 402{ 403 finalizer = Block_copy(finalizer); 404 dispatch_set_context(object, finalizer); 405 dispatch_set_finalizer_f(object, run_and_release_finalizer); 406} 407 408void Transform::FinalizePhase2() 409{ 410 delete this; 411} 412 413void Transform::FinalizeForClang() 414{ 415 CFIndex numAttributes = CFSetGetCount(mAttributes); 416 SecTransformAttributeRef handles[numAttributes]; 417 CFSetGetValues(mAttributes, (const void**)&handles); 418 419 for(CFIndex i = 0; i < numAttributes; ++i) { 420 SecTransformAttributeRef ah = handles[i]; 421 transform_attribute *ta = ah2ta(ah); 422 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; 426 CFRelease(ah); 427 }); 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); 431 } 432 dispatch_release(ta->q); 433 } 434 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); 441 } 442 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 445 FinalizePhase2(); 446 }); 447 dispatch_release(mDispatchQueue); 448 }); 449} 450 451void Transform::Finalize() 452{ 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; }; 457 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(); 461 } else { 462 dispatch_sync(mDispatchQueue, mark_as_finalizing); 463 } 464 465 if (mGroup) { 466 (void)transforms_assume(mGroup->mIsFinalizing); // under retain? 467 mGroup->AddAllChildrenFinalizedCallback(mDispatchQueue, continue_finalization); 468 mGroup->ChildStartedFinalization(this); 469 } else { 470 // a "bare" transform (normally itself a group) still needs to be deconstructed 471 dispatch_async(mDispatchQueue, continue_finalization); 472 } 473} 474 475Transform::~Transform() 476{ 477 CFRelease(mAttributes); 478 if (mAbortError) { 479 CFRelease(mAbortError); 480 mAbortError = NULL; 481 } 482 483 // See if we can catch anything using us after our death 484 mDispatchQueue = (dispatch_queue_t)0xdeadbeef; 485 486 CFRelease(mTypeName); 487 488 if (NULL != mPushedback) 489 { 490 CFRelease(mPushedback); 491 } 492 dispatch_release(mActivationPending); 493} 494 495CFStringRef Transform::GetName() { 496 return (CFStringRef)GetAttribute(kSecTransformTransformName); 497} 498 499CFTypeID Transform::GetCFTypeID() 500{ 501 return CoreFoundationObject::FindObjectType(CFSTR("SecTransform")); 502} 503 504std::string Transform::DebugDescription() 505{ 506 return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName()); 507} 508 509CFErrorRef Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type, CFTypeRef value) 510{ 511 SecTransformAttributeRef ah = getAH(key, true); 512 transform_attribute *ta = ah2ta(ah); 513 switch (type) 514 { 515 case kSecTransformMetaAttributeRequired: 516 ta->required = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; 517 break; 518 519 case kSecTransformMetaAttributeRequiresOutboundConnection: 520 ta->requires_outbound_connection = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; 521 break; 522 523 case kSecTransformMetaAttributeDeferred: 524 ta->deferred = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; 525 break; 526 527 case kSecTransformMetaAttributeStream: 528 ta->stream = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0; 529 break; 530 531 case kSecTransformMetaAttributeHasOutboundConnections: 532 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah); 533 534 case kSecTransformMetaAttributeHasInboundConnection: 535 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah); 536 537 case kSecTransformMetaAttributeCanCycle: 538 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah); 539 540 case kSecTransformMetaAttributeExternalize: 541 ta->ignore_while_externalizing = CFBooleanGetValue((CFBooleanRef)value) ? 0 : 1; 542 break; 543 544 case kSecTransformMetaAttributeValue: 545 return SetAttributeNoCallback(ah, value); 546 547 case kSecTransformMetaAttributeRef: 548 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah); 549 550 case kSecTransformMetaAttributeName: 551 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah); 552 553 default: 554 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set unknown meta attribute #%d to %@ on %@", type, value, key); 555 } 556 557 return NULL; 558} 559 560CFTypeRef Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type) { 561 SecTransformAttributeRef ah = getAH(key, true); 562 transform_attribute *ta = ah2ta(ah); 563 switch (type) { 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: 581 return ah; 582 case kSecTransformMetaAttributeValue: 583 return ta->value; 584 case kSecTransformMetaAttributeName: 585 return ta->name; 586 default: 587 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't get unknown meta attribute #%d from %@", type, key); 588 break; 589 } 590 591 return NULL; 592} 593 594 595 596CFErrorRef Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError) 597{ 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); 604 605 // add the new key and value to the dictionary 606 CFDictionaryAddValue(userInfo, kSecTransformAbortOriginatorKey, GetCFObject()); 607 608 // make a new CFError 609 CFErrorRef newError = CFErrorCreate(NULL, domain, code, userInfo); 610 CFRelease(userInfo); 611 return newError; 612} 613 614// NOTE: If called prior to execution will schedule a later call to AbortAllTransforms 615void Transform::AbortJustThisTransform(CFErrorRef abortErr) 616{ 617 (void)transforms_assume(abortErr); 618 (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey) == this); 619 620 Boolean wasActive = mIsActive; 621 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. 626 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. 631 CFRetain(abortErr); 632 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. 636 637 if (!wasActive) { 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); 645 } 646 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); 651 try_pushbacks(); 652 }; 653 654 if (mIsActive) { 655 // This transform is running, so we use the normal queue (which we are 656 // already executing on) 657 abortAction(); 658 } else { 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); 662 } 663 } else { 664 Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH, abortErr, mAbortError); 665 } 666} 667 668// abort all transforms in the root group & below 669void Transform::AbortAllTransforms(CFTypeRef err) 670{ 671 Debug("%@ set to %@, aborting\n", AbortAH, err); 672 CFErrorRef error = NULL; 673 674 CFTypeRef replacementErr = NULL; 675 676 if (CFGetTypeID(err) != CFErrorGetTypeID()) 677 { 678 replacementErr = err = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "ABORT set to a %@ (%@) not a %@", CFCopyTypeIDDescription(CFGetTypeID(err)), err, CFCopyTypeIDDescription(CFErrorGetTypeID())); 679 } 680 681 error = RefactorErrorToIncludeAbortingTransform((CFErrorRef)err); 682 683 if (replacementErr) 684 { 685 CFRelease(replacementErr); 686 } 687 688 GroupTransform *root = GetRootGroup(); 689 if (root) 690 { 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); 695 }); 696 dispatch_group_notify(all_aborted, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) { 697 CFRelease(error); 698 dispatch_release(all_aborted); 699 }); 700 } 701 else 702 { 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); 707 } 708} 709 710 711 712CFErrorRef Transform::Disconnect(Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey) 713{ 714 //CFTypeRef thisTransform = (SecTransformRef) GetCFObject(); 715 716 // find this transform in the backlinks for the destination 717 CFIndex i; 718 719 // now remove the link in the transform dictionary 720 transform_attribute *src = getTA(myKey, true); 721 SecTransformAttributeRef dst = destinationTransform->getAH(hisKey); 722 723 if (src->connections == NULL) 724 { 725 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Cannot find transform in destination."); 726 } 727 728 CFIndex numConnections = CFArrayGetCount(src->connections); 729 for (i = 0; i < numConnections; ++i) 730 { 731 if (CFArrayGetValueAtIndex(src->connections, i) == dst) 732 { 733 CFArrayRemoveValueAtIndex(src->connections, i); 734 numConnections = CFArrayGetCount(src->connections); 735 } 736 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; 740 } 741 742 if (HasNoInboundConnections() && HasNoOutboundConnections()) 743 { 744 // we have been orphaned, just remove us 745 mGroup->RemoveMemberFromGroup(GetCFObject()); 746 mGroup = NULL; 747 } 748 749 return NULL; 750} 751 752 753 754CFErrorRef Transform::Connect(GroupTransform *group, Transform* destinationTransform, CFStringRef destAttr, CFStringRef srcAttr) 755{ 756 if (group == NULL) 757 { 758 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections without a specific group (do not call with group = NULL)"); 759 return err; 760 } 761 762 GroupTransform *newSourceGroup = mGroup; 763 GroupTransform *newDestinationGroup = destinationTransform->mGroup; 764 765 if (mGroup == NULL || mGroup == this) 766 { 767 newSourceGroup = group; 768 } 769 770 if (destinationTransform->mGroup == NULL || destinationTransform->mGroup == destinationTransform) 771 { 772 newDestinationGroup = group; 773 } 774 775 if (newSourceGroup != newDestinationGroup && mGroup) 776 { 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()); 778 return err; 779 } 780 781 if (!validConnectionPoint(srcAttr)) { 782 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection from non-exported attribute %@ of %@", srcAttr, this->GetName()); 783 return err; 784 } 785 if (!destinationTransform->validConnectionPoint(destAttr)) { 786 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection to non-exported attribute %@ of %@", destAttr, destinationTransform->GetName()); 787 return err; 788 } 789 790 mGroup = newSourceGroup; 791 destinationTransform->mGroup = newDestinationGroup; 792 793 // NOTE: this fails on OOM 794 group->AddMemberToGroup(this->GetCFObject()); 795 group->AddMemberToGroup(destinationTransform->GetCFObject()); 796 797 transform_attribute *src = this->getTA(srcAttr, true); 798 SecTransformAttributeRef dst = destinationTransform->getAH(destAttr); 799 800 if (!src->connections) 801 { 802 src->connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 803 } 804 CFArrayAppendValue(src->connections, dst); 805 806 ah2ta(dst)->has_incoming_connection = 1; 807 808 return NULL; 809} 810 811 812bool Transform::validConnectionPoint(CFStringRef attributeName) 813{ 814 return true; 815} 816 817// NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes 818// SetAttribute eventually calls SetAttributeNoCallback 819CFErrorRef Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key, CFTypeRef value) 820{ 821 SecTransformAttributeRef ah = getAH(key, true); 822 if (!ah) 823 { 824 abort(); 825 } 826 transform_attribute *ta = ah2ta(ah); 827 828 if (ah == AbortAH && value && (mIsActive || !ta->deferred)) 829 { 830 AbortAllTransforms(value); 831 return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress, "Abort started"); 832 } 833 834 bool do_propagate = true; 835 836 if (!ta->has_been_deferred) 837 { 838 bool doNotRetain = false; 839 840 if (value) 841 { 842 CFStringRef name = ta->name; 843 if (CFGetTypeID(value) == CFReadStreamGetTypeID()) 844 { 845 CFTypeRef src = StreamSource::Make((CFReadStreamRef) value, this, name); 846 value = src; 847 do_propagate = false; 848 ta->has_been_deferred = 1; 849 doNotRetain = true; 850 } 851 else if (ta->deferred && !mIsActive) 852 { 853 if (ta->deferred) 854 { 855 Debug("%@ deferred value=%p\n", ah, value); 856 } 857 858 CFTypeRef src = SingleShotSource::Make(value, this, name); 859 ta->has_been_deferred = 1; 860 861 // the old value will be release when Transform::Do terminates 862 863 value = src; 864 do_propagate = false; 865 doNotRetain = true; 866 } 867 else 868 { 869 ta->has_been_deferred = 0; 870 } 871 } 872 873 if (ta->value != value) { 874 if (value && !doNotRetain) { 875 CFRetain(value); 876 } 877 if (ta->value) { 878 CFRelease(ta->value); 879 } 880 } 881 882 ta->value = value; 883 } 884 885 // propagate the changes out to all connections 886 if (ta->connections && mIsActive && do_propagate && !(mAbortError || mIsFinalizing)) 887 { 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; 893 if (NULL != tt) 894 { 895 if (tt->mIsActive) 896 { 897 tt->SetAttribute(ah, value); 898 } 899 else 900 { 901 dispatch_block_t setAttribute = ^{ 902 tt->SetAttribute(ah, value); 903 }; 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 910 if (tt->mIsActive) { 911 setAttribute(); 912 } else { 913 dispatch_async(ah2ta(ah)->transform->mActivationQueue, setAttribute); 914 } 915 }); 916 } 917 } 918 } 919 } 920 921 return NULL; 922} 923 924// external sets normally fail if the transform is running 925CFErrorRef Transform::ExternalSetAttribute(CFTypeRef key, CFTypeRef value) 926{ 927 if (!mIsActive) 928 { 929 return this->SetAttribute(key, value); 930 } 931 else 932 { 933 SecTransformAttributeRef ah = getAH(key, false); 934 if (ah != NULL && ah2ta(ah)->allow_external_sets) 935 { 936 return this->SetAttribute(static_cast<CFTypeRef>(ah), value); 937 } 938 else 939 { 940 return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "%@ can not be set while %@ is executing", ah, this->GetName()); 941 } 942 } 943} 944 945 946// queue up the setting of the key and value 947CFErrorRef Transform::SetAttribute(CFTypeRef key, CFTypeRef value) 948{ 949 if (mAbortError) 950 { 951 return CreateSecTransformErrorRef(kSecTransformErrorAborted, "ABORT has been sent to the transform (%@)", mAbortError); 952 } 953 954 // queue up the setting of the key and value 955 SecTransformAttributeRef ah; 956 if (CFGetTypeID(key) == transform_attribute::cftype) 957 { 958 ah = key; 959 } 960 else if (CFGetTypeID(key) == CFStringGetTypeID()) 961 { 962 ah = getAH(static_cast<CFStringRef>(key)); 963 if (!ah) 964 { 965 return CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute, "Can't set attribute %@ in transform %@", key, GetName()); 966 } 967 } 968 else 969 { 970 return CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key); 971 } 972 973 // Do this after the error check above so we don't leak 974 if (value != NULL) 975 { 976 CFRetain(value); // if we use dispatch_async we need to own the value (the matching release is in the set block) 977 } 978 979 980 transform_attribute *ta = ah2ta(ah); 981 982 dispatch_block_t set = ^{ 983 Do(ah, value); 984 985 dispatch_semaphore_signal(ta->semaphore); 986 987 if (value != NULL) 988 { 989 CFRelease(value); 990 } 991 }; 992 993 994 // when the transform is active, set attributes asynchronously. Otherwise, we are doing 995 // initialization and must wait for the operation to complete. 996 if (mIsActive) 997 { 998 dispatch_async(ta->q, set); 999 } 1000 else 1001 { 1002 dispatch_sync(ta->q, set); 1003 } 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); 1007 } 1008 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) 1011 // 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). 1017 1018 return mAbortError; 1019} 1020 1021CFErrorRef Transform::SendAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value) 1022{ 1023 return SetAttributeNoCallback(key, value); 1024} 1025 1026 1027 1028CFTypeRef Transform::GetAttribute(SecTransformStringOrAttributeRef key) 1029{ 1030 struct transform_attribute *ta = getTA(key, false); 1031 if (ta == NULL || ta->value == NULL) { 1032 return NULL; 1033 } 1034 1035 if (CFGetTypeID(ta->value) == internalID) 1036 { 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(); 1040 } 1041 else 1042 { 1043 return ta->value; 1044 } 1045} 1046 1047CFErrorRef Transform::Pushback(SecTransformAttributeRef ah, CFTypeRef value) 1048{ 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)) 1052 { 1053 CFErrorRef error = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidOperation, CFSTR("Can not pushback new value until old value has been processed")); 1054 SetAttribute(kSecTransformAbortAttributeName, error); 1055 return error; 1056 } 1057 if (value == NULL && ta->pushback_value == NULL && ta->pushback_state == transform_attribute::pb_repush) 1058 { 1059 ta->pushback_state = transform_attribute::pb_presented_once; 1060 } else 1061 { 1062 ta->pushback_state = transform_attribute::pb_value; 1063 } 1064 if (value) 1065 { 1066 CFRetain(value); 1067 } 1068 ta->pushback_value = value; 1069 dispatch_suspend(ta->q); 1070 if (!mPushedback) 1071 { 1072 mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1073 } 1074 CFArrayAppendValue(mPushedback, ah); 1075 return result; 1076} 1077 1078void Transform::try_pushbacks() { 1079 if (!mPushedback || !CFArrayGetCount(mPushedback)) { 1080 mProcessingPushbacks = FALSE; 1081 return; 1082 } 1083 1084 CFArrayRef pb = (CFArrayRef)mPushedback; 1085 mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1086 CFIndex i, n = CFArrayGetCount(pb); 1087 int succeeded = 0; 1088 for(i = 0; i < n; ++i) 1089 { 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; 1095 Do(ah, v); 1096 if (v) 1097 { 1098 CFRelease(v); 1099 } 1100 if (ta->pushback_state == transform_attribute::pb_repush) { 1101 ta->pushback_state = transform_attribute::pb_empty; 1102 succeeded++; 1103 } 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); 1108 } 1109 1110 CFRelease(pb); 1111 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(); }); 1116 } else { 1117 mProcessingPushbacks = FALSE; 1118 } 1119} 1120 1121void Transform::Debug(const char *cfmt, ...) { 1122 CFTypeRef d = ah2ta(DebugAH)->value; 1123 if (d) { 1124 CFWriteStreamRef out = NULL; 1125 if (CFGetTypeID(d) == CFWriteStreamGetTypeID()) { 1126 out = (CFWriteStreamRef)d; 1127 } else { 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); 1134 CFRelease(p); 1135 }); 1136 out = StdErrWriteStream; 1137 } 1138 1139 va_list ap; 1140 va_start(ap, cfmt); 1141 1142 CFStringRef fmt = CFStringCreateWithCString(NULL, cfmt, kCFStringEncodingUTF8); 1143 CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, ap); 1144 CFRelease(fmt); 1145 va_end(ap); 1146 1147 1148 CFIndex sz = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); 1149 sz += 1; 1150 CFIndex used = 0; 1151 unsigned char *buf; 1152 bool needs_free = true; 1153 buf = (unsigned char*)malloc(sz); 1154 if (buf) { 1155 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, '?', FALSE, buf, sz, &used); 1156 } else { 1157 buf = (unsigned char *)"malloc failure during Transform::Debug\n"; 1158 needs_free = false; 1159 } 1160 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)); 1166 }); 1167 1168 dispatch_async(print_q, ^{ 1169 CFWriteStreamWrite(out, buf, used); 1170 if (needs_free) { 1171 free(buf); 1172 } 1173 }); 1174 1175 CFRelease(str); 1176 } 1177} 1178 1179void Transform::Do(SecTransformAttributeRef ah, CFTypeRef value) 1180{ 1181 transform_attribute *ta = ah2ta(ah); 1182 if (ta->pushback_state == transform_attribute::pb_discard) 1183 { 1184 return; 1185 } 1186 (void)transforms_assume(dispatch_get_current_queue() == ((ta->pushback_state == transform_attribute::pb_repush) ? mDispatchQueue : ta->q)); 1187 1188 if (mIsFinalizing) 1189 { 1190 Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value, ah, dispatch_queue_get_label(dispatch_get_current_queue())); 1191 return; 1192 } 1193 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()))) 1201 { 1202 if (value) { 1203 Debug("Ignoring value (%@) sent to %@ during abort\n", value, ah); 1204 } else { 1205 Debug("Ignoring NULL sent to %@ during abort\n", ah); 1206 } 1207 return; 1208 } 1209 1210 if (mIsActive || (mAlwaysSelfNotify && !ta->deferred)) 1211 { 1212 Debug("AttributeChanged: %@ (%s) = %@\n", ah, mIsActive ? "is executing" : "self notify set", value ? value : (CFTypeRef)CFSTR("(NULL)")); 1213 AttributeChanged(ah, value); 1214 } 1215 1216 if (mPushedback && CFArrayGetCount(mPushedback) && !mProcessingPushbacks) 1217 { 1218 Debug("will process pushbacks (%@) later\n", mPushedback); 1219 mProcessingPushbacks = TRUE; 1220 dispatch_async(mDispatchQueue, ^{ try_pushbacks(); }); 1221 } 1222 1223 return; 1224} 1225 1226 1227void Transform::AttributeChanged(CFStringRef name, CFTypeRef value) 1228{ 1229} 1230 1231void Transform::AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value) 1232{ 1233 AttributeChanged(ah2ta(ah)->name, value); 1234} 1235 1236CFArrayRef 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); 1241} 1242 1243CFTypeRef Transform::Execute(dispatch_queue_t deliveryQueue, SecMessageBlock deliveryBlock, CFErrorRef* errorRef) 1244{ 1245 if (!mGroup) 1246 { 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) 1251 { 1252 deliveryBlock(message, error, isFinal); 1253 if (isFinal) 1254 { 1255 dispatch_async(this->mDispatchQueue, ^{ 1256 CFRelease(g); 1257 }); 1258 } 1259 }; 1260 1261 CFTypeRef ret = this->Execute(deliveryQueue, deliveryBlock ? smb : (SecMessageBlock) NULL, errorRef); 1262 1263 if (!deliveryBlock) 1264 { 1265 CFRelease(g); 1266 } 1267 1268 return ret; 1269 } 1270 1271 if (mIsActive) 1272 { 1273 if (errorRef) 1274 { 1275 *errorRef = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform has already executed, it may not be executed again.", GetName()); 1276 } 1277 1278 return NULL; 1279 } 1280 1281 // Do a retain on our parent since we are using it 1282 GroupTransform *rootGroup = GetRootGroup(); 1283 CFRetain(rootGroup->GetCFObject()); 1284 1285 CFTypeRef result = NULL; 1286 1287 CFTypeRef monitorRef = BlockMonitor::Make(deliveryQueue, deliveryBlock); 1288 1289 __block CFStringRef outputAttached = NULL; 1290 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); 1298 }); 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); 1306 1307 if (errorRef) 1308 { 1309 *errorRef = temp; 1310 } 1311 if (temp) { 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); 1318 return NULL; 1319 } 1320 1321 dispatch_group_t initialized = dispatch_group_create(); 1322 rootGroup->ForAllNodesAsync(true, initialized, ^(Transform*t) { 1323 t->Initialize(); 1324 }); 1325 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(); 1333 }); 1334 dispatch_group_leave(activated); 1335 }); 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); 1341 }); 1342 }); 1343 1344 return result; 1345} 1346 1347 1348void Transform::Initialize() 1349{ 1350} 1351 1352static 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); 1356 s->Activate(); 1357 } 1358} 1359 1360void Transform::ActivateInputs() 1361{ 1362 (void)transforms_assume_zero(mIsActive && this != dispatch_get_specific(&dispatchQueueToTransformKey)); 1363 1364 // now run all of the forward links 1365 if (!mIsFinalizing) { 1366 CFSetApplyFunction(mAttributes, ActivateInputs_set, NULL); 1367 } 1368} 1369 1370CFErrorRef Transform::ForAllNodes(bool parallel, bool includeOwningGroup, Transform::TransformOperation op) 1371{ 1372 GroupTransform *g = GetRootGroup(); 1373 if (g) { 1374 return g->ForAllNodes(parallel, includeOwningGroup, op); 1375 } else { 1376 return op(this); 1377 } 1378} 1379 1380CFErrorRef Transform::TraverseTransform(CFMutableSetRef visited, TransformOperation t) 1381{ 1382 return ForAllNodes(true, true, t); 1383} 1384 1385CFErrorRef Transform::ExecuteOperation(CFStringRef &outputAttached, SecMonitorRef output, dispatch_queue_t phase2, dispatch_queue_t phase3) 1386{ 1387 if (!mGroup) { 1388 // top level groups are special, and don't go through this path. 1389 return NULL; 1390 } 1391 1392 if (!TransformCanExecute()) 1393 { 1394 // oops, this transform isn't ready to go 1395 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "The transform %@ was not ready for execution.", GetName()); 1396 } 1397 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) { 1406 if (!still_need) { 1407 still_need = CFArrayCreateMutable(NULL, i, &kCFTypeArrayCallBacks); 1408 } 1409 CFArrayAppendValue(still_need, ta->name); 1410 } 1411 } 1412 if (still_need) { 1413 CFStringRef elist = CFStringCreateByCombiningStrings(NULL, still_need, CFSTR(", ")); 1414 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorMissingParameter, "Can not execute %@, missing required attributes: %@", GetName(), elist); 1415 CFRelease(elist); 1416 CFRelease(still_need); 1417 return err; 1418 } 1419 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) 1425 { 1426 transform_attribute *ta = attributes[i]; 1427 CFIndex arraySize = ta->connections ? CFArrayGetCount(ta->connections) : 0; 1428 if (arraySize == 0 && ta->requires_outbound_connection) 1429 { 1430 if (CFStringCompare(ta->name, kSecTransformOutputAttributeName, 0) == kCFCompareEqualTo) { 1431 // this is a place where we can hook up our output -- maybe 1432 if (outputAttached) 1433 { 1434 // oops, we've already done that. 1435 return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput, "Both %@ and %@ have loose outputs, attach one to something", outputAttached, ta->transform->GetName()); 1436 } 1437 // Delay the connect until after ForAllNodes returns 1438 dispatch_async(phase2, ^{ 1439 SecTransformConnectTransformsInternal(mGroup->GetCFObject(), 1440 GetCFObject(), kSecTransformOutputAttributeName, 1441 output, kSecTransformInputAttributeName); 1442 }); 1443 outputAttached = ta->transform->GetName(); 1444 1445 // activate the attached monitor 1446 Monitor* m = (Monitor*) CoreFoundationHolder::ObjectFromCFType(output); 1447 m->mIsActive = true; 1448 1449 // add the monitor to the output so that it doesn't get activated twice 1450 } else { 1451 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta->name, GetName()); 1452 } 1453 1454 break; 1455 } 1456 } 1457 1458 // Delay activation until after the Monitor is connected 1459 dispatch_async(phase3, ^{ 1460 phase3Activation(); 1461 }); 1462 1463 return NULL; 1464} 1465 1466 1467 1468void Transform::DoPhase3Activation() 1469{ 1470 this->mIsActive = true; 1471 // execution has now truly started ("mIsActive is true") 1472 CFErrorRef initError = TransformStartingExecution(); 1473 if (initError) 1474 { 1475 // Oops, now execution is about to grind to a halt 1476 this->SendAttribute(AbortAH, initError); 1477 } 1478 1479 dispatch_resume(this->mActivationQueue); 1480 dispatch_group_async(this->mActivationPending, this->mActivationQueue, ^{ 1481 dispatch_release(this->mActivationQueue); 1482 this->mActivationQueue = NULL; 1483 }); 1484} 1485 1486 1487 1488// This would be best expressed as a block, but we seem to run into compiler errors 1489void Transform::phase3Activation() 1490{ 1491 dispatch_async(this->mDispatchQueue, ^ 1492 { 1493 DoPhase3Activation(); 1494 }); 1495} 1496 1497 1498Boolean Transform::TransformCanExecute() 1499{ 1500 return true; 1501} 1502 1503 1504 1505CFErrorRef Transform::TransformStartingExecution() 1506{ 1507 return NULL; 1508} 1509 1510 1511 1512bool Transform::IsExternalizable() 1513{ 1514 return true; 1515} 1516 1517static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator, const void *value) { 1518 if (value != NULL) { 1519 return CFRetain(value); 1520 } else { 1521 return value; 1522 } 1523} 1524 1525static void CFTypeOrNULLRelease(CFAllocatorRef allocator, const void *value) { 1526 if (value != NULL) { 1527 CFRelease(value); 1528 } 1529} 1530 1531static CFStringRef CFTypeOrNULLCopyDescription (const void *value) { 1532 if (value != NULL) { 1533 return CFCopyDescription(value); 1534 } else { 1535 return CFSTR("NULL"); 1536 } 1537} 1538 1539static Boolean CFTypeOrNULLEqual(const void *value1, const void *value2) { 1540 if (value1 == NULL && value2 == NULL) { 1541 return TRUE; 1542 } else { 1543 if (value1 == NULL || value2 == NULL) { 1544 return FALSE; 1545 } else { 1546 return CFEqual(value1, value2); 1547 } 1548 } 1549} 1550 1551// Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState 1552CFDictionaryRef Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key) 1553{ 1554 SecTransformMetaAttributeType types[] = 1555 { 1556 kSecTransformMetaAttributeRequired, 1557 kSecTransformMetaAttributeRequiresOutboundConnection, 1558 kSecTransformMetaAttributeDeferred, 1559 kSecTransformMetaAttributeStream, 1560 kSecTransformMetaAttributeCanCycle, 1561 kSecTransformMetaAttributeValue 1562 }; 1563 1564 CFIndex i, cnt = sizeof(types)/sizeof(SecTransformMetaAttributeType); 1565 CFTypeRef values[cnt]; 1566 CFNumberRef keys[cnt]; 1567 key = getAH(key); 1568 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) 1575 { 1576 values[i] = GetMetaAttribute(key, types[i]); 1577 int tmp = (int)types[i]; 1578 keys[i] = CFNumberCreate(NULL, kCFNumberIntType, &tmp); 1579 } 1580 1581 static CFDictionaryValueCallBacks CFTypeOrNULL; 1582 static dispatch_once_t once; 1583 dispatch_block_t b = 1584 ^{ 1585 CFTypeOrNULL.version = 0; 1586 CFTypeOrNULL.retain = CFTypeOrNULLRetain; 1587 CFTypeOrNULL.release = CFTypeOrNULLRelease; 1588 CFTypeOrNULL.copyDescription = CFTypeOrNULLCopyDescription; 1589 CFTypeOrNULL.equal = CFTypeOrNULLEqual; 1590 }; 1591 dispatch_once(&once, b); 1592 1593 CFDictionaryRef ret = CFDictionaryCreate(NULL, (const void**)&keys, (const void**)&values, cnt, &kCFTypeDictionaryKeyCallBacks, &CFTypeOrNULL); 1594 1595 for(i = 0; i < cnt; ++i) 1596 { 1597 CFRelease(keys[i]); 1598 } 1599 1600 return ret; 1601} 1602 1603// return everything that doesn't have ignore_while_externalizing set 1604CFDictionaryRef Transform::CopyState() 1605{ 1606 CFIndex i, j, cnt = CFSetGetCount(mAttributes); 1607 transform_attribute *attrs[cnt]; 1608 CFStringRef names[cnt]; 1609 CFDictionaryRef values[cnt]; 1610 TAGetAll(attrs); 1611 for(i = j = 0; i < cnt; ++i) 1612 { 1613 transform_attribute *ta = attrs[i]; 1614 if (!ta->ignore_while_externalizing) 1615 { 1616 names[j] = ta->name; 1617 values[j++] = GetAHDictForSaveState(ta->name); 1618 } 1619 } 1620 1621 CFDictionaryRef result = CFDictionaryCreate(NULL, (const void**)&names, (const void**)&values, j, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1622 1623 for(i = j = 0; i < cnt; ++i) 1624 { 1625 transform_attribute *ta = attrs[i]; 1626 if (!ta->ignore_while_externalizing) 1627 { 1628 CFRelease(values[j++]); 1629 } 1630 } 1631 1632 return result; 1633} 1634 1635 1636 1637void Transform::RestoreState(CFDictionaryRef state) 1638{ 1639 CFIndex i, cnt = CFDictionaryGetCount(state); 1640 const void 1641 **keys = (const void **)alloca(sizeof(void*)*cnt), 1642 **values = (const void **)alloca(sizeof(void*)*cnt); 1643 1644 CFDictionaryGetKeysAndValues(state, keys, values); 1645 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. 1649 1650 for(i = 0; i < cnt; i++) 1651 { 1652 SecTransformAttributeRef ah = getAH(keys[i]); 1653 1654 if (NULL == ah) 1655 { 1656 continue; 1657 } 1658 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); 1662 1663 int t; 1664 for(j = 0; j < meta_cnt; ++j) 1665 { 1666 CFNumberGetValue((CFNumberRef)types[j], kCFNumberIntType, &t); 1667 if (t == kSecTransformMetaAttributeValue) 1668 { 1669 if (meta_values[j]) { 1670 // SendMetaAttribute doesn't activate the callbacks 1671 SetAttribute(ah, meta_values[j]); 1672 } 1673 } 1674 else 1675 { 1676 CFErrorRef result = SendMetaAttribute(ah, (SecTransformMetaAttributeType)t, meta_values[j]); 1677 if (result) 1678 { 1679 CFRelease(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns 1680 } 1681 } 1682 } 1683 1684 CFErrorRef result = SendMetaAttribute(ah, kSecTransformMetaAttributeExternalize, kCFBooleanTrue); 1685 if (result) 1686 { 1687 CFRelease(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns 1688 } 1689 } 1690} 1691 1692GroupTransform* Transform::GetRootGroup() 1693{ 1694 GroupTransform *g = mGroup; 1695 if (g) { 1696 while (g->mGroup) { 1697 g = g->mGroup; 1698 } 1699 } else { 1700 if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) { 1701 return (GroupTransform *)this; 1702 } 1703 } 1704 return g; 1705} 1706 1707CFDictionaryRef Transform::GetCustomExternalData() 1708{ 1709 return NULL; 1710} 1711 1712void Transform::SetCustomExternalData(CFDictionaryRef customData) 1713{ 1714 return; 1715} 1716 1717CFDictionaryRef Transform::Externalize(CFErrorRef* error) 1718{ 1719 if (mIsActive) 1720 { 1721 return (CFDictionaryRef)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform is executing, you need to externalize it prior to execution", GetName()); 1722 } 1723 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(); 1728 1729 CFErrorRef err = ForAllNodes(false, true, ^(Transform *t) { 1730 if (t != root) { 1731 return t->ProcessExternalize(transforms, connections); 1732 } 1733 return (CFErrorRef)NULL; 1734 }); 1735 1736 if (NULL != err) 1737 { 1738 // Really? This just seems like a bad idea 1739 if (NULL != error) 1740 { 1741 *error = err; 1742 } 1743 return NULL; 1744 1745 } 1746 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); 1751 1752 // clean up 1753 CFRelease(connections); 1754 CFRelease(transforms); 1755 1756 return output; 1757} 1758 1759CFErrorRef Transform::ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections) 1760{ 1761 if (!IsExternalizable()) { 1762 return NULL; 1763 } 1764 1765 CFDictionaryRef state = CopyState(); 1766 if (state && CFGetTypeID(state) == CFErrorGetTypeID()) { 1767 return (CFErrorRef)state; 1768 } 1769 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()); 1773 1774 CFTypeRef type = CFStringCreateCopy(NULL, mTypeName); 1775 CFDictionaryAddValue(node, EXTERN_TRANSFORM_TYPE, type); 1776 CFRelease(type); 1777 1778 if (state != NULL) 1779 { 1780 CFDictionaryAddValue(node, EXTERN_TRANSFORM_STATE, state); 1781 CFRelease(state); 1782 } 1783 1784 CFDictionaryRef customItems = GetCustomExternalData(); 1785 if (NULL != customItems) 1786 { 1787 CFDictionaryAddValue(node, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY, customItems); 1788 CFRelease(customItems); 1789 } 1790 1791 // append the resulting dictionary to the node list 1792 CFArrayAppendValue(transforms, node); 1793 CFRelease(node); 1794 1795 // now walk the attribute list 1796 CFIndex numAttributes = CFSetGetCount(mAttributes); 1797 transform_attribute *attributes[numAttributes]; 1798 TAGetAll(attributes); 1799 1800 CFIndex i; 1801 1802 // walk the forward links 1803 for (i = 0; i < numAttributes; ++i) 1804 { 1805 CFIndex arraySize = attributes[i]->connections ? CFArrayGetCount(attributes[i]->connections) : 0; 1806 if (arraySize != 0) 1807 { 1808 CFIndex j; 1809 for (j = 0; j < arraySize; ++j) 1810 { 1811 transform_attribute *ta = ah2ta((SecTransformAttributeRef)CFArrayGetValueAtIndex(attributes[i]->connections, j)); 1812 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. 1815 continue; 1816 } 1817 1818 // add this forward connection to the array -- make a dictionary 1819 CFMutableDictionaryRef connection = 1820 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1821 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); 1826 1827 CFArrayAppendValue(connections, connection); 1828 CFRelease(connection); 1829 } 1830 } 1831 } 1832 1833 return NULL; 1834} 1835