/* * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * HISTORY * */ #include "KEXTPrivate.h" #include "vers_rsrc.h" #include "CFAdditions.h" #include #include #include #include #include static KEXTReturn URLResourceExists( CFURLRef url ); static KEXTReturn URLResourceCreateModifcationTime( CFURLRef url, CFDateRef * date ); static KEXTReturn URLResourceCreateData( CFURLRef url, CFDataRef * data ); static void ArrayAddModuleEntities( KEXTModuleRef module, KEXTManagerRef manager ); static void ArrayAddPersonalityEntities( KEXTPersonalityRef personality, KEXTManagerRef manager ); #if 0 static void ArrayAddConfigEntities( KEXTConfigRef config, KEXTManagerRef manager ); #endif static void ArrayRemoveModuleEntities( KEXTModuleRef module, KEXTManagerRef manager ); static void ArrayRemovePersonalityEntities( KEXTPersonalityRef personality, KEXTManagerRef manager ); #if 0 static void ArrayRemoveConfigEntities( KEXTConfigRef config, KEXTManagerRef manager ); #endif static void DictionaryCopyAllBundles( CFStringRef key, KEXTEntityRef entity, CFMutableArrayRef array ); static void DictionaryCopyAllModules( CFStringRef key, KEXTEntityRef entity, CFMutableArrayRef array ); static void DictionaryCopyAllPersonalities( CFStringRef key, KEXTEntityRef entity, CFMutableArrayRef array ); static void DictionaryCopyAllConfigs( CFStringRef key, KEXTEntityRef entity, CFMutableArrayRef array ); static void _KEXTManagerAddRelation( CFMutableDictionaryRef relationsDict, KEXTEntityRef entity ); static void _KEXTManagerRemoveRelation( CFMutableDictionaryRef relationsDict, KEXTEntityRef entity ); static void _KEXTManagerAddBundleEntity( KEXTManagerRef manager, KEXTBundleRef bundle ); static KEXTReturn _KEXTManagerAddBundle( KEXTManagerRef manager, CFURLRef bundleUrl, Boolean toplevel); static void _KEXTManagerAddModuleEntity( KEXTManagerRef manager, KEXTModuleRef module ); static void _KEXTManagerAddPersonalityEntity( KEXTManagerRef manager, KEXTPersonalityRef personality ); static void _KEXTManagerAddConfigEntity( KEXTManagerRef manager, KEXTConfigRef config ); static void _KEXTManagerRemoveBundleEntity( KEXTManagerRef manager, KEXTBundleRef bundle ); static void _KEXTManagerRemoveModuleEntity( KEXTManagerRef manager, KEXTModuleRef module ); static void _KEXTManagerRemovePersonalityEntity( KEXTManagerRef manager, KEXTPersonalityRef personality ); static void _KEXTManagerRemoveConfigEntity( KEXTManagerRef manager, KEXTConfigRef config ); static void _KEXTManagerRemoveBundleItemsOnly( KEXTManagerRef manager, KEXTBundleRef bundle ); void _KEXTManagerRemoveConfigEntityWithCallback( KEXTManagerRef manager, KEXTConfigRef config ); static KEXTReturn _KEXTManagerScanBundles( KEXTManagerRef manager, CFURLRef url, Boolean toplevel); static void _KEXTManagerLogError(KEXTManagerRef manager, CFStringRef string, ...); static void _KEXTManagerLogMessage(KEXTManagerRef manager, CFStringRef string, ...); //**********************************************************// // // Misc. file I/O functions. // //**********************************************************// // Test the existence of the resource. static KEXTReturn URLResourceExists( CFURLRef url ) { CFBooleanRef val; Boolean boolval; SInt32 err; if ( !url ) { return kKEXTReturnBadArgument; } val = IOURLCreatePropertyFromResource( kCFAllocatorDefault, url, kIOURLFileExists, &err); if ( !val ) { return kKEXTReturnBundleNotFound; } boolval = CFBooleanGetValue(val); CFRelease(val); if ( !boolval ) { return kKEXTReturnBundleNotFound; } return kKEXTReturnSuccess; } // Create the modification time for a URL resource. static KEXTReturn URLResourceCreateModifcationTime( CFURLRef url, CFDateRef * date ) { SInt32 err; if ( !url || !date ) { return kKEXTReturnBadArgument; } *date = IOURLCreatePropertyFromResource( kCFAllocatorDefault, url, kIOURLFileLastModificationTime, &err); if ( !*date ) { return kKEXTReturnError; } return kKEXTReturnSuccess; } // Create data object from URL resource. static KEXTReturn URLResourceCreateData( CFURLRef url, CFDataRef * data ) { Boolean val; SInt32 err; *data = NULL; val = IOURLCreateDataAndPropertiesFromResource( kCFAllocatorDefault, url, data, NULL, NULL, &err); if ( !val ) { if ( *data ) { CFRelease(*data); *data = NULL; } return kKEXTReturnResourceNotFound; } return kKEXTReturnSuccess; } //**********************************************************// // // URL stuff. // //**********************************************************// static Boolean _KEXTManagerIsKernelExtensionURL(CFURLRef url) { CFStringRef path = CFURLGetString(url); return ( CFStringHasSuffix(path, CFSTR("kext")) || CFStringHasSuffix(path, CFSTR("kext/")) ); } // Creates a URL path to the module file. static CFURLRef _KEXTManagerCreateURLForModule( KEXTManagerRef manager, KEXTModuleRef module ) { KEXTReturn error; KEXTBundleRef bundle; CFStringRef primaryKey; CFStringRef file; primaryKey = KEXTModuleGetPrimaryKey(module); if ( !primaryKey) { return NULL; } bundle = KEXTManagerGetBundleWithModule(manager, primaryKey); if ( !bundle ) { return NULL; } file = KEXTModuleGetProperty(module, CFSTR(kModuleFileKey)); if ( !file ) { return NULL; } return KEXTBundleCreateURLForResource(bundle, file, NULL, &error); } //**********************************************************// // // Misc. internal relationship setters. // //**********************************************************// static void _KEXTManagerAddRelation( CFMutableDictionaryRef relationsDict, KEXTEntityRef entity ) { CFStringRef parentKey; CFStringRef primaryKey; CFMutableArrayRef keyArray; CFIndex count; parentKey = CFDictionaryGetValue(entity, CFSTR("ParentKey")); if ( !parentKey ) { return; } keyArray = (CFMutableArrayRef)CFDictionaryGetValue(relationsDict, parentKey); if ( !keyArray ) { keyArray = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !keyArray ) { return; } CFDictionarySetValue(relationsDict, parentKey, keyArray); CFRelease(keyArray); } count = CFArrayGetCount(keyArray); primaryKey = CFDictionaryGetValue(entity, CFSTR("PrimaryKey")); if( !CFArrayContainsValue(keyArray, CFRangeMake(0, count), primaryKey) ) { CFArrayAppendValue(keyArray, primaryKey); } } static void _KEXTManagerRemoveRelation( CFMutableDictionaryRef relationsDict, KEXTEntityRef entity ) { CFStringRef parentKey; CFStringRef primaryKey; CFMutableArrayRef keyArray; CFIndex count; CFIndex index; parentKey = CFDictionaryGetValue(entity, CFSTR("ParentKey")); primaryKey = CFDictionaryGetValue(entity, CFSTR("PrimaryKey")); if ( !parentKey || !primaryKey ) { return; } keyArray = (CFMutableArrayRef)CFDictionaryGetValue(relationsDict, parentKey); if ( !keyArray ) { return; } count = CFArrayGetCount(keyArray); index = CFArrayGetFirstIndexOfValue(keyArray, CFRangeMake(0, count), primaryKey); if ( index != -1 ) { CFArrayRemoveValueAtIndex(keyArray, index); } } //**********************************************************// // // Common internal entity getters/setters. // //**********************************************************// static inline KEXTEntityRef _KEXTManagerGetEntityWithKey( KEXTManagerRef manager, CFStringRef primaryKey ) { return (KEXTEntityRef)CFDictionaryGetValue(deref(manager)->_entities, primaryKey); } static inline void _KEXTManagerAddEntity( KEXTManagerRef manager, KEXTEntityRef entity ) { CFStringRef primaryKey; primaryKey = CFDictionaryGetValue(entity, CFSTR("PrimaryKey")); if ( primaryKey ) { CFDictionarySetValue(deref(manager)->_entities, primaryKey, entity); } } static void _KEXTManagerAddBundleEntity( KEXTManagerRef manager, KEXTBundleRef bundle ) { CFURLRef url; CFStringRef primaryKey; if ( !manager || !bundle ) { return; } // Add entity to the KEXTManagerDatabase. _KEXTManagerAddEntity(manager, bundle); // Add URL to the URL relationship database. url = KEXTBundleCopyURL(bundle); if ( url ) { primaryKey = KEXTBundleGetPrimaryKey(bundle); CFDictionarySetValue(deref(manager)->_urlRels, url, primaryKey); CFRelease(url); } } static void _KEXTManagerAddModuleEntity( KEXTManagerRef manager, KEXTModuleRef module ) { if ( !manager || !module ) { return; } _KEXTManagerAddEntity(manager, module); // Create a relationship between the parent bundle // and the module. _KEXTManagerAddRelation(deref(manager)->_modRels, module); } static void _KEXTManagerAddPersonalityEntity( KEXTManagerRef manager, KEXTPersonalityRef personality ) { if ( !manager || !personality ) { return; } _KEXTManagerAddEntity(manager, personality); // Create a relationship between the parent bundle // and the personality. _KEXTManagerAddRelation(deref(manager)->_perRels, personality); } static void _KEXTManagerAddConfigEntity( KEXTManagerRef manager, KEXTConfigRef config ) { if ( !manager || !config ) { return; } _KEXTManagerAddEntity(manager, config); // Create a relationship between the parent bundle // and the configuration. _KEXTManagerAddRelation(deref(manager)->_cfgRels, config); } static inline void _KEXTManagerRemoveEntity( KEXTManagerRef manager, KEXTEntityRef entity ) { CFStringRef primaryKey; primaryKey = CFDictionaryGetValue(entity, CFSTR("PrimaryKey")); if ( primaryKey ) { CFDictionaryRemoveValue(deref(manager)->_entities, primaryKey); } } static void _KEXTManagerRemoveBundleEntity( KEXTManagerRef manager, KEXTBundleRef bundle ) { CFURLRef url; if ( !manager || !bundle ) { return; } // Remove the URL relationship to this bundle. url = KEXTBundleCopyURL(bundle); if ( url ) { CFDictionaryRemoveValue(deref(manager)->_urlRels, url); CFRelease(url); } _KEXTManagerRemoveEntity(manager, bundle); } static void _KEXTManagerRemoveModuleEntity( KEXTManagerRef manager, KEXTModuleRef module ) { if ( !manager || !module ) { return; } _KEXTManagerRemoveEntity(manager, module); _KEXTManagerRemoveRelation(deref(manager)->_modRels, module); } static void _KEXTManagerRemovePersonalityEntity( KEXTManagerRef manager, KEXTPersonalityRef personality ) { if ( !manager || !personality ) { return; } _KEXTManagerRemoveEntity(manager, personality); _KEXTManagerRemoveRelation(deref(manager)->_perRels, personality); } static void _KEXTManagerRemoveConfigEntity( KEXTManagerRef manager, KEXTConfigRef config ) { if ( !manager || !config ) { return; } _KEXTManagerRemoveEntity(manager, config); _KEXTManagerRemoveRelation(deref(manager)->_cfgRels, config); } static CFArrayRef _KEXTManagerGetModuleKeysForBundle( KEXTManagerRef manager, KEXTBundleRef bundle ) { CFStringRef key; key = KEXTBundleGetPrimaryKey(bundle); return CFDictionaryGetValue(deref(manager)->_modRels, key); } static CFArrayRef _KEXTManagerGetPersonalityKeysForBundle( KEXTManagerRef manager, KEXTBundleRef bundle ) { CFStringRef key; key = KEXTBundleGetPrimaryKey(bundle); return CFDictionaryGetValue(deref(manager)->_perRels, key); } static CFArrayRef _KEXTManagerGetConfigKeysForBundle( KEXTManagerRef manager, KEXTBundleRef bundle ) { CFStringRef key; key = KEXTBundleGetPrimaryKey(bundle); return CFDictionaryGetValue(deref(manager)->_cfgRels, key); } //**********************************************************// // // Misc. internal CFArray applier functions. // //**********************************************************// static void ArrayAddModuleEntities( KEXTModuleRef module, KEXTManagerRef manager ) { _KEXTManagerAddModuleEntity(manager, module); } static void ArrayAddPersonalityEntities( KEXTPersonalityRef personality, KEXTManagerRef manager ) { _KEXTManagerAddPersonalityEntity(manager, personality); } #if 0 static void ArrayAddConfigEntities( KEXTConfigRef config, KEXTManagerRef manager ) { _KEXTManagerAddConfigEntity(manager, config); } #endif static void ArrayRemoveModuleEntities( KEXTModuleRef module, KEXTManagerRef manager ) { _KEXTManagerRemoveModuleEntity(manager, module); } static void ArrayRemovePersonalityEntities( KEXTPersonalityRef personality, KEXTManagerRef manager ) { _KEXTManagerRemovePersonalityEntity(manager, personality); } #if 0 static void ArrayRemoveConfigEntities( KEXTConfigRef config, KEXTManagerRef manager ) { _KEXTManagerRemoveConfigEntity(manager, config); } #endif static void DictionaryCopyAllBundles( CFStringRef key, KEXTEntityRef entity, CFMutableArrayRef array ) { CFStringRef type; type = KEXTManagerGetEntityType(entity); if ( CFEqual(type, KEXTBundleGetEntityType()) ) { CFArrayAppendValue(array, entity); } } static void DictionaryCopyAllModules( CFStringRef key, KEXTEntityRef entity, CFMutableArrayRef array ) { CFStringRef type; type = KEXTManagerGetEntityType(entity); if ( CFEqual(type, KEXTModuleGetEntityType()) ) { CFArrayAppendValue(array, entity); } } static void DictionaryCopyAllPersonalities( CFStringRef key, KEXTEntityRef entity, CFMutableArrayRef array ) { CFStringRef type; CFBooleanRef prop; prop = CFDictionaryGetValue(entity, CFSTR("IsConfig")); if ( prop && CFEqual(prop, kCFBooleanTrue) ) { return; } type = KEXTManagerGetEntityType(entity); if ( CFEqual(type, KEXTPersonalityGetEntityType()) ) { CFArrayAppendValue(array, entity); } } static void DictionaryCopyAllConfigs( CFStringRef key, KEXTEntityRef entity, CFMutableArrayRef array ) { CFStringRef type; CFBooleanRef prop; prop = CFDictionaryGetValue(entity, CFSTR("IsConfig")); if ( !prop || !CFEqual(prop, kCFBooleanTrue) ) { return; } type = KEXTManagerGetEntityType(entity); if ( CFEqual(type, KEXTPersonalityGetEntityType()) ) { CFArrayAppendValue(array, entity); } } static void ArrayCopyEntityForKey( CFStringRef key, void * context[] ) { KEXTManagerRef manager; KEXTEntityRef entity; CFMutableArrayRef array; manager = context[0]; array = context[1]; entity = _KEXTManagerGetEntityWithKey(manager, key); if ( entity ) { CFArrayAppendValue(array, entity); } } //**********************************************************// // // Misc. internal functions. // //**********************************************************// static void ArrayGatherDependencyPaths(const void * val, void * context[]) { KEXTManagerRef manager; KEXTModuleRef module; CFMutableArrayRef array; CFStringRef path; CFURLRef url; module = (KEXTModuleRef)val; manager = context[0]; array = context[1]; url = _KEXTManagerCreateURLForModule(manager, module); if ( !url ) { return; } path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); CFRelease(url); if ( !path ) { return; } CFArrayAppendValue(array, path); CFRelease(path); } Boolean KEXTManagerCheckSafeBootForModule( KEXTManagerRef manager, KEXTModuleRef module) { Boolean result = false; CFStringRef modSafe; if (!deref(manager)->_safeBoot) { result = true; goto finish; } modSafe = KEXTModuleGetProperty(module, CFSTR("OSBundleRequired")); if (!modSafe) { result = false; goto finish; } if (kCFCompareEqualTo == CFStringCompare(modSafe, CFSTR("Root"), 0) || kCFCompareEqualTo == CFStringCompare(modSafe, CFSTR("Local-Root"), 0) || kCFCompareEqualTo == CFStringCompare(modSafe, CFSTR("Network-Root"), 0) || kCFCompareEqualTo == CFStringCompare(modSafe, CFSTR("Console"), 0) || kCFCompareEqualTo == CFStringCompare(modSafe, CFSTR("Safe Boot"), 0) ) { result = true; goto finish; } finish: return result; } // Callback for loading a module into the kernel. static KEXTReturn _KEXTManagerLoadModule( KEXTManagerRef manager, KEXTModuleRef module, CFArrayRef dependencies ) { KEXTReturn error; CFMutableArrayRef paths; CFStringRef primaryKey; CFStringRef mode; CFStringRef moduleName; CFRange range; void * params[2]; if ( !module || !manager ) { return kKEXTReturnBadArgument; } primaryKey = KEXTModuleGetPrimaryKey(module); if ( !primaryKey ) { return kKEXTReturnError; } moduleName = KEXTModuleGetProperty(module, CFSTR(kNameKey)); if ( !moduleName ) { return kKEXTReturnError; } // Check the noloads list for this module. If it's // not to be loaded, then don't load it. mode = CFDictionaryGetValue(deref(manager)->_noloads, primaryKey); if ( mode && CFEqual(mode, CFSTR("Disabled")) ) { _KEXTManagerLogError(manager, CFSTR("Extension %@ is disabled.\n"), moduleName); return kKEXTReturnModuleDisabled; } paths = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !paths ) { return kKEXTReturnNoMemory; } params[0] = manager; params[1] = paths; range = CFRangeMake(0, CFArrayGetCount(dependencies)); CFArrayApplyFunction( dependencies, range, (CFArrayApplierFunction)ArrayGatherDependencyPaths, params); error = kKEXTReturnError; do { CFURLRef url; CFStringRef path; url = _KEXTManagerCreateURLForModule(manager, module); if ( !url ) { error = kKEXTReturnNoMemory; break; } path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); CFRelease(url); if ( !path ) { error = kKEXTReturnNoMemory; break; } error = KEXTLoadModule(path, paths); CFRelease(path); } while ( false ); CFRelease(paths); return error; } // Callback for removing a personality from the IOCatalogue. KEXTReturn _KEXTManagerUnloadPersonality( KEXTManagerRef manager, KEXTPersonalityRef personality ) { KEXTReturn error; CFDictionaryRef matchingDict; if ( !manager || !personality ) { return kKEXTReturnBadArgument; } matchingDict = _KEXTPersonalityGetProperties(personality); // First, remove personality from IOCatalogue. error = KEXTSendDataToCatalog(deref(manager)->_catPort, kIOCatalogRemoveDrivers, matchingDict); if ( error != kKEXTReturnSuccess ) { return error; } return kKEXTReturnSuccess; } // Callback for bundle authentication. static KEXTReturn BundleAuthenticateCallback( CFURLRef url, void * context ) { return KEXTManagerAuthenticateURL(url); } //**********************************************************// // // Public API's. // //**********************************************************// #define kAdministratorUID 0 KEXTReturn KEXTManagerAuthenticateURL( CFURLRef url ) { KEXTReturn error; CFArrayRef attribs; CFDictionaryRef dict; Boolean ret; SInt32 err; CFNumberRef uid = NULL; const void * vals[] = { kIOURLFileExists, kIOURLFileOwnerID, kIOURLFilePOSIXMode }; dict = NULL; if ( !url || (CFGetTypeID(url) != CFURLGetTypeID()) ) { return kKEXTReturnBadArgument; } attribs = CFArrayCreate(kCFAllocatorDefault, vals, 3, &kCFTypeArrayCallBacks); if ( !attribs ) { return kKEXTReturnNoMemory; } error = kKEXTReturnError; ret = IOURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, NULL, &dict, attribs, &err); CFRelease(attribs); if ( ret ) do { SInt32 uidVal; mode_t modeVal; CFBooleanRef cfbool; CFNumberRef mode; cfbool = CFDictionaryGetValue(dict, kIOURLFileExists); if (!cfbool || !CFBooleanGetValue(cfbool)) { error = kKEXTReturnResourceNotFound; break; } uid = CFDictionaryGetValue(dict, kIOURLFileOwnerID); if (!uid || !CFNumberGetValue(uid, kCFNumberSInt32Type, &uidVal) || uidVal != kAdministratorUID) { error = kKEXTReturnPermissionError; break; } mode = CFDictionaryGetValue(dict, kIOURLFilePOSIXMode); if ( !mode ) { error = kKEXTReturnError; break; } if ( !CFNumberGetValue(mode, kCFNumberShortType, &modeVal) ) { error = kKEXTReturnError; break; } // Check Group and Other write permissions. if ( (modeVal & S_IWOTH) || (modeVal & S_IWGRP) ) { error = kKEXTReturnPermissionError; } error = kKEXTReturnSuccess; } while ( false ); if ( dict ) CFRelease(dict); return error; } KEXTManagerRef KEXTManagerCreate( KEXTManagerBundleLoadingCallbacks * bundleCallbacks, KEXTManagerModuleLoadingCallbacks * moduleCallbacks, KEXTManagerPersonalityLoadingCallbacks * personalityCallbacks, KEXTManagerConfigsCallbacks * configsCallbacks, void * context, void (* logErrorCallback)(const char * s), void (* logMessageCallback)(const char * s), Boolean safeBoot, KEXTReturn * error ) { KEXTManager * manager; *error = kKEXTReturnSuccess; manager = (KEXTManager *)malloc(sizeof(KEXTManager)); if ( !manager ) return NULL; memset(manager, '\0', sizeof(KEXTManager)); manager->_refcount = 1; manager->_safeBoot = safeBoot; manager->_context = context; manager->_logErrorFunc = logErrorCallback; manager->_logMessageFunc = logMessageCallback; *error = KERN2KEXTReturn(IOMasterPort(bootstrap_port, &manager->_catPort)); if ( *error != kKEXTReturnSuccess ) { KEXTManagerFree((KEXTManagerRef)manager); return NULL; } manager->_entities = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); manager->_urlRels = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); manager->_modRels = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); manager->_perRels = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); manager->_cfgRels = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); manager->_noloads = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if ( !manager->_entities ) { KEXTManagerFree((KEXTManagerRef)manager); *error = kKEXTReturnNoMemory; return NULL; } manager->bcb.BundleAuthentication = BundleAuthenticateCallback; if ( bundleCallbacks ) { manager->bcb.BundleWillAdd = bundleCallbacks->BundleWillAdd; manager->bcb.BundleWillRemove = bundleCallbacks->BundleWillRemove; manager->bcb.BundleWasAdded = bundleCallbacks->BundleWasAdded; manager->bcb.BundleWasRemoved = bundleCallbacks->BundleWasRemoved; if ( bundleCallbacks->BundleAuthentication ) manager->bcb.BundleAuthentication = bundleCallbacks->BundleAuthentication; } if ( moduleCallbacks ) { manager->mcb.ModuleWillLoad = moduleCallbacks->ModuleWillLoad; manager->mcb.ModuleWasLoaded = moduleCallbacks->ModuleWasLoaded; manager->mcb.ModuleError = moduleCallbacks->ModuleError; manager->mcb.ModuleWillUnload = moduleCallbacks->ModuleWillUnload; manager->mcb.ModuleWasUnloaded = moduleCallbacks->ModuleWasUnloaded; } if ( personalityCallbacks ) { manager->pcb.PersonalityWillLoad = personalityCallbacks->PersonalityWillLoad; manager->pcb.PersonalityWasLoaded = personalityCallbacks->PersonalityWasLoaded; manager->pcb.PersonalityError = personalityCallbacks->PersonalityError; manager->pcb.PersonalityWillUnload = personalityCallbacks->PersonalityWillUnload; manager->pcb.PersonalityWasUnloaded = personalityCallbacks->PersonalityWasUnloaded; } if ( configsCallbacks ) { manager->ccb.ConfigWillAdd = configsCallbacks->ConfigWillAdd; manager->ccb.ConfigWasAdded = configsCallbacks->ConfigWasAdded; manager->ccb.ConfigWillRemove = configsCallbacks->ConfigWillRemove; manager->ccb.ConfigWasRemoved = configsCallbacks->ConfigWasRemoved; } manager->_mode = kKEXTManagerDefaultMode; manager->_configsDate = NULL; return (KEXTManagerRef)manager; } KEXTManagerRef KEXTManagerRetain( KEXTManagerRef manager ) { deref(manager)->_refcount++; return manager; } void KEXTManagerRelease( KEXTManagerRef manager ) { deref(manager)->_refcount--; if ( deref(manager)->_refcount < 1 ) { KEXTManagerFree(manager); } } void KEXTManagerFree( KEXTManagerRef manager ) { if ( deref(manager)->_entities ) { CFRelease(deref(manager)->_entities); } if ( deref(manager)->_modRels) { CFRelease(deref(manager)->_modRels); } if ( deref(manager)->_perRels) { CFRelease(deref(manager)->_perRels); } if ( deref(manager)->_urlRels) { CFRelease(deref(manager)->_urlRels); } if ( deref(manager)->_noloads ) { CFRelease(deref(manager)->_noloads); } if ( deref(manager)->_cfgRels ) { CFRelease(deref(manager)->_cfgRels); } if ( deref(manager)->_configsDate ) { CFRelease(deref(manager)->_configsDate); } free(manager); } void KEXTManagerSetMode( KEXTManagerRef manager, KEXTManagerMode mode ) { if ( !manager) { return; } deref(manager)->_mode = mode; } void KEXTManagerReset( KEXTManagerRef manager ) { CFDictionaryRemoveAllValues(deref(manager)->_entities); CFDictionaryRemoveAllValues(deref(manager)->_modRels); CFDictionaryRemoveAllValues(deref(manager)->_perRels); CFDictionaryRemoveAllValues(deref(manager)->_cfgRels); CFDictionaryRemoveAllValues(deref(manager)->_noloads); } static inline CFArrayRef _KEXTManagerCopyRelationsForBundle( CFMutableDictionaryRef relationsDict, KEXTBundleRef bundle ) { CFStringRef primaryKey; CFArrayRef array; primaryKey = KEXTBundleGetPrimaryKey(bundle); if ( !primaryKey ) { return NULL; } array = CFDictionaryGetValue(relationsDict, primaryKey); if ( !array ) { return NULL; } return CFArrayCreateCopy(kCFAllocatorDefault, array); } CFArrayRef KEXTManagerCopyModulesForBundle( KEXTManagerRef manager, KEXTBundleRef bundle) { CFMutableArrayRef array; CFArrayRef moduleKeys; CFRange range; void * context[2]; moduleKeys = _KEXTManagerGetModuleKeysForBundle(manager, bundle); if ( !moduleKeys ) { return NULL; } array = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !array ) { return NULL; } context[0] = manager; context[1] = array; range = CFRangeMake(0, CFArrayGetCount(moduleKeys)); CFArrayApplyFunction(moduleKeys, range, (CFArrayApplierFunction)ArrayCopyEntityForKey, context); return array; } CFArrayRef KEXTManagerCopyPersonalitiesForBundle( KEXTManagerRef manager, KEXTBundleRef bundle) { CFMutableArrayRef array; CFArrayRef personKeys; CFRange range; void * context[2]; personKeys = _KEXTManagerGetPersonalityKeysForBundle(manager, bundle); if ( !personKeys ) { return NULL; } array = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !array ) { return NULL; } context[0] = manager; context[1] = array; range = CFRangeMake(0, CFArrayGetCount(personKeys)); CFArrayApplyFunction(personKeys, range, (CFArrayApplierFunction)ArrayCopyEntityForKey, context); return array; } CFArrayRef KEXTManagerCopyConfigsForBundle( KEXTManagerRef manager, KEXTBundleRef bundle) { CFMutableArrayRef array; CFArrayRef configKeys; CFRange range; void * context[2]; configKeys = _KEXTManagerGetConfigKeysForBundle(manager, bundle); if ( !configKeys ) { return NULL; } array = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !array ) { return NULL; } context[0] = manager; context[1] = array; range = CFRangeMake(0, CFArrayGetCount(configKeys)); CFArrayApplyFunction(configKeys, range, (CFArrayApplierFunction)ArrayCopyEntityForKey, context); return array; } /********* * A module in the new scheme is just the top level of the plist, * minus the personalities dictionary. */ static CFArrayRef _KEXTBundleCopyModule ( CFDictionaryRef props /* a bundle dict */ ) { CFArrayRef moduleArray; CFDictionaryRef moduleDict; CFDictionaryRef personalitiesDict; moduleDict = CFDictionaryCreateMutableCopy(NULL, NULL, props); if (!moduleDict) { return NULL; } personalitiesDict = CFDictionaryGetValue(moduleDict, CFSTR("IOKitPersonalities")); if (personalitiesDict) { CFDictionaryRemoveValue(moduleDict, CFSTR("IOKitPersonalities")); } moduleArray = CFArrayCreate(kCFAllocatorDefault, &moduleDict, 1, &kCFTypeArrayCallBacks); CFRelease(moduleDict); return moduleArray; // perhaps it should be made immutable.... } static void _personalityInjectName( const void * key, const void * val, void * context) { CFStringRef * personalityName; CFDictionaryRef * personality; personalityName = (CFStringRef *)key; personality = (CFDictionaryRef *)val; CFDictionarySetValue(personality, CFSTR("IOPersonalityName"), personalityName); return; } static CFArrayRef _KEXTBundleCopyPersonalities ( CFDictionaryRef props /* a bundle dict */ ) { CFDictionaryRef personalities; CFDictionaryRef personsMutable; CFArrayRef personalitiesArray; CFIndex numPersonalities; void ** personalityValues; personalities = CFDictionaryGetValue(props, CFSTR(kPersonalitiesKey)); if ( ! personalities ) { return NULL; } numPersonalities = CFDictionaryGetCount(personalities); if (numPersonalities < 1) { return NULL; } personsMutable = CFDictionaryCreateMutableCopy(NULL, 0, personalities); if ( ! personsMutable ) { return NULL; } /* Put the key for each personality into its dictionary so we can track * things by name (using the "IOPersonalityName" key). */ CFDictionaryApplyFunction(personsMutable, & _personalityInjectName, NULL); personalityValues = (void **)malloc(numPersonalities * sizeof(void *)); if (!personalityValues) { return NULL; } CFDictionaryGetKeysAndValues(personsMutable, NULL, personalityValues); personalitiesArray = CFArrayCreateMutable(kCFAllocatorDefault, numPersonalities, &kCFTypeArrayCallBacks); if (!personalitiesArray) { return NULL; } CFArrayReplaceValues(personalitiesArray, CFRangeMake(0, 0), personalityValues, numPersonalities); free(personalityValues); return personalitiesArray; } static void ArrayGetModuleAndKey(const void * val, void * context[]) { CFMutableArrayRef keys; CFMutableArrayRef mods; CFStringRef bundleKey; KEXTModuleRef mod; bundleKey = context[0]; mods = context[1]; keys = context[2]; mod = KEXTModuleCreate(bundleKey, val); if ( !mod ) { return; } CFArrayAppendValue(mods, mod); CFArrayAppendValue(keys, KEXTModuleGetPrimaryKey(mod)); CFRelease(mod); } static CFMutableArrayRef _KEXTManagerCreateModulesArray( CFStringRef bundleKey, CFDictionaryRef props, CFMutableArrayRef * keys, Boolean isCFStyle ) { CFMutableArrayRef modules; CFMutableArrayRef modKeys; CFArrayRef array; CFRange range; void * context[3]; if ( !props || !bundleKey || !keys ) return NULL; array = _KEXTBundleCopyModule(props); if ( !array ) { return NULL; } modKeys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); modules = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !modules || !modKeys ) { if ( modules ) { CFRelease(modules); } if ( modKeys ) { CFRelease(modKeys); } CFRelease(array); return NULL; } context[0] = (void *)bundleKey; context[1] = modules; context[2] = modKeys; range = CFRangeMake(0, CFArrayGetCount(array)); CFArrayApplyFunction(array, range, (CFArrayApplierFunction)ArrayGetModuleAndKey, context); CFRelease(array); *keys = modKeys; return modules; } static void ArrayGetPersonalityAndKey(const void * val, void * context[]) { CFMutableArrayRef personKeys; CFMutableArrayRef persons; CFStringRef bundleKey; KEXTPersonalityRef person; bundleKey = context[0]; persons = context[1]; personKeys = context[2]; person = KEXTPersonalityCreate(bundleKey, val); if ( !person ) { return; } CFArrayAppendValue(persons, person); CFArrayAppendValue(personKeys, KEXTPersonalityGetPrimaryKey(person)); CFRelease(person); } static CFMutableArrayRef _KEXTManagerCreatePersonsArray( CFStringRef bundleKey, CFDictionaryRef props, CFMutableArrayRef * keys, Boolean isCFStyle ) { CFMutableArrayRef persons; CFMutableArrayRef personKeys; CFArrayRef array; CFRange range; void * context[3]; if ( !props || !bundleKey || !keys ) { return NULL; } array = _KEXTBundleCopyPersonalities(props); if ( !array ) { return NULL; } personKeys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); persons = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !persons || !personKeys ) { if ( persons ) { CFRelease(persons); } if ( personKeys ) { CFRelease(personKeys); } CFRelease(array); return NULL; } context[0] = (void *)bundleKey; context[1] = persons; context[2] = personKeys; range = CFRangeMake(0, CFArrayGetCount(array)); CFArrayApplyFunction(array, range, (CFArrayApplierFunction)ArrayGetPersonalityAndKey, context); CFRelease(array); *keys = personKeys; return persons; } // Extract the Info-macosx.plist from the // bundle and turn it into an object. static KEXTReturn _KEXTManagerCreateProperties( KEXTManagerRef manager, CFURLRef url, CFDictionaryRef * properties, Boolean * isCFStyle ) { KEXTReturn ret; CFStringRef urlString; CFStringRef newPath; CFStringRef str; CFURLRef newUrl; CFDataRef xmlData; *isCFStyle = true; urlString = CFURLGetString(url); newPath = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("%@/Contents/Info.plist"), urlString); if ( !newPath ) { return kKEXTReturnNoMemory; } newUrl = CFURLCreateWithString(kCFAllocatorDefault, newPath, NULL); CFRelease(newPath); if ( !newUrl ) { return kKEXTReturnNoMemory; } ret = URLResourceCreateData(newUrl, &xmlData); CFRelease(newUrl); if ( ret != kKEXTReturnSuccess ) { _KEXTManagerLogError(manager, CFSTR("%@ is not a valid kernel extension bundle.\n"), urlString); return kKEXTReturnNotKext; } if ( !xmlData ) { _KEXTManagerLogError(manager, CFSTR("%@ is not a valid kernel extension bundle.\n"), urlString); return kKEXTReturnNotKext; } *properties = (CFDictionaryRef)CFPropertyListCreateFromXMLData( kCFAllocatorDefault, xmlData, kCFPropertyListImmutable, &str); CFRelease(xmlData); if ( !*properties ) { KEXTError(kKEXTReturnSerializationError, CFSTR("Error obtaining property list")); if ( str ) { CFRelease(str); } return kKEXTReturnSerializationError; } if ( CFDictionaryGetTypeID() != CFGetTypeID(*properties) ) { CFRelease(*properties); *properties = NULL; return kKEXTReturnError; } return kKEXTReturnSuccess; } // This function creates all the necessary entities from the url provided. static KEXTReturn _KEXTManagerCreateEntities( KEXTManagerRef manager, CFURLRef url, KEXTBundleRef * bundle, CFArrayRef * modules, CFArrayRef * persons ) { KEXTReturn ret; CFStringRef urlString; CFStringRef bundleKey; CFDateRef date; CFDictionaryRef properties; CFArrayRef keys; Boolean isCFStyle; if ( !url || !bundle || !modules || !persons ) { return kKEXTReturnBadArgument; } urlString = CFURLGetString(url); // Make sure we are a KEXT bundle. if (! _KEXTManagerIsKernelExtensionURL(url)) { _KEXTManagerLogError(manager, CFSTR("%@ is not a valid kernel extension bundle.\n"), urlString); return kKEXTReturnNotKext; } // Check existence of file/folder. ret = URLResourceExists(url); if ( ret != kKEXTReturnSuccess ) { return ret; } // Check modification times. ret = URLResourceCreateModifcationTime(url, &date); if ( ret != kKEXTReturnSuccess ) { return ret; } ret = _KEXTManagerCreateProperties(manager, url, &properties, &isCFStyle); if ( ret != kKEXTReturnSuccess) { _KEXTManagerLogError(manager, CFSTR("%@ is not a valid kernel extension bundle.\n"), urlString); CFRelease(date); return ret; } *bundle = KEXTBundleCreate(url, properties, isCFStyle); if (!*bundle) { CFRelease(properties); CFRelease(date); _KEXTManagerLogError(manager, CFSTR("%@ is not a valid kernel extension bundle.\n"), urlString); return kKEXTReturnNotKext; } bundleKey = KEXTBundleGetPrimaryKey(*bundle); // Add special keys to the KEXTBundle object. *modules = _KEXTManagerCreateModulesArray(bundleKey, properties, &keys, isCFStyle); if ( *modules ) { if ( keys ) { // XXX -- We really don't need this... // CFDictionarySetValue(*bundle, CFSTR("BundleModules"), keys); CFRelease(keys); } CFDictionarySetValue(*bundle, CFSTR("BundleType"), CFSTR("KMOD")); } *persons = _KEXTManagerCreatePersonsArray(bundleKey, properties, &keys, isCFStyle); if ( *persons ) { if ( keys ) { // XXX -- We really don't need this... // CFDictionarySetValue(*bundle, CFSTR("BundlePersons"), keys); CFRelease(keys); } CFDictionarySetValue(*bundle, CFSTR("BundleType"), CFSTR("KEXT")); } CFDictionarySetValue(*bundle, CFSTR("ModificationDate"), date); CFRelease(date); return kKEXTReturnSuccess; } // Return an array of all the entities in the database. CFArrayRef KEXTManagerCopyAllEntities( KEXTManagerRef manager ) { void ** vals; CFArrayRef array; CFIndex count; count = CFDictionaryGetCount(deref(manager)->_entities); vals = (void **)malloc(sizeof(void *) * count); CFDictionaryGetKeysAndValues(deref(manager)->_entities, NULL, vals); array = CFArrayCreate(kCFAllocatorDefault, vals, count, &kCFTypeArrayCallBacks); free(vals); return array; } CFArrayRef KEXTManagerCopyAllBundles( KEXTManagerRef manager ) { CFDictionaryRef entities; CFMutableArrayRef array; if ( !manager ) { return NULL; } entities = deref(manager)->_entities; array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !array ) { return NULL; } CFDictionaryApplyFunction( entities, (CFDictionaryApplierFunction)DictionaryCopyAllBundles, array); return array; } CFArrayRef KEXTManagerCopyAllModules( KEXTManagerRef manager ) { CFDictionaryRef entities; CFMutableArrayRef array; if ( !manager ) { return NULL; } entities = deref(manager)->_entities; array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !array ) { return NULL; } CFDictionaryApplyFunction( entities, (CFDictionaryApplierFunction)DictionaryCopyAllModules, array); return array; } CFArrayRef KEXTManagerCopyAllPersonalities( KEXTManagerRef manager ) { CFDictionaryRef entities; CFMutableArrayRef array; if ( !manager ) { return NULL; } entities = deref(manager)->_entities; array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !array ) { return NULL; } CFDictionaryApplyFunction( entities, (CFDictionaryApplierFunction)DictionaryCopyAllPersonalities, array); return array; } CFArrayRef KEXTManagerCopyAllConfigs( KEXTManagerRef manager ) { CFDictionaryRef entities; CFMutableArrayRef array; if ( !manager ) { return NULL; } entities = deref(manager)->_entities; array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !array ) { return NULL; } CFDictionaryApplyFunction( entities, (CFDictionaryApplierFunction)DictionaryCopyAllConfigs, array); return array; } // *The* function which removes a bundle and all of its contents // from the database. void KEXTManagerRemoveBundle( KEXTManagerRef manager, CFStringRef primaryKey ) { KEXTManager * man = (KEXTManager *)manager; KEXTBundleRef bundle; if ( !manager || !primaryKey ) { return; } bundle = _KEXTManagerGetEntityWithKey(manager, primaryKey); if ( !bundle ) { return; } // First check with the "user" if this bundle should be added or not. if ( man->bcb.BundleWillRemove ) { if ( !man->bcb.BundleWillRemove(manager, bundle, man->_context) ) return; } CFRetain(bundle); // Now remove all the bundle items. _KEXTManagerRemoveBundleItemsOnly(manager, bundle); if ( man->bcb.BundleWasRemoved ) { man->bcb.BundleWasRemoved(manager, bundle, man->_context); } CFRelease(bundle); } // Add a bundle complete with its contents to the KEXTManager database. static inline void _KEXTManagerAddBundleAndContents( KEXTManagerRef manager, KEXTBundleRef bundle, CFArrayRef modules, CFArrayRef persons ) { CFRange range; // Add bundle descriptor to the KEXTManager database. _KEXTManagerAddBundleEntity(manager, bundle); // Add module descriptors to the KEXTManager database. if ( modules ) { range = CFRangeMake(0, CFArrayGetCount(modules)); CFArrayApplyFunction( modules, range, (CFArrayApplierFunction)ArrayAddModuleEntities, manager); } // Add personality descriptors to the KEXTManager database. if ( persons ) { range = CFRangeMake(0, CFArrayGetCount(persons)); CFArrayApplyFunction( persons, range, (CFArrayApplierFunction)ArrayAddPersonalityEntities, manager); } } // Remove the bundle and it's contents, but don't remove // config information. Leave that to the KEXTRemoveBundle // function. static void _KEXTManagerRemoveBundleItemsOnly( KEXTManagerRef manager, KEXTBundleRef bundle ) { CFStringRef primaryKey; CFArrayRef array; CFRange range; primaryKey = KEXTBundleGetPrimaryKey(bundle); if ( !primaryKey ) { return; } array = KEXTManagerCopyModulesForBundle(manager, bundle); if ( array ) { range = CFRangeMake(0, CFArrayGetCount(array)); CFArrayApplyFunction( array, range, (CFArrayApplierFunction)ArrayRemoveModuleEntities, manager); } array = KEXTManagerCopyPersonalitiesForBundle(manager, bundle); if ( array ) { range = CFRangeMake(0, CFArrayGetCount(array)); CFArrayApplyFunction( array, range, (CFArrayApplierFunction)ArrayRemovePersonalityEntities, manager); } _KEXTManagerRemoveBundleEntity(manager, bundle); } KEXTReturn KEXTManagerAddBundle( KEXTManagerRef manager, CFURLRef bundleUrl ) { return _KEXTManagerAddBundle(manager, bundleUrl, /* toplevel */ true); } static KEXTReturn _KEXTManagerAddBundle( KEXTManagerRef manager, CFURLRef bundleUrl, Boolean toplevel) { KEXTBundleRef bundle; KEXTReturn ret; CFMutableArrayRef modules; CFMutableArrayRef persons; CFStringRef primaryKey; if ( !manager || !bundleUrl ) { return kKEXTReturnBadArgument; } ret = URLResourceExists(bundleUrl); if ( ret != kKEXTReturnSuccess ) { return ret; } ret = deref(manager)->bcb.BundleAuthentication(bundleUrl, deref(manager)->_context); if ( ret != kKEXTReturnSuccess ) { return ret; } // Create entries. ret = _KEXTManagerCreateEntities(manager, bundleUrl, &bundle, &modules, &persons); if ( ret != kKEXTReturnSuccess ) { return ret; } if ( deref(manager)->bcb.BundleWillAdd ) { if ( !deref(manager)->bcb.BundleWillAdd(manager, bundle, deref(manager)->_context) ) { return kKEXTReturnSuccess; } } // Check if bundle already exists in database. primaryKey = KEXTBundleGetPrimaryKey(bundle); if ( !primaryKey ) { return kKEXTReturnPropertyNotFound; } ret = kKEXTReturnSuccess; do { KEXTBundleRef bundle2; bundle2 = _KEXTManagerGetEntityWithKey(manager, primaryKey); if ( bundle2 ) { CFStringRef info1; CFStringRef info2; // Apparently there is already a bundle in the database. info1 = KEXTBundleGetProperty(bundle, CFSTR("BundleInfo")); info2 = KEXTBundleGetProperty(bundle2, CFSTR("BundleInfo")); if ( (!info2 && !info1) || (info1 && info2 && CFEqual(info1, info2)) ) { // These are two different bundles. // Let the client application figure it out. if ( deref(manager)->bcb.BundleError ) { ret = deref(manager)->bcb.BundleError( manager, bundle, kKEXTReturnBundleAlreadyExists, deref(manager)->_context); } if ( ret != kKEXTReturnSuccess ) { break; } } // Replace the bundle. _KEXTManagerRemoveBundleItemsOnly(manager, bundle2); } _KEXTManagerAddBundleAndContents(manager, bundle, modules, persons); if ( deref(manager)->bcb.BundleWasAdded ) { deref(manager)->bcb.BundleWasAdded(manager, bundle, deref(manager)->_context); } } while ( false ); // We may have to recurse into the bundle so lets check. if (toplevel) { CFStringRef tmpStr; CFURLRef pluginURL = 0; tmpStr = KEXTBundleGetProperty(bundle, CFSTR("BundleFormat")); if ( CFEqual(tmpStr, CFSTR("CF")) ) { pluginURL = _KEXTBundleCreateFileURL(bundle, CFSTR("PlugIns/"), NULL); } if (pluginURL) { // We can ignore sub kext bundle errors (void) _KEXTManagerScanBundles(manager, pluginURL, /* toplevel */ false); CFRelease(pluginURL); } } if ( bundle ) CFRelease(bundle); if ( modules ) CFRelease(modules); if ( persons ) CFRelease(persons); return ret; } CFIndex KEXTManagerGetCount( KEXTManagerRef manager ) { return CFDictionaryGetCount(deref(manager)->_entities); } static void DictionaryRemoveBundleWithURL(void * key, void * val, void * context) { CFURLRef url; KEXTManagerRef manager; KEXTBundleRef bundle; manager = context; url = val; if ( !val || !context ) { return; } bundle = _KEXTManagerGetEntityWithKey(manager, val); if ( bundle ) { KEXTManagerRemoveBundle(manager, KEXTBundleGetPrimaryKey(bundle)); } } // Checks to see if any bundles have been modified, added, or deleted. static KEXTReturn _KEXTManagerScanBundles( KEXTManagerRef manager, CFURLRef url, Boolean toplevel) { CFArrayRef array; CFMutableArrayRef kextList; CFMutableDictionaryRef urlRelCopy = 0; CFIndex count; CFIndex index; SInt32 err; array = (CFArrayRef)IOURLCreatePropertyFromResource( kCFAllocatorDefault, url, kIOURLFileDirectoryContents, &err); if ( !array ) return kKEXTReturnResourceNotFound; // Now search for the kernel extensions in the directory. kextList = CFArrayCreateMutable (kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); count = CFArrayGetCount(array); for ( index = 0; index < count; index++ ) { CFURLRef entry; CFURLRef url; CFStringRef path; entry = CFArrayGetValueAtIndex(array, index); if (!entry || !_KEXTManagerIsKernelExtensionURL(entry)) continue; entry = CFURLCopyAbsoluteURL(entry); path = CFURLGetString(entry); url = CFURLCreateWithString(kCFAllocatorDefault, path, NULL); CFRelease(entry); CFArrayAppendValue(kextList, url); CFRelease(url); } CFRelease(array); array = NULL; urlRelCopy = CFDictionaryCreateMutableCopy (kCFAllocatorDefault, 0, deref(manager)->_urlRels); count = CFArrayGetCount(kextList); for ( index = 0; index < count; index++ ) { CFURLRef kextURL; KEXTBundleRef bundle; CFStringRef bundleKey; kextURL = CFArrayGetValueAtIndex(kextList, index); // If the bundle's url already exists, then avoid creating a new one. bundleKey = CFDictionaryGetValue(urlRelCopy, kextURL); if ( !bundleKey ) { // This may possibly be a new bundle. _KEXTManagerAddBundle(manager, kextURL, toplevel); } else { // An url to an existing bundle was found. Check to see // if it has been deleted or modified since it was added // to the database. bundle = _KEXTManagerGetEntityWithKey(manager, bundleKey); // Has an existing bundle been removed or modified? switch ( KEXTBundleValidate(bundle) ) { case kKEXTBundleValidateRemoved: KEXTManagerRemoveBundle(manager, bundleKey); break; case kKEXTBundleValidateModified: _KEXTManagerRemoveBundleItemsOnly(manager, bundle); _KEXTManagerAddBundle(manager, kextURL, toplevel); break; default: break; } CFDictionaryRemoveValue(urlRelCopy, kextURL); } } if (toplevel) { // Now if there are any left over urls, this means the bundles // they refer to have been removed. CFDictionaryApplyFunction(urlRelCopy, (CFDictionaryApplierFunction) DictionaryRemoveBundleWithURL, manager); } CFRelease(urlRelCopy); CFRelease(kextList); return kKEXTReturnSuccess; } // Scan the folder for KEXT bundles. // Also, check for driver configuration files. KEXTReturn KEXTManagerScanPath( KEXTManagerRef manager, CFURLRef url ) { KEXTReturn error; if ( !url || !CFURLHasDirectoryPath(url) ) { return kKEXTReturnBadArgument; } error = kKEXTReturnSuccess; do { error = KEXTManagerScanConfigs(manager, url); if ( (error != kKEXTReturnSuccess) && (error != kKEXTReturnResourceNotFound) ) { printf("Error (%d): cannot get configs.\n", error); // break; } error = _KEXTManagerScanBundles(manager, url, /* toplevel */ true); if ( error != kKEXTReturnSuccess ) { break; } } while ( false ); return error; } KEXTBundleRef KEXTManagerGetBundle( KEXTManagerRef manager, CFStringRef primaryKey ) { if ( !manager || !primaryKey ) { return NULL; } return _KEXTManagerGetEntityWithKey(manager, primaryKey); } KEXTBundleRef KEXTManagerGetBundleWithURL( KEXTManagerRef manager, CFURLRef url ) { CFStringRef primaryKey; if ( !manager || !url ) { return NULL; } primaryKey = CFDictionaryGetValue(deref(manager)->_urlRels, url); return KEXTManagerGetBundle(manager, primaryKey); } KEXTBundleRef KEXTManagerGetBundleWithModule( KEXTManagerRef manager, CFStringRef moduleName ) { KEXTModuleRef module; CFStringRef bundleKey; if ( !manager || !moduleName ) { return NULL; } module = _KEXTManagerGetEntityWithKey(manager, moduleName); if ( !module ) { return NULL; } bundleKey = CFDictionaryGetValue(module, CFSTR("ParentKey")); return _KEXTManagerGetEntityWithKey(manager, bundleKey); } KEXTModuleRef KEXTManagerGetModule( KEXTManagerRef manager, CFStringRef moduleName) { KEXTModuleRef module; CFStringRef primaryKey; if ( !manager || !moduleName ) { return NULL; } primaryKey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("KEXTModule?%@"), moduleName); module = _KEXTManagerGetEntityWithKey(manager, primaryKey); CFRelease(primaryKey); return module; } KEXTPersonalityRef KEXTManagerGetPersonality( KEXTManagerRef manager, CFStringRef primaryKey) { if ( !manager || !primaryKey ) { return NULL; } return _KEXTManagerGetEntityWithKey(manager, primaryKey); } static Boolean _CFStringToVers(CFStringRef versionString, UInt32 * vers) { Boolean result = true; char vers_buffer[32]; // more than long enough for legal vers if (!CFStringGetCString(versionString, vers_buffer, sizeof(vers_buffer) - 1, kCFStringEncodingMacRoman)) { result = false; goto finish; } if (!VERS_parse_string(vers_buffer, vers)) { result = false; goto finish; } finish: return result; } static Boolean _KEXTManagerGetModuleVers(KEXTManagerRef manager, KEXTModuleRef module, CFStringRef versionKey, UInt32 * vers) { Boolean result = true; CFStringRef thisName = NULL; CFStringRef thisVersion = NULL; char vers_buffer[32]; // more than long enough for legal vers thisName = KEXTModuleGetProperty(module, CFSTR(kNameKey)); if (!thisName) { _KEXTManagerLogError(manager, CFSTR("Extension has no \"CFBundleIdentifier\" property.\n")); result = false; goto finish; } thisVersion = KEXTModuleGetProperty(module, versionKey); if (!thisVersion) { _KEXTManagerLogError(manager, CFSTR("Extension %@ has no \"%@\" property.\n"), thisName, versionKey); result = false; goto finish; } if (!CFStringGetCString(thisVersion, vers_buffer, sizeof(vers_buffer) - 1, kCFStringEncodingMacRoman)) { _KEXTManagerLogError(manager, CFSTR("Can't extract version for extension %@.\n"), thisName); result = false; goto finish; } if (!VERS_parse_string(vers_buffer, vers)) { _KEXTManagerLogError(manager, CFSTR("Extension %@ has an invalid version string.\n"), thisName); result = false; goto finish; } finish: return result; } // Create a dependency list for loading the given module. // If the list is incomplete, the function will return false. static KEXTReturn _KEXTManagerGraphDependencies( KEXTManagerRef manager, KEXTModuleRef module, CFStringRef requestedModuleName, CFMutableArrayRef array ) { KEXTReturn result = kKEXTReturnSuccess; CFDictionaryRef dependencies = NULL; // do not release KEXTModuleRef mod = NULL; // do not release CFTypeID scratch = NULL; // do not release Boolean isKernelResource = false; CFRange range; CFIndex numDependencies = 0; void ** nameList = NULL; // must free void ** versionList = NULL; // must free CFArrayRef nameArray = NULL; // must release CFArrayRef versionArray = NULL; // must release CFIndex i; if ( !manager || !module || !array) { result = kKEXTReturnBadArgument; goto finish; } if (CFArrayGetCount(array) > 255) { _KEXTManagerLogError(manager, CFSTR("Dependency list for %@ is " "ridiculously long (circular reference?).\n"), requestedModuleName); result = kKEXTReturnError; goto finish; } // Check to see if a special properties exists for this "module". scratch = KEXTModuleGetProperty(module, CFSTR("OSKernelResource")); if ( scratch ) { isKernelResource = CFBooleanGetValue(scratch); } /* All KEXTs must declare their dependencies, at least on the kernel. */ if (isKernelResource) { result = kKEXTReturnSuccess; goto finish; } dependencies = KEXTModuleGetProperty(module, CFSTR(kRequiresKey)); if ( ! dependencies ) { _KEXTManagerLogError(manager, CFSTR("Extension %@ " "declares no dependencies.\n"), requestedModuleName); #if 0 // make a failure after folks have converted kexts result = kKEXTReturnError; goto finish; #else _KEXTManagerLogError(manager, CFSTR("Loading anyway.\n")); goto no_dependencies; #endif 0 } if (CFGetTypeID(dependencies) != CFDictionaryGetTypeID() ) { result = kKEXTReturnError; goto finish; } numDependencies = CFDictionaryGetCount(dependencies); if (numDependencies < 1) { // FIXME: Make this an error when all kexts properly // FIXME: declare their kernel dependencies. goto no_dependencies; } nameList = (void **)malloc(numDependencies * sizeof(void *)); if (! nameList) { result = kKEXTReturnNoMemory; goto finish; } versionList = (void **)malloc(numDependencies * sizeof(void *)); if (! versionList) { result = kKEXTReturnNoMemory; goto finish; } CFDictionaryGetKeysAndValues(dependencies, nameList, versionList); nameArray = CFArrayCreate(NULL, nameList, numDependencies, &kCFTypeArrayCallBacks); if (! nameArray) { result = kKEXTReturnNoMemory; goto finish; } versionArray = CFArrayCreate(NULL, versionList, numDependencies, &kCFTypeArrayCallBacks); if (! versionArray) { result = kKEXTReturnNoMemory; goto finish; } for ( i = 0; i < numDependencies; i++ ) { CFStringRef name; CFStringRef version; UInt32 req_vers; UInt32 dep_vers; UInt32 dep_compat_vers; name = CFArrayGetValueAtIndex(nameArray, i); if ( !name ) continue; version = CFArrayGetValueAtIndex(versionArray, i); if ( !version ) { _KEXTManagerLogError(manager, CFSTR("No required version for dependency %@ of extension %@.\n"), name, requestedModuleName); result = kKEXTReturnError; goto finish; } if (!_CFStringToVers(version, &req_vers)) { _KEXTManagerLogError(manager, CFSTR("Can't parse required version for dependency %@ " "of extension %@.\n"), name, requestedModuleName); result = kKEXTReturnError; goto finish; } mod = KEXTManagerGetModule(manager, name); if ( !mod ) { _KEXTManagerLogError(manager, CFSTR("Can't find dependency %@ of extension %@.\n"), name, requestedModuleName); result = kKEXTReturnMissingDependency; goto finish; } if (!_KEXTManagerGetModuleVers(manager, mod, CFSTR("CFBundleVersion"), &dep_vers)) { _KEXTManagerLogError(manager, CFSTR("Dependency %@ of extension %@ " "declares no version.\n"), name, requestedModuleName); #if 0 // make a failure after folks have converted kexts result = kKEXTReturnError; goto finish; #else _KEXTManagerLogError(manager, CFSTR("Loading anyway.\n")); #endif 0 } if (!_KEXTManagerGetModuleVers(manager, mod, CFSTR("OSBundleCompatibleVersion"), &dep_compat_vers)) { _KEXTManagerLogError(manager, CFSTR("Dependency %@ of extension %@ " "declares no compatible version.\n"), name, requestedModuleName); #if 0 // make a failure after folks have converted kexts result = kKEXTReturnError; goto finish; #else _KEXTManagerLogError(manager, CFSTR("Loading anyway.\n")); #endif 0 } if (req_vers > dep_vers || req_vers < dep_compat_vers) { _KEXTManagerLogError(manager, CFSTR("Installed version %@ of dependency %@ is not compatible " "with extension %@.\n"), version, name, requestedModuleName); #if 0 // make a failure after folks have converted kexts result = kKEXTReturnDependencyVersionMismatch; goto finish; #else _KEXTManagerLogError(manager, CFSTR("Loading anyway.\n")); #endif 0 } range = CFRangeMake(0, CFArrayGetCount(array)); if ( !CFArrayContainsValue(array, range, mod)) { result = _KEXTManagerGraphDependencies(manager, mod, requestedModuleName, array); if (result != kKEXTReturnSuccess) { goto finish; } } } no_dependencies: // Prevent "special" modules from being added to the dependency list // as used by kmodload. if ( ! isKernelResource ) { if (numDependencies < 1) { _KEXTManagerLogError(manager, CFSTR("Extension %@ declares no dependencies.\n"), requestedModuleName); // FIXME: Make this an error. _KEXTManagerLogError(manager, CFSTR("Loading anyway.\n")); } range = CFRangeMake(0, CFArrayGetCount(array)); if ( !CFArrayContainsValue(array, range, module) ) { CFArrayAppendValue(array, module); } } finish: // do not release dependencies // do not release mod // do not release scratch if (nameList) free(nameList); if (versionList) free(versionList); if (nameArray) CFRelease(nameArray); if (versionArray) CFRelease(versionArray); return result; } Boolean KEXTManagerCopyModuleDependencies( KEXTManagerRef manager, KEXTModuleRef module, CFArrayRef * array ) { KEXTReturn graphDependencyResult; CFStringRef moduleName; CFMutableArrayRef modules; Boolean ret; if ( !manager || !module || !array ) { return false; } moduleName = KEXTModuleGetProperty(module, CFSTR(kNameKey)); if ( !moduleName ) { return false; } modules = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !modules ) { return false; } graphDependencyResult = _KEXTManagerGraphDependencies(manager, module, moduleName, modules); if (graphDependencyResult == kKEXTReturnSuccess) { ret = true; } else { ret = false; } // Return the contents, even if there is an error since it // will have every dependency up to the point of the failure. *array = modules; return ret; } // Load a module. KEXTReturn KEXTManagerLoadModule( KEXTManagerRef manager, KEXTModuleRef module ) { KEXTManager * man; KEXTReturn graphDependencyResult; CFStringRef moduleName; CFMutableArrayRef array; CFStringRef primaryKey; CFStringRef mode; CFIndex count; CFIndex i; KEXTReturn error; if ( !manager || !module ) return kKEXTReturnBadArgument; primaryKey = KEXTModuleGetPrimaryKey(module); if ( !primaryKey ) { return kKEXTReturnError; } moduleName = KEXTModuleGetProperty(module, CFSTR(kNameKey)); if ( !moduleName ) { return kKEXTReturnError; } man = (KEXTManager *)manager; // Check the noloads list for this module. If it's // not to be loaded, then don't load it. mode = CFDictionaryGetValue(man->_noloads, primaryKey); if ( mode && CFEqual(mode, CFSTR("Disabled")) ) { return kKEXTReturnModuleDisabled; } if ( KEXTModuleIsLoaded(module, &error) ) { if ( error == kKEXTReturnSuccess && man->mcb.ModuleError ) error = man->mcb.ModuleError(manager, module, kKEXTReturnModuleAlreadyLoaded, man->_context); return error; } if (!KEXTManagerCheckSafeBootForModule(manager, module)) { _KEXTManagerLogError(manager, CFSTR("Extension %@ is not a safe-boot extension.\n"), moduleName); return kKEXTReturnModuleDisabled; } array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !array ) { return kKEXTReturnNoMemory; } graphDependencyResult = _KEXTManagerGraphDependencies(manager, module, moduleName, array); if (graphDependencyResult != kKEXTReturnSuccess) { CFRelease(array); return graphDependencyResult; } error = kKEXTReturnSuccess; count = CFArrayGetCount(array); for ( i = 0; i < count; i++ ) { KEXTModuleRef mod1; KEXTReturn err; CFMutableArrayRef deps; CFIndex j; mod1 = (KEXTModuleRef)CFArrayGetValueAtIndex(array, i); if ( !mod1 ) continue; if ( KEXTModuleIsLoaded(mod1, &err) ) { if ( man->mcb.ModuleError ) { error = man->mcb.ModuleError(manager, mod1, kKEXTReturnModuleAlreadyLoaded, man->_context); if ( (error != kKEXTReturnSuccess) && (error != kKEXTReturnModuleAlreadyLoaded) ) break; } continue; } if ( man->mcb.ModuleWillLoad ) { if ( !man->mcb.ModuleWillLoad(manager, mod1, man->_context) ) continue; } deps = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); for ( j = 0; j < i; j++ ) { KEXTModuleRef dep; dep = (KEXTModuleRef)CFArrayGetValueAtIndex(array, j); CFArrayAppendValue(deps, dep); } error = _KEXTManagerLoadModule(manager, mod1, deps); CFRelease(deps); if ( (error != kKEXTReturnSuccess) && man->mcb.ModuleError ) { error = man->mcb.ModuleError(manager, mod1, error, man->_context); } if ( error != kKEXTReturnSuccess ) break; if ( man->mcb.ModuleWasLoaded ) { man->mcb.ModuleWasLoaded(manager, mod1, man->_context); } } CFRelease(array); return error; } KEXTReturn KEXTManagerUnloadModule( KEXTManagerRef manager, KEXTModuleRef module ) { KEXTReturn ret; CFStringRef modName; kern_return_t status; char name[256]; ret = kKEXTReturnError; if ( !manager || !module ) return kKEXTReturnBadArgument; modName = KEXTModuleGetProperty(module, CFSTR(kNameKey)); if ( !modName || !CFStringGetCString(modName, name, 256, kCFStringEncodingMacRoman) ) { return kKEXTReturnError; } if ( deref(manager)->mcb.ModuleWillUnload ) { if ( !deref(manager)->mcb.ModuleWillUnload(manager, module, deref(manager)->_context) ) { return kKEXTReturnSuccess; } } status = IOCatalogueTerminate(deref(manager)->_catPort, kIOCatalogModuleUnload, name); ret = KERN2KEXTReturn(status); if ( (ret != kKEXTReturnSuccess) && deref(manager)->mcb.ModuleError ) { ret = deref(manager)->mcb.ModuleError(manager, module, ret, deref(manager)->_context); } if ( (ret == kKEXTReturnSuccess ) && deref(manager)->mcb.ModuleWasUnloaded ) { deref(manager)->mcb.ModuleWasUnloaded(manager, module, deref(manager)->_context); } return ret; } static void ArrayPersonalityWillLoad(const void * val, void * context[]) { KEXTManagerRef manager; KEXTPersonalityRef personality; CFMutableArrayRef toload; Boolean boolval; personality = (KEXTPersonalityRef)val; manager = context[0]; toload = context[1]; boolval = true; if ( deref(manager)->pcb.PersonalityWillLoad ) { boolval = deref(manager)->pcb.PersonalityWillLoad( manager, personality, deref(manager)->_context); } if ( true ) { CFArrayAppendValue(toload, personality); } } static void ArrayPersonalityCollectProperties(const void * val, void * context) { KEXTPersonalityRef person; CFDictionaryRef props; CFMutableArrayRef properties; person = (KEXTPersonalityRef)val; properties = context; props = CFDictionaryGetValue(person, CFSTR("PersonProperties")); if ( props ) { CFArrayAppendValue(properties, props); } } static void ArrayPersonalityPostamble(const void * val, void * context[]) { KEXTManagerRef manager; KEXTPersonalityRef person; KEXTReturn error; person = (KEXTPersonalityRef)val; manager = context[0]; error = *(KEXTReturn *)context[1]; if ( (error != kKEXTReturnSuccess) && deref(manager)->pcb.PersonalityError ) { error = deref(manager)->pcb.PersonalityError( manager, person, error, deref(manager)->_context); } if ( error != kKEXTReturnSuccess ) { return; } if ( deref(manager)->pcb.PersonalityWasLoaded ) { deref(manager)->pcb.PersonalityWasLoaded( manager, person, deref(manager)->_context); } } KEXTReturn KEXTManagerLoadPersonalities( KEXTManagerRef manager, CFArrayRef personalities ) { CFMutableArrayRef toload; CFMutableArrayRef properties; CFRange range; KEXTReturn error; void * context[2]; if ( !manager || !personalities ) { return kKEXTReturnBadArgument; } toload = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !toload ) { return kKEXTReturnNoMemory; } properties = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !properties ) { CFRelease(toload); return kKEXTReturnNoMemory; } context[0] = manager; context[1] = toload; range = CFRangeMake(0, CFArrayGetCount(personalities)); CFArrayApplyFunction(personalities, range, (CFArrayApplierFunction)ArrayPersonalityWillLoad, context); if ( CFArrayGetCount(personalities) < 1 ) { return kKEXTReturnSuccess; } range = CFRangeMake(0, CFArrayGetCount(toload)); CFArrayApplyFunction(toload, range, (CFArrayApplierFunction)ArrayPersonalityCollectProperties, properties); error = KEXTSendDataToCatalog(deref(manager)->_catPort, kIOCatalogAddDrivers, properties); CFRelease(properties); context[0] = manager; context[1] = &error; CFArrayApplyFunction(toload, range, (CFArrayApplierFunction)ArrayPersonalityPostamble, context); CFRelease(toload); return error; } KEXTReturn KEXTManagerUnloadPersonality( KEXTManagerRef manager, KEXTPersonalityRef personality ) { KEXTReturn error; if ( !manager || !personality ) { return kKEXTReturnBadArgument; } if ( deref(manager)->pcb.PersonalityWillUnload ) { if ( !deref(manager)->pcb.PersonalityWillUnload(manager, personality, deref(manager)->_context) ) { return kKEXTReturnSuccess; } } error = _KEXTManagerUnloadPersonality(manager, personality); if ( (error != kKEXTReturnSuccess) && deref(manager)->pcb.PersonalityError ) { error = deref(manager)->pcb.PersonalityError(manager, personality, error, deref(manager)->_context); } if ( (error == kKEXTReturnSuccess) && deref(manager)->pcb.PersonalityWasUnloaded ) { deref(manager)->pcb.PersonalityWasUnloaded(manager, personality, deref(manager)->_context); } return error; } // @@@gvdl: Talk to simon about this. How does kextunload work. KEXTReturn KEXTManagerTerminatePersonality( KEXTManagerRef manager, KEXTPersonalityRef personality ) { kern_return_t status; KEXTReturn ret; CFStringRef str; char name[256]; ret = kKEXTReturnError; if ( !manager || !personality ) { return kKEXTReturnBadArgument; } // XXX -- this should be changed to do a more exact match of the personality. // This way we can target individual drivers to terminate. str = KEXTPersonalityGetProperty(personality, CFSTR("IOClass")); if ( str ) { if ( !CFStringGetCString(str, name, 256, kCFStringEncodingMacRoman) ) { return kKEXTReturnError; } status = IOCatalogueTerminate( deref(manager)->_catPort, kIOCatalogServiceTerminate, name); ret = KERN2KEXTReturn(status); } else { ret = kKEXTReturnPropertyNotFound; } return ret; } CFStringRef KEXTManagerGetEntityType( KEXTEntityRef entity) { if ( !entity ) { return NULL; } return CFDictionaryGetValue(entity, CFSTR("EntityType")); } mach_port_t _KEXTManagerGetMachPort( KEXTManagerRef manager ) { return deref(manager)->_catPort; } static void logCFString(void (*logFunc)(const char *), CFStringRef format, va_list arguments) { CFStringRef string = 0; unsigned stringLength; unsigned bufferLength; char * buffer = NULL; string = CFStringCreateWithFormatAndArguments( NULL, NULL, format, arguments); if (!string) return; stringLength = CFStringGetLength(string); bufferLength = (1 + stringLength) * sizeof(char); buffer = (char *)malloc(bufferLength); if (!buffer) return; if (!CFStringGetCString(string, buffer, bufferLength, kCFStringEncodingMacRoman)) { goto finish; } logFunc(buffer); finish: if (buffer) free(buffer); if (string) CFRelease(string); return; } static void _KEXTManagerLogError(KEXTManagerRef manager, CFStringRef format, ...) { KEXTManager * mgr = (KEXTManager *)manager; va_list args; if (!mgr->_logErrorFunc) return; va_start(args, format); logCFString(mgr->_logErrorFunc, format, args); va_end(args); return; } static void _KEXTManagerLogMessage(KEXTManagerRef manager, CFStringRef format, ...) { KEXTManager * mgr = (KEXTManager *)manager; va_list args; if (!mgr->_logMessageFunc) return; va_start(args, format); logCFString(mgr->_logMessageFunc, format, args); va_end(args); return; } void _KEXTManagerShow( KEXTManagerRef manager ) { printf("entities:");CFShow(deref(manager)->_entities); printf("modules:");CFShow(deref(manager)->_modRels); printf("persons:");CFShow(deref(manager)->_perRels); printf("urls :");CFShow(deref(manager)->_urlRels); printf("configs:");CFShow(deref(manager)->_cfgRels); printf("noloads:");CFShow(deref(manager)->_noloads); printf("date :");CFShow(deref(manager)->_configsDate); } //***********************************************************// // // KEXT configuration stuff. // // //***********************************************************// static void CopyNoLoads(const void * key, const void * val, void * context) { CFMutableArrayRef array; CFDictionaryRef dict; const void * keys[2]; const void * vals[2]; if ( !val || !key ) { return; } array = context; keys[0] = CFSTR("PrimaryKey"); vals[0] = key; keys[1] = CFSTR("LoadValue"); vals[1] = val; dict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFArrayAppendValue(array, dict); CFRelease(dict); } // Save always saves to the current database no matter what mode // we are in. KEXTReturn KEXTManagerSaveConfigs( KEXTManagerRef manager, CFURLRef url ) { CFArrayRef array; CFMutableArrayRef noloads; CFDictionaryRef dict; CFDataRef data; CFURLRef path; SInt32 error; KEXTReturn ret; const void * vals[2]; const void * keys[2]; if ( !manager || !url ) { return kKEXTReturnBadArgument; } array = KEXTManagerCopyAllConfigs(manager); if ( !array ) { return kKEXTReturnNoMemory; } noloads = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if ( !noloads ) { CFRelease(array); return kKEXTReturnNoMemory; } // Get the list of non-loadable modules. CFDictionaryApplyFunction(deref(manager)->_noloads, (CFDictionaryApplierFunction)CopyNoLoads, noloads); if ( (CFArrayGetCount(array) < 1) && (CFArrayGetCount(noloads) < 1) ) { CFRelease(array); CFRelease(noloads); return kKEXTReturnSuccess; } keys[0] = CFSTR("Configs"); vals[0] = array; keys[1] = CFSTR("Modules"); vals[1] = noloads; dict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(array); CFRelease(noloads); if ( !dict ) { return kKEXTReturnNoMemory; } // XXX -- when in normal mode, move the current configuration // database to the previous database and save the new one as // current. Otherwise just save as current. if ( deref(manager)->_mode == kKEXTManagerDefaultMode ) { // XXX -- CFURL has no way to rename a resource, it looks // like we'll need to use UNIX filesystem API. #warning finish me. } data = CFPropertyListCreateXMLData(kCFAllocatorDefault, dict); CFRelease(dict); if ( !data ) { return kKEXTReturnNoMemory; } ret = kKEXTReturnSuccess; do { path = CFURLCreateCopyAppendingPathComponent( kCFAllocatorDefault, url, CFSTR("Configuration.current"), false); if ( !path ) { ret = kKEXTReturnNoMemory; break; } ret = IOURLWriteDataAndPropertiesToResource(path, data, NULL, &error); CFRelease(path); if ( ret != kKEXTReturnSuccess ) { break; } } while ( false ); CFRelease(data); return ret; } static void RemoveEntitiesWithConfig(const void * val, void * context) { KEXTConfigRef config1; KEXTConfigRef config2; CFMutableDictionaryRef dict; CFStringRef primaryKey; config1 = (KEXTConfigRef)val; dict = context; primaryKey = KEXTPersonalityGetPrimaryKey(config1); if ( !primaryKey ) { return; } // Remove values from dict if they are still present and not modified. // Whatever is leftover in dict will be removed from the database. config2 = (KEXTConfigRef)CFDictionaryGetValue(dict, primaryKey); if ( !config2 ) { return; } if ( !CFEqual(config1, config2) ) { return; } CFDictionaryRemoveValue(dict, primaryKey); } static void ArrayRemoveConfigsWithKey(const void * val, void * context) { KEXTManagerRef manager; KEXTConfigRef config; if ( !val || !context ) { return; } manager = context; config = KEXTManagerGetConfig(manager, val); if ( config ) { _KEXTManagerRemoveConfigEntityWithCallback(manager, config); } } static void DictionaryRemoveFromConfigs(const void * key, const void * val, void * context) { CFArrayRef configKeys; configKeys = (CFArrayRef)val; if ( configKeys ) { CFRange range; range = CFRangeMake(0, CFArrayGetCount(configKeys)); CFArrayApplyFunction(configKeys, range, ArrayRemoveConfigsWithKey, context); } } static void _KEXTManagerRemoveMissingConfigs( KEXTManagerRef manager, CFArrayRef configs ) { CFMutableDictionaryRef dict; CFRange range; if ( !manager || !deref(manager)->_cfgRels || !configs ) { return; } dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, deref(manager)->_cfgRels); if ( !dict ) { return; } // Find common configs. Remove them from dict. When done, // whatever is in dict needs to be removed from configs database. range = CFRangeMake(0, CFArrayGetCount(configs)); CFArrayApplyFunction(configs, range, RemoveEntitiesWithConfig, dict); CFDictionaryApplyFunction(dict, DictionaryRemoveFromConfigs, manager); CFRelease(dict); } static void ArrayAddConfigEntityAndSignal(void * val, void * context) { KEXTManagerRef manager; KEXTConfigRef config; manager = context; config = val; KEXTManagerAddConfig(manager, config); } static void _KEXTManagerAddNewConfigs( KEXTManagerRef manager, CFArrayRef configs ) { CFRange range; if ( !manager || !configs ) { return; } range = CFRangeMake(0, CFArrayGetCount(configs)); CFArrayApplyFunction(configs, range, (CFArrayApplierFunction)ArrayAddConfigEntityAndSignal, manager); } static void AddNoLoads(void * val, void * context) { CFMutableDictionaryRef noloads; CFDictionaryRef modconf; CFStringRef primaryKey; CFStringRef loadValue; noloads = context; modconf = val; primaryKey = CFDictionaryGetValue(modconf, CFSTR("PrimaryKey")); loadValue = CFDictionaryGetValue(modconf, CFSTR("LoadValue")); CFDictionarySetValue(noloads, primaryKey, loadValue); } // Read database from disk and udate the in-memory configs database. KEXTReturn KEXTManagerScanConfigs( KEXTManagerRef manager, CFURLRef url ) { CFDateRef date; CFDataRef data; CFStringRef path; CFStringRef tail; CFStringRef str; CFURLRef newUrl; CFDictionaryRef dict; CFArrayRef configs; CFArrayRef noloads; KEXTReturn error; SInt32 cferr; if ( !manager ) { return kKEXTReturnBadArgument; } switch ( deref(manager)->_mode ) { case kKEXTManagerInstallMode: tail = CFSTR("install"); break; case kKEXTManagerRecoveryMode: tail = CFSTR("previous"); break; case kKEXTManagerDefaultMode: tail = CFSTR("current"); break; default: tail = NULL; break; } // Are we in some strange mode of operation?!?!? if ( !tail ) { return kKEXTReturnBadArgument; } path = CFURLGetString(url); path = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("%@/Configuration.%@"), path, tail); if ( !path ) { return kKEXTReturnNoMemory; } newUrl = CFURLCreateWithString( kCFAllocatorDefault, path, NULL); CFRelease(path); if ( !newUrl ) { return kKEXTReturnNoMemory; } error = KEXTManagerAuthenticateURL(newUrl); if ( (error != kKEXTReturnSuccess) ) { CFRelease(newUrl); return error; } // Get the current modification time for the file. date = IOURLCreatePropertyFromResource( kCFAllocatorDefault, url, kIOURLFileLastModificationTime, &cferr); if ( !date ) { CFRelease(newUrl); return kKEXTReturnError; } // Check the date for the database. // If there isn't one, then the database is new. // If there is one, and the dates differ, then configuration database // has been updated. if ( deref(manager)->_configsDate ) { // If there isn't any change, then we needn't do anything. if ( CFEqual(deref(manager)->_configsDate, date) ) { CFRelease(date); return kKEXTReturnSuccess; } CFRelease(deref(manager)->_configsDate); deref(manager)->_configsDate = NULL; } deref(manager)->_configsDate = date; // Load the configs database from disk. error = URLResourceCreateData(newUrl, &data); CFRelease(newUrl); if ( error != kKEXTReturnSuccess ) { return error; } dict = (CFDictionaryRef)CFPropertyListCreateFromXMLData( kCFAllocatorDefault, data, kCFPropertyListMutableContainersAndLeaves, &str); CFRelease(data); if ( !dict ) { if ( deref(manager)->_configsDate ) { CFRelease(deref(manager)->_configsDate); deref(manager)->_configsDate = NULL; } if ( str ) { CFRelease(str); } return kKEXTReturnSerializationError; } configs = CFDictionaryGetValue(dict, CFSTR("Configs")); if ( configs ) { // Dump all unused configs and signal the client app. _KEXTManagerRemoveMissingConfigs(manager, configs); // Now add the new configs. _KEXTManagerAddNewConfigs(manager, configs); } noloads = CFDictionaryGetValue(dict, CFSTR("Modules")); if ( noloads ) { CFRange range; // Clean out the old configs. CFDictionaryRemoveAllValues(deref(manager)->_noloads); // Get the list of modules NOT to load. range = CFRangeMake(0, CFArrayGetCount(noloads)); CFArrayApplyFunction(noloads, range, (CFArrayApplierFunction)AddNoLoads, deref(manager)->_noloads); } CFRelease(dict); return kKEXTReturnSuccess; } KEXTConfigRef KEXTManagerCreateConfig( KEXTManagerRef manager, KEXTPersonalityRef personality ) { KEXTPersonalityRef config; CFDictionaryRef props; CFMutableDictionaryRef properties; Boolean found; if ( !manager || !personality ) { return NULL; } found = false; config = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, personality); props = CFDictionaryGetValue(config, CFSTR("PersonProps")); if ( props ) { properties = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, props); CFDictionarySetValue(config, CFSTR("PersonProps"), properties); CFRelease(properties); } if ( config ) { CFStringRef key; CFStringRef newKey; CFIndex index; key = CFDictionaryGetValue(config, CFSTR("PrimaryKey")); found = false; for ( index = 0; index < 0xffff; index++ ) { newKey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@?%x"), key, index); if ( !CFDictionaryGetValue(deref(manager)->_entities, newKey) ) { CFMutableDictionaryRef dict; CFStringRef name; CFStringRef newName; CFNumberRef score; CFIndex num; dict = (CFMutableDictionaryRef)CFDictionaryGetValue(config, CFSTR("PersonProperties")); CFDictionarySetValue(config, CFSTR("IsConfig"), kCFBooleanTrue); CFDictionarySetValue(config, CFSTR("PrimaryKey"), newKey); if ( !dict ) { break; } name = CFDictionaryGetValue(dict, CFSTR("Name")); if ( name ) { newName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@?%x"), name, index); CFDictionarySetValue(dict, CFSTR("Name"), newName); CFRelease(newName); } // Create a new probe score, make it higher than in the original personality. num = 0; score = KEXTPersonalityGetProperty(personality, CFSTR("IOProbeScore")); if ( score ) { CFNumberGetValue(score, kCFNumberSInt32Type, &num); } num += 10; score = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &num); if ( score ) { CFDictionarySetValue(dict, CFSTR("IOProbeScore"), score); CFRelease(score); } CFRelease(newKey); found = true; break; } CFRelease(newKey); } } if ( !found && config ) { CFRelease(config); config = NULL; } return config; } void KEXTManagerAddConfig( KEXTManagerRef manager, KEXTConfigRef config ) { Boolean ret; if ( !manager || !config ) { return; } ret = true; if ( deref(manager)->ccb.ConfigWillAdd ) { ret = deref(manager)->ccb.ConfigWillAdd(manager, config, deref(manager)->_context); } if ( !ret ) { return; } // Add entity and relationships. _KEXTManagerAddConfigEntity(manager, config); if ( deref(manager)->ccb.ConfigWasAdded ) { deref(manager)->ccb.ConfigWasAdded(manager, config, deref(manager)->_context); } } void _KEXTManagerRemoveConfigEntityWithCallback( KEXTManagerRef manager, KEXTConfigRef config ) { Boolean ret; ret = true; if ( deref(manager)->ccb.ConfigWillRemove ) { ret = deref(manager)->ccb.ConfigWillRemove(manager, config, deref(manager)->_context); } if ( !ret ) { return; } // Remove the config and relationships from the config database. CFRetain(config); _KEXTManagerRemoveConfigEntity(manager, config); if ( deref(manager)->ccb.ConfigWasRemoved ) { deref(manager)->ccb.ConfigWasRemoved(manager, config, deref(manager)->_context); } CFRelease(config); } KEXTConfigRef KEXTManagerGetConfig( KEXTManagerRef manager, CFStringRef primaryKey ) { return _KEXTManagerGetEntityWithKey(manager, primaryKey); } void KEXTManagerRemoveConfig( KEXTManagerRef manager, CFStringRef primaryKey ) { KEXTConfigRef config; if ( !manager || !primaryKey ) { return; } config = KEXTManagerGetConfig(manager, primaryKey); if ( config ) { _KEXTManagerRemoveConfigEntityWithCallback(manager, config); } } static void ArrayRemoveConfigEntityWithCallback(void * val, void * context) { KEXTManagerRef manager; KEXTConfigRef config; if ( !val || !context ) { return; } manager = context; config = val; _KEXTManagerRemoveConfigEntityWithCallback(manager, config); } void KEXTManagerRemoveConfigsForBundle( KEXTManagerRef manager, KEXTBundleRef bundle ) { CFArrayRef array; // Remove bundle configurations. array = KEXTManagerCopyConfigsForBundle(manager, bundle); if ( array ) { CFRange range; range = CFRangeMake(0, CFArrayGetCount(array)); CFArrayApplyFunction(array, range, (CFArrayApplierFunction)ArrayRemoveConfigEntityWithCallback, manager); CFRelease(array); } } void KEXTManagerSetModuleMode( KEXTManagerRef manager, KEXTModuleRef module, KEXTModuleMode mode ) { CFMutableDictionaryRef noloads; CFStringRef primaryKey; CFStringRef val; if ( !manager || !module ) { return; } noloads = deref(manager)->_noloads; primaryKey = KEXTModuleGetPrimaryKey(module); switch ( mode ) { case kKEXTModuleModeDefault: { val = CFDictionaryGetValue(noloads, primaryKey); if ( val ) { CFDictionaryRemoveValue(noloads, primaryKey); } } break; case kKEXTModuleModeNoload: { CFDictionarySetValue(noloads, primaryKey, CFSTR("Disabled")); } break; case kKEXTModuleModeForceLoad: { CFDictionarySetValue(noloads, primaryKey, CFSTR("Force")); } break; } }