/* * Copyright (c) 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@ */ /* File: UnpackPiData.c Written by: Jeffrey Robbin Copyright: © 1994, 1996 by Apple Computer, Inc., all rights reserved. File: GetSymbolFromPEF.c Written by: Jeffrey Robbin Copyright: © 1994, 1996 by Apple Computer, Inc., all rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GetSymbolFromPEF.h" #include #include "IOGraphicsLibPrivate.h" enum { kIOPEFparamErr = 1001, kIOPEFmemFullErr = 1002, kIOPEFcfragFragmentFormatErr = 1003, kIOPEFcfragNoSectionErr = 1004, kIOPEFcfragNoSymbolErr = 1005, kIOPEFcfragFragmentCorruptErr = 1006 }; /******************************************************************************* * *******************************************************************************/ static unsigned char PEFGetNextByte( unsigned char ** rawBuffer, long * rawBufferRemaining) { *rawBufferRemaining = *rawBufferRemaining - 1; return *(*rawBuffer)++; } /******************************************************************************* * *******************************************************************************/ static unsigned long PEFGetCount( unsigned char ** rawBuffer, long * rawBufferRemaining) { register unsigned char b; register unsigned long value = 0UL; /* Scan the count value. All required bytes MUST be present... */ b = PEFGetNextByte(rawBuffer, rawBufferRemaining); if (!IS_LAST_PICNT_BYTE(b)) { // if 1st byte is not that last... value = CONCAT_PICNT(value, b); // ...init value using 1st byte b = PEFGetNextByte(rawBuffer, rawBufferRemaining); if (!IS_LAST_PICNT_BYTE(b)) { // if 2nd byte is not the last... value = CONCAT_PICNT(value, b); // ...add in 2nd byte b = PEFGetNextByte(rawBuffer, rawBufferRemaining); if (!IS_LAST_PICNT_BYTE(b)) { // if 3rd byte is not the last... value = CONCAT_PICNT(value, b); // ...add in 3rd byte b = PEFGetNextByte(rawBuffer, rawBufferRemaining); if (!IS_LAST_PICNT_BYTE(b)) { // if 4th is not the last... value = CONCAT_PICNT(value, b); // ...add in 4th byte // 5th byte is definitly last! b = PEFGetNextByte(rawBuffer, rawBufferRemaining); } } } } value = CONCAT_PICNT(value, b); /* add in "last" byte (whichever one) */ return value; } /******************************************************************************* * UnpackPiData expands a compressed section into memory. *******************************************************************************/ static OSErr UnpackPiData( LogicalAddress thePEFPtr, SectionHeaderPtr sectionHeaderPtr, LogicalAddress * theData) { long cntX, cnt, rpt, dcnt, delta; unsigned char op, b; unsigned char * unpackBuffer; unsigned char * originalUnpackBuffer; unsigned char * endUnpackBuffer; unsigned char * oldRawBuffer; long oldRawBufferRemaining; unsigned char * rawBuffer; long rawBufferRemaining; // Verify incoming section is packed. if (sectionHeaderPtr->regionKind != kPIDataSection) { return kIOPEFparamErr; } // Allocate memory to unpack into originalUnpackBuffer = (unsigned char*)NewPtrSys(sectionHeaderPtr->initSize); if (originalUnpackBuffer == nil) { return kIOPEFmemFullErr; } unpackBuffer = originalUnpackBuffer; endUnpackBuffer = unpackBuffer + sectionHeaderPtr->initSize; rawBuffer = (unsigned char*)((unsigned long)thePEFPtr + sectionHeaderPtr->containerOffset); rawBufferRemaining = sectionHeaderPtr->rawSize; /* Expand the pidata instructions. EOF will terminate processing * through the setjmp on pidData_jmpbuf above... */ while (rawBufferRemaining > 0) { /***** * The first byte of each instruction contains the opcode and a count. * If the countis 0, the count starts in the next byte... */ /* Pick up the opcode and first count operand... */ b = PEFGetNextByte(&rawBuffer, &rawBufferRemaining); op = PIOP(b); cnt = PICNT(b); if (cnt == 0) { cnt = PEFGetCount(&rawBuffer, &rawBufferRemaining); } /* Unpack the data as a function of the opcode... */ switch (op) { case kZero: // zero out cnt bytes... if (unpackBuffer + cnt > endUnpackBuffer) { goto Error; } memset(unpackBuffer, 0, cnt); unpackBuffer += cnt; break; case kBlock: // copy cnt bytes... if (unpackBuffer + cnt > endUnpackBuffer) { goto Error; } while (--cnt >= 0) { *unpackBuffer++ = PEFGetNextByte(&rawBuffer, &rawBufferRemaining); } break; case kRepeat: // copy cnt bytes rpt times... rpt = PEFGetCount(&rawBuffer, &rawBufferRemaining) + 1; if (cnt == 1) { if (unpackBuffer + rpt > endUnpackBuffer) { goto Error; } b = PEFGetNextByte(&rawBuffer, &rawBufferRemaining); memset(unpackBuffer, b, rpt); unpackBuffer += rpt; } else { oldRawBufferRemaining = rawBufferRemaining; oldRawBuffer = rawBuffer; while (--rpt >= 0) { if (unpackBuffer + cnt > endUnpackBuffer) { goto Error; } rawBufferRemaining = oldRawBufferRemaining; rawBuffer = oldRawBuffer; cntX = cnt; while (--cntX >= 0) { *unpackBuffer++ = PEFGetNextByte(&rawBuffer, &rawBufferRemaining); } } } break; case kRepeatZero: //copy cnt 0's and dcnt bytes rpt times dcnt = PEFGetCount(&rawBuffer, &rawBufferRemaining); // ...then copy cnt more 0's rpt = PEFGetCount(&rawBuffer, &rawBufferRemaining); goto rptPart1; // jump into loop to copy 0's first... while (--rpt >= 0) { if (unpackBuffer + dcnt > endUnpackBuffer) { goto Error; } cntX = dcnt; // cnt repeating parts follow each other while (--cntX >= 0) { *unpackBuffer++ = PEFGetNextByte(&rawBuffer, &rawBufferRemaining); } rptPart1: // non-repeating part is always 0's... if (unpackBuffer + cnt > endUnpackBuffer) { goto Error; } memset(unpackBuffer, 0, cnt); unpackBuffer += cnt; } break; case kRepeatBlock: // copy cnt repeating bytes and dcnt dcnt = PEFGetCount(&rawBuffer, &rawBufferRemaining); /* non-repating bytes rcnt times... */ rpt = PEFGetCount(&rawBuffer, &rawBufferRemaining); /* ...then copy cnt repeating bytes */ oldRawBufferRemaining = rawBufferRemaining; oldRawBuffer = rawBuffer; delta = 0; /* the repeating part and each non-rep */ goto rptPart2; /* jump into loop to copy rptng part 1st */ while (--rpt >= 0) { if (unpackBuffer + dcnt > endUnpackBuffer) { goto Error; } rawBuffer = oldRawBuffer + cnt + delta; rawBufferRemaining = oldRawBufferRemaining - (cnt + delta); cntX = dcnt; while (--cntX >= 0) { *unpackBuffer++ = PEFGetNextByte(&rawBuffer, &rawBufferRemaining); } delta += dcnt; rptPart2: if (unpackBuffer + cnt > endUnpackBuffer) { goto Error; } rawBuffer = oldRawBuffer; rawBufferRemaining = oldRawBufferRemaining; cntX = cnt; while (--cntX >= 0) { *unpackBuffer++ = PEFGetNextByte(&rawBuffer, &rawBufferRemaining); } } rawBuffer = oldRawBuffer + cnt + delta; rawBufferRemaining = oldRawBufferRemaining - (cnt + delta); break; default: goto Error; break; } /* switch */ } /* for */ *theData = originalUnpackBuffer; return noErr; Error: if (unpackBuffer) DisposePtr((Ptr)originalUnpackBuffer); *theData = nil; return kIOPEFparamErr; } /******************************************************************************* * GetSymbolFromPEF will extract from a PEF container the data associated * with a given symbol name. It requires that the PEF file have been previously * loaded into memory. *******************************************************************************/ static OSStatus GetSymbolFromPEF( StringPtr theSymbolName, const LogicalAddress thePEFPtr, LogicalAddress theSymbolPtr, ByteCount theSymbolSize) { ContainerHeaderPtr containerHeaderPtr; // Pointer to the Container Header SectionHeaderPtr loaderSectionPtr = 0; // Ptr to Loader Section Header SectionHeaderPtr exportSectionPtr; // Ptr to Section Header with symbol short currentSection; Boolean foundSection; Boolean foundSymbol; long numExportSymbols; LoaderHeaderPtr loaderHeaderPtr; ExportSymbolEntryPtr exportSymbolEntryPtr; LoaderExportChainEntryPtr exportChainEntryPtr; StringPtr exportSymbolName; LogicalAddress expandedDataPtr; unsigned char * sourceDataPtr; unsigned char * destDataPtr; containerHeaderPtr = (ContainerHeaderPtr)thePEFPtr; // Does the magic cookie match? if (containerHeaderPtr->magicCookie != 'Joy!') { return kIOPEFcfragFragmentFormatErr; } // Is this a known PEF container format? if (containerHeaderPtr->containerID != 'peff') { return kIOPEFcfragFragmentFormatErr; } // Validate parameters if (theSymbolPtr == nil) { return kIOPEFparamErr; } // Find the loader section. foundSection = false; for (currentSection = 0; currentSection < containerHeaderPtr->nbrOfSections; currentSection++) { loaderSectionPtr = (SectionHeaderPtr)((unsigned long)containerHeaderPtr + sizeof(ContainerHeader) + (sizeof(SectionHeader) * currentSection)); if (loaderSectionPtr->regionKind == kLoaderSection) { foundSection = true; break; } } if (foundSection == false) { return kIOPEFcfragNoSectionErr; } // Get the number of export symbols. loaderHeaderPtr = (LoaderHeaderPtr)((unsigned long)thePEFPtr + loaderSectionPtr->containerOffset); numExportSymbols = loaderHeaderPtr->nbrExportSyms; // Start at the first exported symbol. exportSymbolEntryPtr = (ExportSymbolEntryPtr)((unsigned long)loaderHeaderPtr + loaderHeaderPtr->slotTblOffset + (sizeof(LoaderHashSlotEntry) * (1<hashSlotTblSz)) + (sizeof(LoaderExportChainEntry) * numExportSymbols)); exportChainEntryPtr = (LoaderExportChainEntryPtr) ((unsigned long)loaderHeaderPtr + loaderHeaderPtr->slotTblOffset + (sizeof(LoaderHashSlotEntry) * (1<hashSlotTblSz))); foundSymbol = false; while (numExportSymbols-- > 0) { exportSymbolName = (StringPtr)((unsigned long)loaderHeaderPtr + loaderHeaderPtr->strTblOffset + (exportSymbolEntryPtr->class_and_name & 0x00FFFFFF)); if (SymbolCompare(theSymbolName, exportSymbolName, exportChainEntryPtr->_h._h_h._nameLength)) { foundSymbol = true; break; } exportSymbolEntryPtr = (ExportSymbolEntryPtr) (((int)exportSymbolEntryPtr) + 10); exportChainEntryPtr++; } if (foundSymbol == false) { return kIOPEFcfragNoSymbolErr; } // Found the symbol, so... let's go get the data! exportSectionPtr = (SectionHeaderPtr)((unsigned long)containerHeaderPtr + sizeof(ContainerHeader) + (sizeof(SectionHeader) * exportSymbolEntryPtr->sectionNumber)); expandedDataPtr = nil; switch (exportSectionPtr -> regionKind) { case kPIDataSection: // Expand the data! (Not yet... :) if (UnpackPiData(thePEFPtr, exportSectionPtr, &expandedDataPtr) != noErr) { return kIOPEFcfragFragmentCorruptErr; } sourceDataPtr = (unsigned char*)((unsigned long)expandedDataPtr + exportSymbolEntryPtr->address); break; default: sourceDataPtr = (unsigned char*)((unsigned long)thePEFPtr + exportSectionPtr->containerOffset + exportSymbolEntryPtr->address); break; } // Copy the data! destDataPtr = (unsigned char*)theSymbolPtr; while (theSymbolSize-- > 0) { *destDataPtr++ = *sourceDataPtr++; } // Cleanup any expanded data if (expandedDataPtr != nil) { DisposePtr((Ptr)expandedDataPtr); } return noErr; } /******************************************************************************* * *******************************************************************************/ static IOByteCount GetPEFLen(LogicalAddress thePEFPtr) { ContainerHeaderPtr containerHeaderPtr; // Pointer to the Container Header SectionHeaderPtr sections; short currentSection; long lastOffset = 0; long len = 0; containerHeaderPtr = (ContainerHeaderPtr)thePEFPtr; // Does the magic cookie match? if (containerHeaderPtr->magicCookie != 'Joy!') { return 0; } // Is this a known PEF container format? if (containerHeaderPtr->containerID != 'peff') { return 0; } // Find the loader section. sections = (SectionHeaderPtr) (containerHeaderPtr + 1); for (currentSection = 0; currentSection < containerHeaderPtr->nbrOfSections; currentSection++) { if (sections[currentSection].containerOffset > lastOffset) { lastOffset = sections[currentSection].containerOffset; len = sections[currentSection].rawSize; } } return lastOffset + len; } /******************************************************************************* * theExportSymbol is NOT null-terminated, so use theExportSymbolLength. *******************************************************************************/ static Boolean SymbolCompare( StringPtr theLookedForSymbol, StringPtr theExportSymbol, unsigned long theExportSymbolLength) { unsigned char * p1 = (unsigned char*)theLookedForSymbol; unsigned char * p2 = (unsigned char*)theExportSymbol; // Same length? // (skip over p string len byte) if (theExportSymbolLength != *p1++) { return false; } while (theExportSymbolLength-- != 0) { if (*p1++ != *p2++) { return false; } } return true; } /******************************************************************************* * *******************************************************************************/ // The Driver Description enum { kInitialDriverDescriptor = 0, kVersionOneDriverDescriptor = 1, kTheDescriptionSignature = 'mtej', }; struct DriverType { unsigned char nameInfoStr[32]; // Driver Name/Info String unsigned long version; // Driver Version Number - really NumVersion }; typedef struct DriverType DriverType; struct DriverDescription { unsigned long driverDescSignature; // Signature field of this structure unsigned long driverDescVersion; // Version of this data structure DriverType driverType; // Type of Driver // other data follows... }; typedef struct DriverDescription DriverDescription; /******************************************************************************* * *******************************************************************************/ static void ExaminePEF( mach_port_t masterPort, char * pef, IOByteCount pefLen, CFDictionaryRef allMatching) { char descripName[] = "\pTheDriverDescription"; long err; DriverDescription descrip; char matchName[40]; unsigned long newVersion; unsigned long curVersion; IOReturn kr; io_iterator_t iter; io_service_t service; io_string_t path; CFStringRef ndrvPropName = CFSTR("driver,AAPL,MacOS,PowerPC"); CFDataRef ndrv; CFStringRef matchKey; CFTypeRef value = 0; CFDictionaryRef matching = 0; CFMutableDictionaryRef dict; err = GetSymbolFromPEF(descripName, pef, &descrip, sizeof(descrip)); if (err != 0) { printf("\nGetSymbolFromPEF returns %ld\n",err); return; } if ((descrip.driverDescSignature != kTheDescriptionSignature) || (descrip.driverDescVersion != kInitialDriverDescriptor)) { return; } strncpy(matchName, descrip.driverType.nameInfoStr + 1, descrip.driverType.nameInfoStr[0]); matchName[descrip.driverType.nameInfoStr[0]] = 0; matchKey = CFStringCreateWithCString( kCFAllocatorDefault, matchName, kCFStringEncodingMacRoman ); if (!matchKey) return; if (allMatching) { value = CFDictionaryGetValue(allMatching, matchKey); if (value) { if (value && (CFDictionaryGetTypeID() == CFGetTypeID(value))) matching = CFRetain(value); else if (value && (CFStringGetTypeID() == CFGetTypeID(value))) { CFRelease(matchKey); matchKey = CFRetain(value); } } } if (!matching) { CFStringRef nameMatchKey = CFSTR(kIONameMatchKey); matching = CFDictionaryCreate( kCFAllocatorDefault, (const void **) &nameMatchKey, (const void **) &matchKey, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); } CFRelease(matchKey); if (!matching) return; kr = IOServiceGetMatchingServices(masterPort, matching, &iter); if (kIOReturnSuccess != kr) return; newVersion = descrip.driverType.version; if ((newVersion & 0xffff) == 0x8000) { // final stage, release rev newVersion |= 0xff; } for ( ; (service = IOIteratorNext(iter)); IOObjectRelease(service)) { kr = IORegistryEntryGetPath(service, kIOServicePlane, path); if (kIOReturnSuccess == kr) printf("Name %s matches %s, ", matchName, path); ndrv = (CFDataRef) IORegistryEntryCreateCFProperty(service, ndrvPropName, kCFAllocatorDefault, kNilOptions); if (ndrv) { DriverDescription _curDesc; DriverDescription * curDesc; curDesc = (DriverDescription *) CFDataGetBytePtr(ndrv); err = noErr; if ((sizeof(DriverDescription) > CFDataGetLength(ndrv)) || (curDesc->driverDescSignature != kTheDescriptionSignature)) { curDesc = &_curDesc; err = GetSymbolFromPEF(descripName, (const LogicalAddress)CFDataGetBytePtr(ndrv), curDesc, sizeof(DriverDescription)); } if (err != noErr) printf("GetSymbolFromPEF returns %ld\n",err); else { if ((curDesc->driverDescSignature == kTheDescriptionSignature) && (curDesc->driverDescVersion == kInitialDriverDescriptor)) { curVersion = curDesc->driverType.version; printf("new version %08lx, current version %08lx\n", newVersion, curVersion); if ((curVersion & 0xffff) == 0x8000) { // final stage, release rev curVersion |= 0xff; } if (newVersion <= curVersion) { pefLen = 0; } } } CFRelease(ndrv); } if (pefLen == 0) continue; ndrv = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pef, pefLen, kCFAllocatorNull); if (ndrv == 0) continue; printf("Installing ndrv ("); dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (dict) { io_service_t child = MACH_PORT_NULL; io_iterator_t iter; CFDictionarySetValue(dict, ndrvPropName, ndrv); kr = IORegistryEntryGetChildIterator(service, kIOServicePlane, &iter); if (kr == kIOReturnSuccess) { kr = kIOReturnNotFound; for( ; (child = IOIteratorNext(iter)); IOObjectRelease(child)) { if (IOObjectConformsTo(child, "IOFramebuffer")) break; } IOObjectRelease(iter); } if (child) { kr = IORegistryEntrySetCFProperties(child, dict); IOObjectRelease(child); } CFRelease(dict); } else kr = kIOReturnNoMemory; CFRelease(ndrv); printf("%08x)\n", kr); } IOObjectRelease(iter); return; } /******************************************************************************* * *******************************************************************************/ static int PEFExamineFile(mach_port_t masterPort, CFURLRef file, CFDictionaryRef props) { vm_offset_t pefBytes; vm_size_t pefFileLen; char * pef; IOByteCount pefLen, pos = 0; int err; CFDictionaryRef fileMatch; CFDictionaryRef matching = 0; Boolean matches = true; char cFile[MAXPATHLEN]; do { if (!props) continue; fileMatch = CFDictionaryGetValue(props, CFSTR("IONDRVFileMatching")); if (fileMatch && (CFDictionaryGetTypeID() != CFGetTypeID(fileMatch))) fileMatch = 0; if (fileMatch) { io_service_t service; CFRetain(fileMatch); service = IOServiceGetMatchingService(masterPort, fileMatch); matches = (MACH_PORT_NULL != service); if (matches) IOObjectRelease(service); continue; } matching = CFDictionaryGetValue(props, CFSTR("IONDRVMatching")); if (matching && (CFDictionaryGetTypeID() != CFGetTypeID(matching))) matching = 0; } while (false); if (!matches) return (kIOReturnSuccess); if (CFURLGetFileSystemRepresentation(file, TRUE, cFile, MAXPATHLEN)) err = readFile(cFile, &pefBytes, &pefFileLen); else err = kIOReturnIOError; if (kIOReturnSuccess != err) return (err); pef = (char *) pefBytes; while ((pos < pefFileLen) && (pefLen = GetPEFLen(pef))) { ExaminePEF(masterPort, pef, pefLen, matching); pefLen = (pefLen + 15) & ~15; pef += pefLen; pos += pefLen; } if (pefBytes) vm_deallocate(mach_task_self(), pefBytes, pefFileLen); return (0); } static char * CFURLCopyCString(CFURLRef anURL) { char * string = NULL; // returned CFIndex bufferLength; CFStringRef urlString = NULL; // must release Boolean error = false; urlString = CFURLCopyFileSystemPath(anURL, kCFURLPOSIXPathStyle); if (!urlString) { goto finish; } bufferLength = 1 + CFStringGetLength(urlString); string = (char *)malloc(bufferLength * sizeof(char)); if (!string) { goto finish; } if (!CFStringGetCString(urlString, string, bufferLength, kCFStringEncodingMacRoman)) { error = true; goto finish; } finish: if (error) { free(string); string = NULL; } if (urlString) CFRelease(urlString); return string; } static void _PEFExamineFile(mach_port_t masterPort, CFURLRef ndrvURL, CFDictionaryRef plist) { if (PEFExamineFile(masterPort, ndrvURL, plist)) { char * ndrv_path = CFURLCopyCString(ndrvURL); printf("error processing NDRV \"%s\"", ndrv_path ? ndrv_path : "(unknown)"); if (ndrv_path) free(ndrv_path); } } static void PEFExamineBundle( mach_port_t masterPort, CFBundleRef bdl ) { CFURLRef ndrvURL; CFDictionaryRef plist; plist = CFBundleGetInfoDictionary(bdl); if (!plist) return; ndrvURL = CFBundleCopyExecutableURL(bdl); if (!ndrvURL) return; _PEFExamineFile(kIOMasterPortDefault, ndrvURL, plist); CFRelease(ndrvURL); } void IOLoadPEFsFromURL( CFURLRef ndrvDirURL, io_service_t service ) { CFIndex ndrvCount, n; CFArrayRef ndrvDirContents = NULL; // must release SInt32 error; ndrvDirContents = (CFArrayRef) CFURLCreatePropertyFromResource( kCFAllocatorDefault, ndrvDirURL, kCFURLFileDirectoryContents, &error); ndrvCount = ndrvDirContents ? CFArrayGetCount(ndrvDirContents) : 0; for (n = 0; n < ndrvCount; n++) { CFURLRef ndrvURL = NULL; // don't release CFNumberRef num; CFBundleRef bdl; CFStringRef ext; SInt32 mode; Boolean skip; ndrvURL = (CFURLRef)CFArrayGetValueAtIndex(ndrvDirContents, n); bdl = CFBundleCreate(kCFAllocatorDefault, ndrvURL); if (bdl) { PEFExamineBundle(kIOMasterPortDefault, bdl); CFRelease(bdl); continue; } ext = CFURLCopyPathExtension(ndrvURL); if (ext) { skip = CFEqual(ext, CFSTR(".plist")); CFRelease(ext); if (skip) continue; } num = (CFNumberRef) CFURLCreatePropertyFromResource( kCFAllocatorDefault, ndrvURL, kCFURLFilePOSIXMode, &error); if (!num) continue; CFNumberGetValue(num, kCFNumberSInt32Type, (SInt32 *) &mode); CFRelease(num); if ((mode & S_IFMT) == S_IFREG) _PEFExamineFile(kIOMasterPortDefault, ndrvURL, NULL); } if (ndrvDirContents) CFRelease(ndrvDirContents); }