CFPlugIn.c
  1  /*      CFPlugIn.c
  2  	Copyright (c) 1999-2019, Apple Inc. and the Swift project authors
  3   
  4  	Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors
  5  	Licensed under Apache License v2.0 with Runtime Library Exception
  6  	See http://swift.org/LICENSE.txt for license information
  7  	See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  8          Responsibility: Tony Parker
  9  */
 10  
 11  #include "CFBundle_Internal.h"
 12  #include "CFInternal.h"
 13  #include "CFRuntime_Internal.h"
 14  
 15  
 16  // MARK: - Declarations
 17  
 18  static os_log_t _CFBundlePluginLogger(void);
 19  
 20  static _CFPFactoryRef _CFPFactoryCommonCreateLocked(CFAllocatorRef allocator, CFUUIDRef factoryID);
 21  
 22  static _CFPFactoryRef _CFPFactoryFindLocked(CFUUIDRef factoryID, Boolean enabled);
 23  
 24  static CFUUIDRef _CFPFactoryCopyFactoryIDLocked(_CFPFactoryRef factory);
 25  static CFPlugInRef _CFPFactoryCopyPlugInLocked(_CFPFactoryRef factory);
 26  
 27  static void _CFPlugInRegisterFactoryFunctionByNameLocked(CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef functionName);
 28  static void _CFPlugInRegisterPlugInTypeLocked(CFUUIDRef factoryID, CFUUIDRef typeID);
 29  
 30  static void _CFPFactoryDisableLocked(_CFPFactoryRef factory);
 31  static void *__CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(CFPlugInFactoryFunction, CFAllocatorRef, CFUUIDRef) __attribute__((noinline));
 32  
 33  
 34  static void _CFPFactoryAddTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID);
 35  static void _CFPFactoryRemoveTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID);
 36  static Boolean _CFPFactorySupportsTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID);
 37  
 38  /* These methods are called by CFPlugInInstance when an instance is created or destroyed.  If a factory's instance count goes to 0 and the factory has been disabled, the factory is destroyed. */
 39  static void _CFPFactoryAddInstanceLocked(_CFPFactoryRef factory);
 40  static void _CFPFactoryRemoveInstanceLocked(_CFPFactoryRef factory);
 41  
 42  static void _CFPlugInAddPlugInInstanceLocked(CFPlugInRef plugIn);
 43  static void _CFPlugInRemovePlugInInstanceLocked(CFPlugInRef plugIn);
 44  static void _CFPlugInIncrementUnloadPreventionLocked(CFPlugInRef plugIn);
 45  static void _CFPlugInDecrementUnloadPreventionLocked(CFPlugInRef plugIn);
 46  
 47  static void _CFPlugInAddFactoryLocked(CFPlugInRef plugIn, _CFPFactoryRef factory);
 48  static void _CFPlugInRemoveFactoryLocked(CFPlugInRef plugIn, _CFPFactoryRef factory);
 49  
 50  CONST_STRING_DECL(kCFPlugInDynamicRegistrationKey, "CFPlugInDynamicRegistration")
 51  CONST_STRING_DECL(kCFPlugInDynamicRegisterFunctionKey, "CFPlugInDynamicRegisterFunction")
 52  CONST_STRING_DECL(kCFPlugInUnloadFunctionKey, "CFPlugInUnloadFunction")
 53  CONST_STRING_DECL(kCFPlugInFactoriesKey, "CFPlugInFactories")
 54  CONST_STRING_DECL(kCFPlugInTypesKey, "CFPlugInTypes")
 55  
 56  struct __CFPlugInInstance {
 57      CFRuntimeBase _base;
 58      
 59      _CFPFactoryRef factory;
 60      
 61      CFPlugInInstanceGetInterfaceFunction getInterfaceFunction;
 62      CFPlugInInstanceDeallocateInstanceDataFunction deallocateInstanceDataFunction;
 63      
 64  #ifdef _MSC_VER
 65  #pragma warning(push)
 66  #pragma warning(disable : 4200)
 67  #endif //_MSC_VER
 68      uint8_t _instanceData[0];
 69  #ifdef _MSC_VER
 70  #pragma warning(pop)
 71  #endif //_MSC_VER
 72  };
 73  
 74  
 75  struct __CFPFactory {
 76      CFRuntimeBase _base;
 77      
 78      // All protected by CFPlugInGlobalDataLock
 79      CFUUIDRef _uuid;
 80      Boolean _enabled;
 81      char _padding[3];
 82      
 83      CFPlugInFactoryFunction _func;
 84      
 85      CFPlugInRef _plugIn;
 86      CFStringRef _funcName;
 87      
 88      CFMutableArrayRef _types;
 89  };
 90  
 91  // Plugin state is stored in several places:
 92  // 1. The following factories by factory/typeID tables
 93  // 2. The list of supported types in each factory instance
 94  // 3. The enabled flag in each factory instance
 95  // 4. The plugInData inside each bundle instance (except isPlugIn, which is constant after init)
 96  // In order to synchronize all of this, there is one global lock for all of it.
 97  os_unfair_recursive_lock CFPlugInGlobalDataLock = OS_UNFAIR_RECURSIVE_LOCK_INIT;
 98  static CFMutableDictionaryRef _factoriesByFactoryID = NULL; /* Value is _CFPFactoryRef */
 99  static CFMutableDictionaryRef _factoriesByTypeID = NULL; /* Value is array of _CFPFactoryRef */
