xref: /xnu-11215/iokit/bsddev/IOKitBSDInit.cpp (revision a3bb9fcc)
1 /*
2  * Copyright (c) 1998-2011 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 #include <IOKit/IOBSD.h>
29 #include <IOKit/IOLib.h>
30 #include <IOKit/IOService.h>
31 #include <IOKit/IOCatalogue.h>
32 #include <IOKit/IODeviceTreeSupport.h>
33 #include <IOKit/IOKitKeys.h>
34 #include <IOKit/IOPlatformExpert.h>
35 
36 extern "C" {
37 
38 #include <pexpert/pexpert.h>
39 #include <kern/clock.h>
40 #include <uuid/uuid.h>
41 #include <sys/vnode_internal.h>
42 
43 // how long to wait for matching root device, secs
44 #if DEBUG
45 #define ROOTDEVICETIMEOUT       120
46 #else
47 #define ROOTDEVICETIMEOUT       60
48 #endif
49 
50 extern dev_t mdevadd(int devid, uint64_t base, unsigned int size, int phys);
51 extern dev_t mdevlookup(int devid);
52 extern void mdevremoveall(void);
53 extern void di_root_ramfile(IORegistryEntry * entry);
54 
55 kern_return_t
56 IOKitBSDInit( void )
57 {
58     IOService::publishResource("IOBSD");
59 
60     return( kIOReturnSuccess );
61 }
62 
63 void
64 IOServicePublishResource( const char * property, boolean_t value )
65 {
66     if ( value)
67         IOService::publishResource( property, kOSBooleanTrue );
68     else
69         IOService::getResourceService()->removeProperty( property );
70 }
71 
72 boolean_t
73 IOServiceWaitForMatchingResource( const char * property, uint64_t timeout )
74 {
75     OSDictionary *	dict = 0;
76     IOService *         match = 0;
77     boolean_t		found = false;
78 
79     do {
80 
81         dict = IOService::resourceMatching( property );
82         if( !dict)
83             continue;
84         match = IOService::waitForMatchingService( dict, timeout );
85         if ( match)
86             found = true;
87 
88     } while( false );
89 
90     if( dict)
91         dict->release();
92     if( match)
93         match->release();
94 
95     return( found );
96 }
97 
98 boolean_t
99 IOCatalogueMatchingDriversPresent( const char * property )
100 {
101     OSDictionary *	dict = 0;
102     OSOrderedSet *	set = 0;
103     SInt32		generationCount = 0;
104     boolean_t		found = false;
105 
106     do {
107 
108         dict = OSDictionary::withCapacity(1);
109         if( !dict)
110             continue;
111         dict->setObject( property, kOSBooleanTrue );
112         set = gIOCatalogue->findDrivers( dict, &generationCount );
113         if ( set && (set->getCount() > 0))
114             found = true;
115 
116     } while( false );
117 
118     if( dict)
119         dict->release();
120     if( set)
121         set->release();
122 
123     return( found );
124 }
125 
126 OSDictionary * IOBSDNameMatching( const char * name )
127 {
128     OSDictionary *	dict;
129     const OSSymbol *	str = 0;
130 
131     do {
132 
133 	dict = IOService::serviceMatching( gIOServiceKey );
134 	if( !dict)
135 	    continue;
136         str = OSSymbol::withCString( name );
137 	if( !str)
138 	    continue;
139         dict->setObject( kIOBSDNameKey, (OSObject *) str );
140         str->release();
141 
142         return( dict );
143 
144     } while( false );
145 
146     if( dict)
147 	dict->release();
148     if( str)
149 	str->release();
150 
151     return( 0 );
152 }
153 
154 OSDictionary * IOUUIDMatching( void )
155 {
156     return IOService::resourceMatching( "boot-uuid-media" );
157 }
158 
159 OSDictionary * IONetworkNamePrefixMatching( const char * prefix )
160 {
161     OSDictionary *	 matching;
162     OSDictionary *   propDict = 0;
163     const OSSymbol * str      = 0;
164 	char networkType[128];
165 
166     do {
167         matching = IOService::serviceMatching( "IONetworkInterface" );
168         if ( matching == 0 )
169             continue;
170 
171         propDict = OSDictionary::withCapacity(1);
172         if ( propDict == 0 )
173             continue;
174 
175         str = OSSymbol::withCString( prefix );
176         if ( str == 0 )
177             continue;
178 
179         propDict->setObject( "IOInterfaceNamePrefix", (OSObject *) str );
180         str->release();
181         str = 0;
182 
183 		// see if we're contrained to netroot off of specific network type
184 		if(PE_parse_boot_argn( "network-type", networkType, 128 ))
185 		{
186 			str = OSSymbol::withCString( networkType );
187 			if(str)
188 			{
189 				propDict->setObject( "IONetworkRootType", str);
190 				str->release();
191 				str = 0;
192 			}
193 		}
194 
195         if ( matching->setObject( gIOPropertyMatchKey,
196                                   (OSObject *) propDict ) != true )
197             continue;
198 
199         propDict->release();
200         propDict = 0;
201 
202         return( matching );
203 
204     } while ( false );
205 
206     if ( matching ) matching->release();
207     if ( propDict ) propDict->release();
208     if ( str      ) str->release();
209 
210     return( 0 );
211 }
212 
213 static bool IORegisterNetworkInterface( IOService * netif )
214 {
215     // A network interface is typically named and registered
216     // with BSD after receiving a request from a user space
217     // "namer". However, for cases when the system needs to
218     // root from the network, this registration task must be
219     // done inside the kernel and completed before the root
220     // device is handed to BSD.
221 
222     IOService *    stack;
223     OSNumber *     zero    = 0;
224     OSString *     path    = 0;
225     OSDictionary * dict    = 0;
226     char *         pathBuf = 0;
227     int            len;
228     enum { kMaxPathLen = 512 };
229 
230     do {
231         stack = IOService::waitForService(
232                 IOService::serviceMatching("IONetworkStack") );
233         if ( stack == 0 ) break;
234 
235         dict = OSDictionary::withCapacity(3);
236         if ( dict == 0 ) break;
237 
238         zero = OSNumber::withNumber((UInt64) 0, 32);
239         if ( zero == 0 ) break;
240 
241         pathBuf = (char *) IOMalloc( kMaxPathLen );
242         if ( pathBuf == 0 ) break;
243 
244         len = kMaxPathLen;
245         if ( netif->getPath( pathBuf, &len, gIOServicePlane )
246              == false ) break;
247 
248         path = OSString::withCStringNoCopy( pathBuf );
249         if ( path == 0 ) break;
250 
251         dict->setObject( "IOInterfaceUnit", zero );
252         dict->setObject( kIOPathMatchKey,   path );
253 
254         stack->setProperties( dict );
255     }
256     while ( false );
257 
258     if ( zero ) zero->release();
259     if ( path ) path->release();
260     if ( dict ) dict->release();
261     if ( pathBuf ) IOFree(pathBuf, kMaxPathLen);
262 
263 	return ( netif->getProperty( kIOBSDNameKey ) != 0 );
264 }
265 
266 OSDictionary * IOOFPathMatching( const char * path, char * buf, int maxLen )
267 {
268     OSDictionary *	matching = NULL;
269     OSString *		str;
270     char *		comp;
271     int			len;
272 
273     do {
274 
275 	len = strlen( kIODeviceTreePlane ":" );
276 	maxLen -= len;
277 	if( maxLen <= 0)
278 	    continue;
279 
280 	strlcpy( buf, kIODeviceTreePlane ":", len + 1 );
281 	comp = buf + len;
282 
283 	len = strlen( path );
284 	maxLen -= len;
285 	if( maxLen <= 0)
286 	    continue;
287 	strlcpy( comp, path, len + 1 );
288 
289 	matching = OSDictionary::withCapacity( 1 );
290 	if( !matching)
291 	    continue;
292 
293 	str = OSString::withCString( buf );
294 	if( !str)
295 	    continue;
296         matching->setObject( kIOPathMatchKey, str );
297 	str->release();
298 
299 	return( matching );
300 
301     } while( false );
302 
303     if( matching)
304         matching->release();
305 
306     return( 0 );
307 }
308 
309 static int didRam = 0;
310 enum { kMaxPathBuf = 512, kMaxBootVar = 128 };
311 
312 kern_return_t IOFindBSDRoot( char * rootName, unsigned int rootNameSize,
313 				dev_t * root, u_int32_t * oflags )
314 {
315     mach_timespec_t	t;
316     IOService *		service;
317     IORegistryEntry *	regEntry;
318     OSDictionary *	matching = 0;
319     OSString *		iostr;
320     OSNumber *		off;
321     OSData *		data = 0;
322 
323     UInt32		flags = 0;
324     int			mnr, mjr;
325     const char *        mediaProperty = 0;
326     char *		rdBootVar;
327     char *		str;
328     const char *	look = 0;
329     int			len;
330     bool		debugInfoPrintedOnce = false;
331     const char * 	uuidStr = NULL;
332 
333     static int		mountAttempts = 0;
334 
335     int xchar, dchar;
336 
337 
338     if( mountAttempts++)
339 	IOSleep( 5 * 1000 );
340 
341     str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar );
342     if( !str)
343 	return( kIOReturnNoMemory );
344     rdBootVar = str + kMaxPathBuf;
345 
346     if (!PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar )
347      && !PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar ))
348 	rdBootVar[0] = 0;
349 
350     do {
351 	if( (regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) {
352 	    di_root_ramfile(regEntry);
353             data = OSDynamicCast(OSData, regEntry->getProperty( "root-matching" ));
354             if (data) {
355                matching = OSDynamicCast(OSDictionary, OSUnserializeXML((char *)data->getBytesNoCopy()));
356                 if (matching) {
357                     continue;
358                 }
359             }
360 
361 	    data = (OSData *) regEntry->getProperty( "boot-uuid" );
362 	    if( data) {
363 		uuidStr = (const char*)data->getBytesNoCopy();
364 		OSString *uuidString = OSString::withCString( uuidStr );
365 
366 		// match the boot-args boot-uuid processing below
367 		if( uuidString) {
368 		    IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr);
369 		    IOService::publishResource( "boot-uuid", uuidString );
370 		    uuidString->release();
371 		    matching = IOUUIDMatching();
372 		    mediaProperty = "boot-uuid-media";
373 		    regEntry->release();
374 		    continue;
375 		} else {
376 		    uuidStr = NULL;
377 		}
378 	    }
379 	    regEntry->release();
380 	}
381     } while( false );
382 
383 //
384 //	See if we have a RAMDisk property in /chosen/memory-map.  If so, make it into a device.
385 //	It will become /dev/mdx, where x is 0-f.
386 //
387 
388 	if(!didRam) {												/* Have we already build this ram disk? */
389 		didRam = 1;												/* Remember we did this */
390 		if((regEntry = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ))) {	/* Find the map node */
391 			data = (OSData *)regEntry->getProperty("RAMDisk");	/* Find the ram disk, if there */
392 			if(data) {											/* We found one */
393 				uintptr_t *ramdParms;
394 				ramdParms = (uintptr_t *)data->getBytesNoCopy();	/* Point to the ram disk base and size */
395 				(void)mdevadd(-1, ml_static_ptovirt(ramdParms[0]) >> 12, ramdParms[1] >> 12, 0);	/* Initialize it and pass back the device number */
396 			}
397 			regEntry->release();								/* Toss the entry */
398 		}
399 	}
400 
401 //
402 //	Now check if we are trying to root on a memory device
403 //
404 
405 	if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) {
406 		dchar = xchar = rdBootVar[2];							/* Get the actual device */
407 		if((xchar >= '0') && (xchar <= '9')) xchar = xchar - '0';	/* If digit, convert */
408 		else {
409 			xchar = xchar & ~' ';								/* Fold to upper case */
410 			if((xchar >= 'A') && (xchar <= 'F')) {				/* Is this a valid digit? */
411 				xchar = (xchar & 0xF) + 9;						/* Convert the hex digit */
412 				dchar = dchar | ' ';							/* Fold to lower case */
413 			}
414 			else xchar = -1;									/* Show bogus */
415 		}
416 		if(xchar >= 0) {										/* Do we have a valid memory device name? */
417 			*root = mdevlookup(xchar);							/* Find the device number */
418 			if(*root >= 0) {									/* Did we find one? */
419 
420 				rootName[0] = 'm';								/* Build root name */
421 				rootName[1] = 'd';								/* Build root name */
422 				rootName[2] = dchar;							/* Build root name */
423 				rootName[3] = 0;								/* Build root name */
424 				IOLog("BSD root: %s, major %d, minor %d\n", rootName, major(*root), minor(*root));
425 				*oflags = 0;									/* Show that this is not network */
426 				goto iofrootx;									/* Join common exit... */
427 			}
428 			panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar);	/* Not there */
429 		}
430 	}
431 
432       if( (!matching) && rdBootVar[0] ) {
433 	// by BSD name
434 	look = rdBootVar;
435 	if( look[0] == '*')
436 	    look++;
437 
438 	if ( strncmp( look, "en", strlen( "en" )) == 0 ) {
439 	    matching = IONetworkNamePrefixMatching( "en" );
440 	} else if ( strncmp( look, "uuid", strlen( "uuid" )) == 0 ) {
441             char *uuid;
442             OSString *uuidString;
443 
444             uuid = (char *)IOMalloc( kMaxBootVar );
445 
446             if ( uuid ) {
447                 if (!PE_parse_boot_argn( "boot-uuid", uuid, kMaxBootVar )) {
448                     panic( "rd=uuid but no boot-uuid=<value> specified" );
449                 }
450                 uuidString = OSString::withCString( uuid );
451                 if ( uuidString ) {
452                     IOService::publishResource( "boot-uuid", uuidString );
453                     uuidString->release();
454                     IOLog( "\nWaiting for boot volume with UUID %s\n", uuid );
455                     matching = IOUUIDMatching();
456                     mediaProperty = "boot-uuid-media";
457                 }
458                 IOFree( uuid, kMaxBootVar );
459             }
460 	} else {
461 	    matching = IOBSDNameMatching( look );
462 	}
463     }
464 
465     if( !matching) {
466 	OSString * astring;
467 	// Match any HFS media
468 
469         matching = IOService::serviceMatching( "IOMedia" );
470         astring = OSString::withCStringNoCopy("Apple_HFS");
471         if ( astring ) {
472             matching->setObject("Content", astring);
473             astring->release();
474         }
475     }
476 
477     if( gIOKitDebug & kIOWaitQuietBeforeRoot ) {
478     	IOLog( "Waiting for matching to complete\n" );
479     	IOService::getPlatform()->waitQuiet();
480     }
481 
482     if( true && matching) {
483         OSSerialize * s = OSSerialize::withCapacity( 5 );
484 
485         if( matching->serialize( s )) {
486             IOLog( "Waiting on %s\n", s->text() );
487             s->release();
488         }
489     }
490 
491     do {
492         t.tv_sec = ROOTDEVICETIMEOUT;
493         t.tv_nsec = 0;
494 	matching->retain();
495         service = IOService::waitForService( matching, &t );
496 	if( (!service) || (mountAttempts == 10)) {
497             PE_display_icon( 0, "noroot");
498             IOLog( "Still waiting for root device\n" );
499 
500             if( !debugInfoPrintedOnce) {
501                 debugInfoPrintedOnce = true;
502                 if( gIOKitDebug & kIOLogDTree) {
503                     IOLog("\nDT plane:\n");
504                     IOPrintPlane( gIODTPlane );
505                 }
506                 if( gIOKitDebug & kIOLogServiceTree) {
507                     IOLog("\nService plane:\n");
508                     IOPrintPlane( gIOServicePlane );
509                 }
510                 if( gIOKitDebug & kIOLogMemory)
511                     IOPrintMemory();
512             }
513 	}
514     } while( !service);
515     matching->release();
516 
517     if ( service && mediaProperty ) {
518         service = (IOService *)service->getProperty(mediaProperty);
519     }
520 
521     mjr = 0;
522     mnr = 0;
523 
524     // If the IOService we matched to is a subclass of IONetworkInterface,
525     // then make sure it has been registered with BSD and has a BSD name
526     // assigned.
527 
528     if ( service
529     &&   service->metaCast( "IONetworkInterface" )
530     &&   !IORegisterNetworkInterface( service ) )
531     {
532         service = 0;
533     }
534 
535     if( service) {
536 
537 	len = kMaxPathBuf;
538 	service->getPath( str, &len, gIOServicePlane );
539 	IOLog( "Got boot device = %s\n", str );
540 
541 	iostr = (OSString *) service->getProperty( kIOBSDNameKey );
542 	if( iostr)
543 	    strlcpy( rootName, iostr->getCStringNoCopy(), rootNameSize );
544 	off = (OSNumber *) service->getProperty( kIOBSDMajorKey );
545 	if( off)
546 	    mjr = off->unsigned32BitValue();
547 	off = (OSNumber *) service->getProperty( kIOBSDMinorKey );
548 	if( off)
549 	    mnr = off->unsigned32BitValue();
550 
551 	if( service->metaCast( "IONetworkInterface" ))
552 	    flags |= 1;
553 
554     } else {
555 
556 	IOLog( "Wait for root failed\n" );
557         strlcpy( rootName, "en0", rootNameSize );
558         flags |= 1;
559     }
560 
561     IOLog( "BSD root: %s", rootName );
562     if( mjr)
563 	IOLog(", major %d, minor %d\n", mjr, mnr );
564     else
565 	IOLog("\n");
566 
567     *root = makedev( mjr, mnr );
568     *oflags = flags;
569 
570     IOFree( str,  kMaxPathBuf + kMaxBootVar );
571 
572 iofrootx:
573     if( (gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) {
574 
575 	IOService::getPlatform()->waitQuiet();
576         if( gIOKitDebug & kIOLogDTree) {
577             IOLog("\nDT plane:\n");
578             IOPrintPlane( gIODTPlane );
579         }
580         if( gIOKitDebug & kIOLogServiceTree) {
581             IOLog("\nService plane:\n");
582             IOPrintPlane( gIOServicePlane );
583         }
584         if( gIOKitDebug & kIOLogMemory)
585             IOPrintMemory();
586     }
587 
588     return( kIOReturnSuccess );
589 }
590 
591 bool IORamDiskBSDRoot(void)
592 {
593     char rdBootVar[kMaxBootVar];
594     if (PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar )
595      || PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar )) {
596         if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) {
597             return true;
598         }
599     }
600     return false;
601 }
602 
603 void IOSecureBSDRoot(const char * rootName)
604 {
605 }
606 
607 void *
608 IOBSDRegistryEntryForDeviceTree(char * path)
609 {
610     return (IORegistryEntry::fromPath(path, gIODTPlane));
611 }
612 
613 void
614 IOBSDRegistryEntryRelease(void * entry)
615 {
616     IORegistryEntry * regEntry = (IORegistryEntry *)entry;
617 
618     if (regEntry)
619 	regEntry->release();
620     return;
621 }
622 
623 const void *
624 IOBSDRegistryEntryGetData(void * entry, char * property_name,
625 			  int * packet_length)
626 {
627     OSData *		data;
628     IORegistryEntry * 	regEntry = (IORegistryEntry *)entry;
629 
630     data = (OSData *) regEntry->getProperty(property_name);
631     if (data) {
632 	*packet_length = data->getLength();
633         return (data->getBytesNoCopy());
634     }
635     return (NULL);
636 }
637 
638 kern_return_t IOBSDGetPlatformUUID( uuid_t uuid, mach_timespec_t timeout )
639 {
640     IOService * resources;
641     OSString *  string;
642 
643     resources = IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey ), ( timeout.tv_sec || timeout.tv_nsec ) ? &timeout : 0 );
644     if ( resources == 0 ) return KERN_OPERATION_TIMED_OUT;
645 
646     string = ( OSString * ) IOService::getPlatform( )->getProvider( )->getProperty( kIOPlatformUUIDKey );
647     if ( string == 0 ) return KERN_NOT_SUPPORTED;
648 
649     uuid_parse( string->getCStringNoCopy( ), uuid );
650 
651     return KERN_SUCCESS;
652 }
653 
654 kern_return_t IOBSDGetPlatformSerialNumber( char *serial_number_str, u_int32_t len )
655 {
656     OSDictionary * platform_dict;
657     IOService *platform;
658     OSString *  string;
659 
660     if (len < 1) {
661 	    return 0;
662     }
663     serial_number_str[0] = '\0';
664 
665     platform_dict = IOService::serviceMatching( "IOPlatformExpertDevice" );
666     if (platform_dict == NULL) {
667 	    return KERN_NOT_SUPPORTED;
668     }
669 
670     platform = IOService::waitForService( platform_dict );
671     if (platform) {
672 	    string = ( OSString * ) platform->getProperty( kIOPlatformSerialNumberKey );
673 	    if ( string == 0 ) {
674 		    return KERN_NOT_SUPPORTED;
675 	    } else {
676 		    strlcpy( serial_number_str, string->getCStringNoCopy( ), len );
677 	    }
678     }
679 
680     return KERN_SUCCESS;
681 }
682 
683 void IOBSDIterateMediaWithContent(const char *content_uuid_cstring, int (*func)(const char *bsd_dev_name, const char *uuid_str, void *arg), void *arg)
684 {
685     OSDictionary *dictionary;
686     OSString *content_uuid_string;
687 
688     dictionary = IOService::serviceMatching( "IOMedia" );
689     if( dictionary ) {
690 	content_uuid_string = OSString::withCString( content_uuid_cstring );
691 	if( content_uuid_string ) {
692 	    IOService *service;
693 	    OSIterator *iter;
694 
695 	    dictionary->setObject( "Content", content_uuid_string );
696 	    dictionary->retain();
697 
698 	    iter = IOService::getMatchingServices(dictionary);
699 	    while (iter && (service = (IOService *)iter->getNextObject())) {
700 		    if( service ) {
701 			    OSString *iostr = (OSString *) service->getProperty( kIOBSDNameKey );
702 			    OSString *uuidstr = (OSString *) service->getProperty( "UUID" );
703 			    const char *uuid;
704 
705 			    if( iostr) {
706 				    if (uuidstr) {
707 					    uuid = uuidstr->getCStringNoCopy();
708 				    } else {
709 					    uuid = "00000000-0000-0000-0000-000000000000";
710 				    }
711 
712 				    // call the callback
713 				    if (func && func(iostr->getCStringNoCopy(), uuid, arg) == 0) {
714 					    break;
715 				    }
716 			    }
717 		    }
718 	    }
719 	    if (iter)
720 		    iter->release();
721 
722 	    content_uuid_string->release();
723 	}
724 	dictionary->release();
725     }
726 }
727 
728 
729 int IOBSDIsMediaEjectable( const char *cdev_name )
730 {
731     int ret = 0;
732     OSDictionary *dictionary;
733     OSString *dev_name;
734 
735     if (strncmp(cdev_name, "/dev/", 5) == 0) {
736 	    cdev_name += 5;
737     }
738 
739     dictionary = IOService::serviceMatching( "IOMedia" );
740     if( dictionary ) {
741 	dev_name = OSString::withCString( cdev_name );
742 	if( dev_name ) {
743 	    IOService *service;
744 	    mach_timespec_t tv = { 5, 0 };    // wait up to "timeout" seconds for the device
745 
746 	    dictionary->setObject( kIOBSDNameKey, dev_name );
747 	    dictionary->retain();
748 	    service = IOService::waitForService( dictionary, &tv );
749 	    if( service ) {
750 		OSBoolean *ejectable = (OSBoolean *) service->getProperty( "Ejectable" );
751 
752 		if( ejectable ) {
753 			ret = (int)ejectable->getValue();
754 		}
755 
756 	    }
757 	    dev_name->release();
758 	}
759 	dictionary->release();
760     }
761 
762     return ret;
763 }
764 
765 } /* extern "C" */
766