100  static CFMutableSetRef _plugInsToUnload = NULL;
101  
102  // MARK: - Plugin
103  
104  static os_log_t _CFBundlePluginLogger(void) {
105      static os_log_t _log;
106      static dispatch_once_t onceToken;
107      dispatch_once(&onceToken, ^{
108          _log = os_log_create("com.apple.CFBundle", "plugin");
109      });
110      return _log;
111  }
112  
113  CF_EXPORT void *CFPlugInInstanceCreate(CFAllocatorRef allocator, CFUUIDRef factoryID, CFUUIDRef typeID) {
114      void *result = NULL;
115      CFPlugInFactoryFunction f = NULL;
116  
117      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
118      _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true);
119      if (!factory) {
120          os_log_error(_CFBundlePluginLogger(), "Cannot find factory %{public}@", factoryID);
121      } else {
122          if (!_CFPFactorySupportsTypeLocked(factory, typeID)) {
123              os_log_error(_CFBundlePluginLogger(), "Factory %{public}@ does not support type %{public}@", factoryID, typeID);
124          } else if (factory->_enabled) {
125              if (!factory->_func) {
126                  factory->_func = (CFPlugInFactoryFunction)CFBundleGetFunctionPointerForName(factory->_plugIn, factory->_funcName);
127  
128                  if (!factory->_func) {
129                      os_log_error(_CFBundlePluginLogger(), "Cannot find function pointer %{public}@ for factory %{public}@ in %{public}@", factory->_funcName, factory->_uuid, factory->_plugIn);
130                  }
131              }
132              if (factory->_func) {
133                  f = factory->_func;
134  
135                  // Not every factory comes from a plugin, but if it does, we must prevent unload of the plugin so that the function pointer 'f' remains valid, even if factory->_func is cleared.
136                  if (factory->_plugIn) {
137                      _CFPlugInIncrementUnloadPreventionLocked(factory->_plugIn);
138                  }
139              }
140          } else {
141              os_log_debug(_CFBundlePluginLogger(), "Attempted to create instance, but factory %{public}@ is disabled", factory->_uuid);
142          }
143      }
144      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
145  
146      // Call out to the factory function outside of the lock
147      if (f) {
148          result = __CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(f, allocator, typeID);
149          os_log_debug(_CFBundlePluginLogger(), "Created instance of plugin for factory %{public}@ type %{public}@", factoryID, typeID);
150  
151          os_unfair_recursive_lock_lock(&CFPlugInGlobalDataLock);
152          if (factory->_plugIn) {
153              _CFPlugInDecrementUnloadPreventionLocked(factory->_plugIn);
154          }
155          os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
156      }
157  
158      return result;
159  }
160  
161  /* ===================== Registering factories and types ===================== */
162  /* For plugIn writers who must dynamically register things. */
163  /* Functions to register factory functions and to associate factories with types. */
164  
165  CF_EXPORT Boolean CFPlugInRegisterFactoryFunction(CFUUIDRef factoryID, CFPlugInFactoryFunction func) {
166      // Create factories without plugIns from default allocator
167      // MF:!!! Should probably check that this worked, and maybe do some pre-checking to see if it already exists
168      
169      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
170  
171      _CFPFactoryRef factory = _CFPFactoryCommonCreateLocked(kCFAllocatorSystemDefault, factoryID);
172      factory->_func = func;
173      factory->_plugIn = NULL;
174      factory->_funcName = NULL;
175      
176      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
177  
178      return true;
179  }
180  
181  CF_EXPORT Boolean CFPlugInRegisterFactoryFunctionByName(CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef functionName) {
182      // Create factories with plugIns from plugIn's allocator
183      // MF:!!! Should probably check that this worked, and maybe do some pre-checking to see if it already exists
184      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
185      _CFPlugInRegisterFactoryFunctionByNameLocked(factoryID, plugIn, functionName);
186      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
187  
188      return true;
189  }
190  
191  static void _CFPlugInRegisterFactoryFunctionByNameLocked(CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef functionName) {
192      _CFPFactoryRef factory = _CFPFactoryCommonCreateLocked(kCFAllocatorSystemDefault, factoryID);
193      factory->_func = NULL;
194      factory->_plugIn = (CFPlugInRef)CFRetain(plugIn);
195      if (plugIn) _CFPlugInAddFactoryLocked(plugIn, factory);
196      factory->_funcName = (functionName ? (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, functionName) : NULL);
197  }
198  
199  
200  CF_EXPORT Boolean CFPlugInUnregisterFactory(CFUUIDRef factoryID) {
201      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
202  
203      _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true);
204      
205      if (!factory) {
206          /* MF:!!! Error.  No factory registered for this ID. */
207          os_log_error(_CFBundlePluginLogger(), "UnregisterFactory: No factory registered for id %{public}@", factoryID);
208      } else {
209          _CFPFactoryDisableLocked(factory);
210      }
211      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
212  
213      return true;
214  }
215  
216  CF_EXPORT Boolean CFPlugInRegisterPlugInType(CFUUIDRef factoryID, CFUUIDRef typeID) {
217      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
218      _CFPlugInRegisterPlugInTypeLocked(factoryID, typeID);
219      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
220  
221      return true;
222  }
223  
224  static void _CFPlugInRegisterPlugInTypeLocked(CFUUIDRef factoryID, CFUUIDRef typeID) {
225      _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true);
226      
227      if (!factory) {
228          /* MF:!!! Error.  Factory must be registered (and not disabled) before types can be associated with it. */
229          os_log_error(_CFBundlePluginLogger(), "RegisterPlugInType: No factory registered for id %{public}@", factoryID);
230      } else {
231          _CFPFactoryAddTypeLocked(factory, typeID);
232      }
233  }
234  
235  
236  CF_EXPORT Boolean CFPlugInUnregisterPlugInType(CFUUIDRef factoryID, CFUUIDRef typeID) {
237      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
238  
239      _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true);
240  
241      if (!factory) {
242          /* MF:!!! Error.  Could not find factory. */
243          os_log_error(_CFBundlePluginLogger(), "UnregisterPlugInType: No factory registered for id %{public}@ type %{public}@", factoryID, typeID);
244      } else {
245          _CFPFactoryRemoveTypeLocked(factory, typeID);
246      }
247      
248      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
249  
250      return true;
251  }
252  
253  
254  /* ================= Registering instances ================= */
255  /* When a new instance of a type is created, the instance is responsible for registering itself with the factory that created it and unregistering when it deallocates. */
256  /* This means that an instance must keep track of the CFUUIDRef of the factory that created it so it can unregister when it goes away. */
257  
258  CF_EXPORT void CFPlugInAddInstanceForFactory(CFUUIDRef factoryID) {
259      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
260  
261      _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true);
262  
263      if (!factory) {
264          /* MF:!!! Error.  Could not find factory. */
265          os_log_error(_CFBundlePluginLogger(), "AddInstanceForFactory: No factory registered for id %{public}@", factoryID);
266      } else {
267          _CFPFactoryAddInstanceLocked(factory);
268          os_log_debug(_CFBundlePluginLogger(), "AddInstanceForFactory: Added instance on %p for %{public}@", factory, factoryID);
269      }
270      
271      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
272  }
273  
274  CF_EXPORT void CFPlugInRemoveInstanceForFactory(CFUUIDRef factoryID) {
275      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
276  
277      _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true);
278  
279      if (!factory) {
280          /* MF:!!! Error.  Could not find factory. */
281          os_log_error(_CFBundlePluginLogger(), "RemoveInstanceForFactory: No factory registered for id %{public}@", factoryID);
282      } else {
283          _CFPFactoryRemoveInstanceLocked(factory);
284          os_log_debug(_CFBundlePluginLogger(), "RemoveInstanceForFactory: Removed instance on %p for %{public}@", factory, factoryID);
285      }
286      
287      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
288  }
289  
290  // MARK: Plugin - Unloading
291  
292  static void _CFPlugInScheduleForUnloading(CFBundleRef bundle) {
293      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
294      if (!_plugInsToUnload) {
295          CFSetCallBacks nonRetainingCallbacks = kCFTypeSetCallBacks;
296          nonRetainingCallbacks.retain = NULL;
297          nonRetainingCallbacks.release = NULL;
298          _plugInsToUnload = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingCallbacks);
299      }
300      CFSetAddValue(_plugInsToUnload, bundle);
301      os_log_debug(_CFBundlePluginLogger(), "PlugIn %{public}@ is now scheduled for unloading", bundle);
302      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
303  }
304  
305  CF_PRIVATE void _CFPlugInUnscheduleForUnloading(CFBundleRef bundle) {
306      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
307      if (_plugInsToUnload) CFSetRemoveValue(_plugInsToUnload, bundle);
308      os_log_debug(_CFBundlePluginLogger(), "PlugIn %{public}@ is now unscheduled for unloading", bundle);
309      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
310  }
311  
312  CF_PRIVATE void _CFPlugInUnloadScheduledPlugIns(void) {
313      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
314      if (_plugInsToUnload) {
315          CFIndex i, c = CFSetGetCount(_plugInsToUnload);
316          if (c > 0) {
317              CFBundleRef *unloadThese = (CFBundleRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFBundleRef) * c, 0);
318              CFSetGetValues(_plugInsToUnload, (const void **)unloadThese);
319              for (i = 0; i < c; i++) {
320                  // This will cause them to be removed from the set.  (Which is why we copied all the values out of the set up front.)
321                  CFBundleRef unloadMe = unloadThese[i];
322                  
323                  // If its unloadPreventionCount is > 0 then leave it in the set and unload it the next time someone asks
324                  if (__CFBundleGetPlugInData(unloadMe)->_unloadPreventionCount == 0) {
325                      os_log_debug(_CFBundlePluginLogger(), "PlugIn %{public}@ is about to be unloaded", unloadMe);
326                      _CFBundleUnloadExecutable(unloadMe, true);
327                  }
328              }
329              CFAllocatorDeallocate(kCFAllocatorSystemDefault, unloadThese);
330          }
331      }
332      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
333  }
334  
335  // MARK: Plugin - Internals
336  
337  static void _searchForDummyUUID(const void *key, const void *val, void *context) {
338      Boolean *found = (Boolean *)context;
339      if (*found) {
340          // No need to continue searching here
341          return;
342      }
343      
344      CFStringRef factoryIDStr = (CFStringRef)key;
345      if (CFGetTypeID(factoryIDStr) != CFStringGetTypeID()) {
346          // Factory ID is not a string, skip this entry
347          return;
348      }
349      
350      if (CFStringCompare(factoryIDStr, CFSTR("00000000-0000-0000-0000-000000000000"), 0) == kCFCompareEqualTo) {
351          // This is a dummy UUID. Don't count this as a plugin if it uses the dummy function name too.
352          CFStringRef factoryFunctionStr = (CFStringRef)val;
353          if (factoryFunctionStr && CFGetTypeID(factoryFunctionStr) == CFStringGetTypeID()) {
354              if (CFStringCompare(factoryFunctionStr, CFSTR("MyFactoryFunction"), 0) == kCFCompareEqualTo) {
355                  *found = true;
356              }
357          }
358      }
359  }
360  
361  static void _searchForExistingFactoryLocked(const void *key, const void *val, void *context) {
362      CFBundleRef *found = (CFBundleRef *)context;
363      if (*found) {
364          // No need to continue searching here
365          return;
366      }
367      
368      CFStringRef factoryIDStr = (CFStringRef)key;
369      CFUUIDRef factoryID = (CFGetTypeID(factoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, factoryIDStr) : NULL;
370      if (!factoryID) factoryID = (CFUUIDRef)CFRetain(factoryIDStr);
371      
372      // Match any factory, not just enabled ones
373      _CFPFactoryRef existing = _CFPFactoryFindLocked(factoryID, false);
374      if (existing) {
375          *found = (CFBundleRef)CFRetain(existing->_plugIn);
376      }
377      
378      if (factoryID) CFRelease(factoryID);
379  }
380  
381  static void _registerFactoryLocked(const void *key, const void *val, void *context) {
382      CFStringRef factoryIDStr = (CFStringRef)key;
383      CFStringRef factoryFuncStr = (CFStringRef)val;
384      CFBundleRef bundle = (CFBundleRef)context;
385      CFUUIDRef factoryID = (CFGetTypeID(factoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, factoryIDStr) : NULL;
386      if (!factoryID) factoryID = (CFUUIDRef)CFRetain(factoryIDStr);
387      if (CFGetTypeID(factoryFuncStr) != CFStringGetTypeID() || CFStringGetLength(factoryFuncStr) <= 0) factoryFuncStr = NULL;
388      
389      os_log_debug(_CFBundlePluginLogger(), "Registering static factory %{public}@ %{public}@ bundle %{public}p", factoryID, factoryFuncStr ?: CFSTR("<no func>"), bundle);
390      
391      _CFPlugInRegisterFactoryFunctionByNameLocked(factoryID, bundle, factoryFuncStr);
392      if (factoryID) CFRelease(factoryID);
393  }
394  
395  static void _registerTypeLocked(const void *key, const void *val, void *context) {
396      CFStringRef typeIDStr = (CFStringRef)key;
397      CFArrayRef factoryIDStrArray = (CFArrayRef)val;
398      CFBundleRef bundle = (CFBundleRef)context;
399      SInt32 i, c = (CFGetTypeID(factoryIDStrArray) == CFArrayGetTypeID()) ? CFArrayGetCount(factoryIDStrArray) : 0;
400      CFStringRef curFactoryIDStr;
401      CFUUIDRef typeID = (CFGetTypeID(typeIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, typeIDStr) : NULL;
402      CFUUIDRef curFactoryID;
403      if (!typeID) typeID = (CFUUIDRef)CFRetain(typeIDStr);
404      if (0 == c && CFGetTypeID(factoryIDStrArray) != CFArrayGetTypeID()) {
405          curFactoryIDStr = (CFStringRef)val;
406          curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL;
407          if (!curFactoryID) curFactoryID = (CFUUIDRef)CFRetain(curFactoryIDStr);
408          os_log_debug(_CFBundlePluginLogger(), "Registering factory %{public}@ type %{public}@", curFactoryID, typeID);
409          _CFPlugInRegisterPlugInTypeLocked(curFactoryID, typeID);
410          if (curFactoryID) CFRelease(curFactoryID);
411      } else for (i = 0; i < c; i++) {
412          curFactoryIDStr = (CFStringRef)CFArrayGetValueAtIndex(factoryIDStrArray, i);
413          curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL;
414          if (!curFactoryID) curFactoryID = (CFUUIDRef)CFRetain(curFactoryIDStr);
415          os_log_debug(_CFBundlePluginLogger(), "Registering factory %{public}@ type %{public}@", curFactoryID, typeID);
416          _CFPlugInRegisterPlugInTypeLocked(curFactoryID, typeID);
417          if (curFactoryID) CFRelease(curFactoryID);
418      }
419      if (typeID) CFRelease(typeID);
420  }
421  
422  // Returns false if we found another plugin with the same factory ID.
423  // Important: Do not call out to user code from here, as it is called with the global bundle lock taken. The lock ordering must be:
424  //  CFBundleGlobalDataLock -> CFPlugInGlobalDataLock
425  // PlugIn lock is recursive but the bundle lock is not
426  CF_PRIVATE Boolean _CFBundleInitPlugIn(CFBundleRef bundle, CFDictionaryRef infoDict, CFBundleRef *existingPlugIn) {
427      CFArrayCallBacks _pluginFactoryArrayCallbacks = {0, NULL, NULL, NULL, NULL};
428      Boolean doDynamicReg = false;
429      CFDictionaryRef factoryDict;
430      CFDictionaryRef typeDict;
431      CFStringRef tempStr;
432      
433      if (!infoDict) return true;
434      
435      factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey);
436      if (factoryDict && CFGetTypeID(factoryDict) != CFDictionaryGetTypeID()) factoryDict = NULL;
437      tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey);
438      if (tempStr && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) doDynamicReg = true;
439      if (!factoryDict && !doDynamicReg) return true;  // This is not a plug-in.
440      
441      // Search for placeholder UUIDs (all zero)
442      Boolean foundDummy = false;
443      if (factoryDict) CFDictionaryApplyFunction(factoryDict, _searchForDummyUUID, &foundDummy);
444      if (foundDummy) {
445          // Not a plugin. This combination seems to be part of a template, and is often left in Info.plists without much consideration.
446          os_log_debug(_CFBundlePluginLogger(), "Bundle %{public}@ contains a factory UUID of 00000000-0000-0000-0000-000000000000 with function 'MyFactoryFunction'. This bundle is not a valid plugin.", bundle);
447          return true;
448      }
449      
450      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
451  
452      if (__CFBundleGetPlugInData(bundle)->_registeredFactory) {
453          // We already registered - don't do it again
454          os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
455          return true;
456      }
457      
458      // Look for existing plugins with this factory ID
459      CFBundleRef found = NULL;
460      if (factoryDict) CFDictionaryApplyFunction(factoryDict, _searchForExistingFactoryLocked, &found);
461      if (found) {
462          if (existingPlugIn) {
463              *existingPlugIn = found;
464          }
465          os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
466          return false;
467      }
468  
469      /* loadOnDemand is true by default if the plugIn does not do dynamic registration.  It is false, by default if it does do dynamic registration.  The dynamic register function can set this. */
470      __CFBundleGetPlugInData(bundle)->_isPlugIn = true;
471      __CFBundleGetPlugInData(bundle)->_loadOnDemand = true;
472      __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = false;
473      // It is the responsibility of the caller of this function to call _CFPlugInHandleDynamicRegistration once we are out of critical sections
474      __CFBundleGetPlugInData(bundle)->_needsDynamicRegistration = doDynamicReg;
475      __CFBundleGetPlugInData(bundle)->_instanceCount = 0;
476      __CFBundleGetPlugInData(bundle)->_unloadPreventionCount = 0;
477      __CFBundleGetPlugInData(bundle)->_registeredFactory = true;
478      
479      __CFBundleGetPlugInData(bundle)->_factories = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &_pluginFactoryArrayCallbacks);
480      
481      /* Now do the registration */
482      
483      /* First do static registrations, if any. */
484      if (factoryDict) CFDictionaryApplyFunction(factoryDict, _registerFactoryLocked, bundle);
485      typeDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInTypesKey);
486      if (typeDict && CFGetTypeID(typeDict) != CFDictionaryGetTypeID()) typeDict = NULL;
487      if (typeDict) CFDictionaryApplyFunction(typeDict, _registerTypeLocked, bundle);
488      
489      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
490      
491      return true;
492  }
493  
494  static void __CFPLUGIN_IS_CALLING_OUT_TO_A_DYNAMIC_REGISTRATION_FUNCTION__(CFPlugInDynamicRegisterFunction f, CFBundleRef bundle) __attribute__((noinline));
495  
496  static void __CFPLUGIN_IS_CALLING_OUT_TO_A_DYNAMIC_REGISTRATION_FUNCTION__(CFPlugInDynamicRegisterFunction f, CFBundleRef bundle) {
497      f(bundle);
498      __asm __volatile__(""); // thwart tail-call optimization
499  }
500  
501  CF_PRIVATE void _CFPlugInHandleDynamicRegistration(CFBundleRef bundle) {
502      _CFPlugInData *plugIn = __CFBundleGetPlugInData(bundle);
503      
504      // In order to proceed, it must be a plugin, loaded, and need dynamic registration
505      if (!(plugIn->_isPlugIn && CFBundleIsExecutableLoaded(bundle) && plugIn->_needsDynamicRegistration)) {
506          return;
507      }
508      
509      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
510  
511      if (plugIn->_isDoingDynamicRegistration) {
512          os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
513          return;
514      }
515      
516      plugIn->_needsDynamicRegistration = false;
517      CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
518      CFStringRef tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegisterFunctionKey);
519      if (!tempStr || CFGetTypeID(tempStr) != CFStringGetTypeID() || CFStringGetLength(tempStr) <= 0) tempStr = CFSTR("CFPlugInDynamicRegister");
520      plugIn->_loadOnDemand = false;
521      
522      plugIn->_isDoingDynamicRegistration = true;
523      
524      CFPlugInDynamicRegisterFunction func = (CFPlugInDynamicRegisterFunction)CFBundleGetFunctionPointerForName(bundle, tempStr);
525      if (func) {
526          __CFPLUGIN_IS_CALLING_OUT_TO_A_DYNAMIC_REGISTRATION_FUNCTION__(func, bundle);
527      }
528      
529      plugIn->_isDoingDynamicRegistration = false;
530      
531      if (plugIn->_loadOnDemand && plugIn->_instanceCount == 0) CFBundleUnloadExecutable(bundle);   // Unload now if we can/should.
532      
533      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
534  }
535  
536  CF_PRIVATE void _CFBundleDeallocatePlugIn(CFBundleRef bundle) {
537      _CFPlugInData *plugIn = __CFBundleGetPlugInData(bundle);
538      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
539      if (plugIn->_isPlugIn) {
540          /* Go through factories disabling them.  Disabling these factories should cause them to dealloc since we wouldn't be deallocating if any of the factories had outstanding instances.  So go backwards. */
541          os_log_debug(_CFBundlePluginLogger(), "Disabling factories in array %{public}p for bundle %{public}p", __CFBundleGetPlugInData(bundle)->_factories, bundle);
542          SInt32 c = CFArrayGetCount(plugIn->_factories);
543          while (c-- > 0) _CFPFactoryDisableLocked((_CFPFactoryRef)CFArrayGetValueAtIndex(plugIn->_factories, c));
544          CFRelease(plugIn->_factories);
545          
546          plugIn->_isPlugIn = false;
547      }
548      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
549  }
550  
551  CF_EXPORT CFTypeID CFPlugInGetTypeID(void) {
552      return CFBundleGetTypeID();
553  }
554  
555  CF_EXPORT CFPlugInRef CFPlugInCreate(CFAllocatorRef allocator, CFURLRef plugInURL) {
556      CFBundleRef bundle = CFBundleCreate(allocator, plugInURL);
557      return (CFPlugInRef)bundle;
558  }
559  
560  CF_EXPORT CFBundleRef CFPlugInGetBundle(CFPlugInRef plugIn) {
561      return (CFBundleRef)plugIn;
562  }
563  
564  CF_EXPORT void CFPlugInSetLoadOnDemand(CFPlugInRef plugIn, Boolean flag) {
565      _CFPlugInData *plugInData = __CFBundleGetPlugInData(plugIn);
566      if (plugInData->_isPlugIn) {
567          os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
568  
569          plugInData->_loadOnDemand = flag;
570          if (plugInData->_loadOnDemand && !plugInData->_isDoingDynamicRegistration && plugInData->_instanceCount == 0)
571          {
572              /* Unload now if we can/should. */
573              /* If we are doing dynamic registration currently, do not unload.  The unloading will happen when dynamic registration is done, if necessary. */
574              os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
575  
576              CFBundleUnloadExecutable(plugIn);
577          } else if (!plugInData->_loadOnDemand) {
578              
579              os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
580  
581              /* Make sure we're loaded now. */
582              CFBundleLoadExecutable(plugIn);
583          } else {
584              os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
585          }
586      }
587  }
588  
589  CF_EXPORT Boolean CFPlugInIsLoadOnDemand(CFPlugInRef plugIn) {
590      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
591          // Checking this is a race no matter what, so don't bother with the lock
592          return __CFBundleGetPlugInData(plugIn)->_loadOnDemand;
593      } else {
594          return false;
595      }
596  }
597  
598  CF_PRIVATE void _CFPlugInWillUnload(CFPlugInRef plugIn) {
599      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
600          os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
601  
602          SInt32 c = CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories);
603          /* First, flush all the function pointers that may be cached by our factories. */
604          while (c-- > 0) {
605              _CFPFactoryRef factory = (_CFPFactoryRef)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(plugIn)->_factories, c);
606              factory->_func = NULL;
607          }
608          
609          os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
610      }
611  }
612  
613  static void _CFPlugInAddPlugInInstanceLocked(CFPlugInRef plugIn) {
614      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
615          if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) {
616              // Make sure we are not scheduled for unloading
617              _CFPlugInUnscheduleForUnloading(CFPlugInGetBundle(plugIn));
618          }
619          __CFBundleGetPlugInData(plugIn)->_instanceCount++;
620          /* Instances also retain the CFBundle */
621          CFRetain(plugIn);
622      }
623  }
624  
625  static void _CFPlugInRemovePlugInInstanceLocked(CFPlugInRef plugIn) {
626      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
627          __CFBundleGetPlugInData(plugIn)->_instanceCount--;
628          if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) {
629              // We unload the code lazily because the code that caused this function to be called is probably code from the plugin itself.  If we unload now, we will hose things.
630              _CFPlugInScheduleForUnloading(CFPlugInGetBundle(plugIn));
631          }
632          /* Instances also retain the CFPlugIn */
633          /* MF:!!! This will cause immediate unloading if it was the last ref on the plugin. */
634          // Unless there is an 'unload prevention count'
635          CFRelease(plugIn);
636      }
637  }
638  
639  static void _CFPlugInIncrementUnloadPreventionLocked(CFPlugInRef plugIn) {
640      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
641          __CFBundleGetPlugInData(plugIn)->_unloadPreventionCount++;
642      }
643  }
644  
645  static void _CFPlugInDecrementUnloadPreventionLocked(CFPlugInRef plugIn) {
646      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
647          __CFBundleGetPlugInData(plugIn)->_unloadPreventionCount--;
648      }
649  }
650  
651  static void _CFPlugInAddFactoryLocked(CFPlugInRef plugIn, _CFPFactoryRef factory) {
652      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) CFArrayAppendValue(__CFBundleGetPlugInData(plugIn)->_factories, factory);
653  }
654  
655  static void _CFPlugInRemoveFactoryLocked(CFPlugInRef plugIn, _CFPFactoryRef factory) {
656      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
657          SInt32 idx = CFArrayGetFirstIndexOfValue(__CFBundleGetPlugInData(plugIn)->_factories, CFRangeMake(0, CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories)), factory);
658          if (idx >= 0) CFArrayRemoveValueAtIndex(__CFBundleGetPlugInData(plugIn)->_factories, idx);
659      }
660  }
661  
662  // MARK: Plugin - Factory
663  
664  static void _CFPFactoryDeallocate(CFTypeRef factory);
665  
666  const CFRuntimeClass __CFPFactoryClass = {
667      0,
668      "_CFPFactory",
669      NULL,    // init
670      NULL,    // copy
671      _CFPFactoryDeallocate,
672      NULL,    // equal
673      NULL,    // hash
674      NULL,       // formatting desc
675      NULL,       // debug desc
676  };
677  
678  static CFTypeID _CFPFactoryGetTypeID(void) {
679      return _kCFRuntimeIDCFPFactory;
680  }
681  
682  static void _CFPFactoryAddToTableLocked(_CFPFactoryRef factory) {
683      CFUUIDRef uuid = factory->_uuid;
684      
685      if (!_factoriesByFactoryID) {
686          CFDictionaryValueCallBacks _factoryDictValueCallbacks = {0, NULL, NULL, NULL, NULL};
687          _factoriesByFactoryID = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &_factoryDictValueCallbacks);
688      }
689      CFDictionarySetValue(_factoriesByFactoryID, uuid, factory);
690      
691      os_log_debug(_CFBundlePluginLogger(), "Registered factory %{public}@ (%{public}@)", factory, uuid);
692  }
693  
694  static void _CFPFactoryRemoveFromTableLocked(_CFPFactoryRef factory) {
695      CFUUIDRef uuid = factory->_uuid;
696      if (uuid && _factoriesByTypeID) CFDictionaryRemoveValue(_factoriesByFactoryID, uuid);
697      
698      os_log_debug(_CFBundlePluginLogger(), "Unregistered factory %{public}@ (%{public}@)", factory, uuid);
699  }
700  
701  static _CFPFactoryRef _CFPFactoryFindLocked(CFUUIDRef factoryID, Boolean matchOnlyEnabled) {
702      _CFPFactoryRef result = NULL;
703      
704      if (_factoriesByFactoryID) {
705          result = (_CFPFactoryRef )CFDictionaryGetValue(_factoriesByFactoryID, factoryID);
706          if (result && matchOnlyEnabled && !result->_enabled) result = NULL;
707      }
708  
709      return result;
710  }
711  
712  static void _CFPFactoryDeallocate(CFTypeRef ty) {
713      SInt32 c;
714      _CFPFactoryRef factory = (_CFPFactoryRef)ty;
715      
716      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
717  
718      _CFPFactoryRemoveFromTableLocked(factory);
719      
720      if (factory->_plugIn) {
721          _CFPlugInRemoveFactoryLocked(factory->_plugIn, factory);
722          CFRelease(factory->_plugIn);
723      }
724      
725      /* Remove all types for this factory. */
726      c = CFArrayGetCount(factory->_types);
727      while (c-- > 0) _CFPFactoryRemoveTypeLocked(factory, (CFUUIDRef)CFArrayGetValueAtIndex(factory->_types, c));
728      CFRelease(factory->_types);
729      
730      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
731  
732      if (factory->_funcName) CFRelease(factory->_funcName);
733      if (factory->_uuid) CFRelease(factory->_uuid);
734  }
735  
736  static _CFPFactoryRef _CFPFactoryCommonCreateLocked(CFAllocatorRef allocator, CFUUIDRef factoryID) {
737      _CFPFactoryRef factory;
738      uint32_t size;
739      size = sizeof(struct __CFPFactory) - sizeof(CFRuntimeBase);
740      factory = (_CFPFactoryRef)_CFRuntimeCreateInstance(allocator, _CFPFactoryGetTypeID(), size, NULL);
741      if (!factory) return NULL;
742      
743      factory->_uuid = (CFUUIDRef)CFRetain(factoryID);
744      factory->_enabled = true;
745      factory->_types = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
746      
747      _CFPFactoryAddToTableLocked(factory);
748      
749      return factory;
750  }
751  
752  static CFUUIDRef _CFPFactoryCopyFactoryIDLocked(_CFPFactoryRef factory) {
753      CFUUIDRef uuid = factory->_uuid;
754      if (uuid) CFRetain(uuid);
755      return uuid;
756  }
757  
758  static CFPlugInRef _CFPFactoryCopyPlugInLocked(_CFPFactoryRef factory) {
759      CFPlugInRef result = factory->_plugIn;
760      if (result) CFRetain(result);
761      return result;
762  }
763  
764  static void *__CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(CFPlugInFactoryFunction f, CFAllocatorRef allocator, CFUUIDRef typeID) {
765      FAULT_CALLBACK((void **)&(f));
766      void *result = (void *)INVOKE_CALLBACK2(f, allocator, typeID);
767      __asm __volatile__(""); // thwart tail-call optimization
768      return result;
769  }
770  
771  static void _CFPFactoryDisableLocked(_CFPFactoryRef factory) {
772      factory->_enabled = false;
773      os_log_debug(_CFBundlePluginLogger(), "Factory %{public}@ has been disabled", factory->_uuid);
774  #if !__clang_analyzer__
775      CFRelease(factory);
776  #endif
777  }
778  
779  static void _CFPFactoryAddTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID) {
780      /* Add the type to the factory's type list */
781      CFArrayAppendValue(factory->_types, typeID);
782      
783      if (!_factoriesByTypeID) _factoriesByTypeID = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
784      CFMutableArrayRef array = (CFMutableArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
785      if (!array) {
786          CFArrayCallBacks _factoryArrayCallbacks = {0, NULL, NULL, NULL, NULL};
787          // Create this from default allocator
788          array = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &_factoryArrayCallbacks);
789          CFDictionarySetValue(_factoriesByTypeID, typeID, array);
790          CFRelease(array);
791      }
792      CFArrayAppendValue(array, factory);
793      os_log_debug(_CFBundlePluginLogger(), "Type %{public}@ added to factory %{public}@", typeID, factory->_uuid);
794  }
795  
796  static void _CFPFactoryRemoveTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID) {
797      /* Remove it from the factory's type list */
798      SInt32 idx = CFArrayGetFirstIndexOfValue(factory->_types, CFRangeMake(0, CFArrayGetCount(factory->_types)), typeID);
799      if (idx >= 0) CFArrayRemoveValueAtIndex(factory->_types, idx);
800      
801      /* Remove the factory from the type's list of factories */
802      if (_factoriesByTypeID) {
803          CFMutableArrayRef array = (CFMutableArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
804          if (array) {
805              idx = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), factory);
806              if (idx >= 0) {
807                  CFArrayRemoveValueAtIndex(array, idx);
808                  if (CFArrayGetCount(array) == 0) CFDictionaryRemoveValue(_factoriesByTypeID, typeID);
809              }
810          }
811      }
812      os_log_debug(_CFBundlePluginLogger(), "Type %{public}@ removed from factory %{public}@", typeID, factory->_uuid);
813  }
814  
815  static Boolean _CFPFactorySupportsTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID) {
816      SInt32 idx = CFArrayGetFirstIndexOfValue(factory->_types, CFRangeMake(0, CFArrayGetCount(factory->_types)), typeID);
817      return (idx >= 0 ? true : false);
818  }
819  
820  /* These methods are called by CFPlugInInstance when an instance is created or destroyed.  If a factory's instance count goes to 0 and the factory has been disabled, the factory is destroyed. */
821  static void _CFPFactoryAddInstanceLocked(_CFPFactoryRef factory) {
822      CFPlugInRef plugin = factory->_plugIn;
823      if (plugin) {
824          _CFPlugInAddPlugInInstanceLocked(plugin);
825      }
826  }
827  
828  static void _CFPFactoryRemoveInstanceLocked(_CFPFactoryRef factory) {
829      CFPlugInRef plugin = factory->_plugIn;
830      if (plugin) {
831          _CFPlugInRemovePlugInInstanceLocked(plugin);
832      }
833  }
834  
835  #pragma mark -
836  
837  /* ===================== Finding factories and creating instances ===================== */
838  /* For plugIn hosts. */
839  /* Functions for finding factories to create specific types and actually creating instances of a type. */
840  
841  CF_EXPORT CFArrayRef CFPlugInFindFactoriesForPlugInType(CFUUIDRef typeID) CF_RETURNS_RETAINED {
842      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
843      CFArrayRef array = NULL;
844      if (_factoriesByTypeID) {
845          array = (CFArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
846      }
847      
848      CFMutableArrayRef result = NULL;
849      if (array) {
850          result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
851          
852          CFIndex c = CFArrayGetCount(array);
853          for (CFIndex i = 0; i < c; i++) {
854              CFUUIDRef factoryId = _CFPFactoryCopyFactoryIDLocked((_CFPFactoryRef)CFArrayGetValueAtIndex(array, i));
855              if (factoryId) {
856                  CFArrayAppendValue(result, factoryId);
857                  CFRelease(factoryId);
858              }
859          }
860      }
861      
862      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
863      os_log_debug(_CFBundlePluginLogger(), "%{public}ld factories found for requested plugin type %{public}@", result ? CFArrayGetCount(result) : 0, typeID);
864      return result;
865  }
866  
867  CF_EXPORT CFArrayRef CFPlugInFindFactoriesForPlugInTypeInPlugIn(CFUUIDRef typeID, CFPlugInRef plugIn) CF_RETURNS_RETAINED {
868      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
869      CFArrayRef array = NULL;
870      if (_factoriesByTypeID) {
871          array = (CFArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
872      }
873      
874      
875      CFMutableArrayRef result = NULL;
876      if (array) {
877          CFIndex c = CFArrayGetCount(array);
878          result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
879          for (CFIndex i = 0; i < c; i++) {
880              _CFPFactoryRef factory = (_CFPFactoryRef)CFArrayGetValueAtIndex(array, i);
881              CFPlugInRef factoryPlugIn = _CFPFactoryCopyPlugInLocked(factory);
882              if (factoryPlugIn == plugIn) {
883                  CFUUIDRef factoryId = _CFPFactoryCopyFactoryIDLocked(factory);
884                  CFArrayAppendValue(result, factoryId);
885                  CFRelease(factoryId);
886              }
887              if (factoryPlugIn) CFRelease(factoryPlugIn);
888          }
889      }
890      
891      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
892      os_log_debug(_CFBundlePluginLogger(), "%{public}ld factories found for requested plugin type %{public}@ in plugin %{public}@", result ? CFArrayGetCount(result) : 0, typeID, plugIn);
893      return result;
894  }
895  
896  // MARK: Plugin - Instance
897  
898  static CFStringRef __CFPlugInInstanceCopyDescription(CFTypeRef cf) {
899      /* MF:!!! Implement me */
900      return CFSTR("Some CFPlugInInstance");
901  }
902  
903  static void __CFPlugInInstanceDeallocate(CFTypeRef cf) {
904      CFPlugInInstanceRef instance = (CFPlugInInstanceRef)cf;
905      
906      __CFGenericValidateType(cf, CFPlugInInstanceGetTypeID());
907  
908      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
909  
910      if (instance->deallocateInstanceDataFunction) {
911          FAULT_CALLBACK((void **)&(instance->deallocateInstanceDataFunction));
912          (void)INVOKE_CALLBACK1(instance->deallocateInstanceDataFunction, (void *)(&instance->_instanceData[0]));
913      }
914  
915      if (instance->factory) _CFPFactoryRemoveInstanceLocked(instance->factory);
916      
917      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
918  }
919  
920  const CFRuntimeClass __CFPlugInInstanceClass = {
921      0,
922      "CFPlugInInstance",
923      NULL,      // init
924      NULL,      // copy
925      __CFPlugInInstanceDeallocate,
926      NULL,      // equal
927      NULL,      // hash
928      NULL,      //
929      __CFPlugInInstanceCopyDescription
930  };
931  
932  CFTypeID CFPlugInInstanceGetTypeID(void) {
933      return _kCFRuntimeIDCFPlugInInstance;
934  }
935  
936  CF_EXPORT CFPlugInInstanceRef CFPlugInInstanceCreateWithInstanceDataSize(CFAllocatorRef allocator, CFIndex instanceDataSize, CFPlugInInstanceDeallocateInstanceDataFunction deallocateInstanceFunction, CFStringRef factoryName, CFPlugInInstanceGetInterfaceFunction getInterfaceFunction) {
937      CFPlugInInstanceRef instance;
938      UInt32 size;
939      size = sizeof(struct __CFPlugInInstance) + instanceDataSize - sizeof(CFRuntimeBase);
940      instance = (CFPlugInInstanceRef)_CFRuntimeCreateInstance(allocator, CFPlugInInstanceGetTypeID(), size, NULL);
941      if (!instance) return NULL;
942      
943      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
944  
945      instance->factory = _CFPFactoryFindLocked((CFUUIDRef)factoryName, true);
946      if (instance->factory) _CFPFactoryAddInstanceLocked(instance->factory);
947      instance->getInterfaceFunction = getInterfaceFunction;
948      instance->deallocateInstanceDataFunction = deallocateInstanceFunction;
949      
950      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
951      
952      return instance;
953  }
954  
955  CF_EXPORT Boolean CFPlugInInstanceGetInterfaceFunctionTable(CFPlugInInstanceRef instance, CFStringRef interfaceName, void **ftbl) {
956      void *myFtbl;
957      Boolean result = false;
958      
959      if (instance->getInterfaceFunction) {
960          FAULT_CALLBACK((void **)&(instance->getInterfaceFunction));
961          result = INVOKE_CALLBACK3(instance->getInterfaceFunction, instance, interfaceName, &myFtbl) ? true : false;
962      }
963      if (ftbl) *ftbl = (result ? myFtbl : NULL);
964      return result;
965  }
966  
967  CF_EXPORT CFStringRef CFPlugInInstanceGetFactoryName(CFPlugInInstanceRef instance) {
968      os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
969  
970      // This function leaks, but it's the only safe way to access the factory name (on 10.8 or later).
971      // On 10.9 we added the CF_RETURNS_RETAINED annotation to the header.
972      CFUUIDRef factoryId = _CFPFactoryCopyFactoryIDLocked(instance->factory);
973      
974      os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock);
975  
976      return (CFStringRef)factoryId;
977  }
978  
979  CF_EXPORT void *CFPlugInInstanceGetInstanceData(CFPlugInInstanceRef instance) {
980      return (void *)(&instance->_instanceData[0]);
981  }