1/*
2 * Copyright (c) 1998-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "DADisk.h"
25
26#include "DABase.h"
27#include "DAInternal.h"
28#include "DALog.h"
29
30#include <grp.h>
31#include <paths.h>
32#include <pwd.h>
33#include <CoreFoundation/CFRuntime.h>
34#include <DiskArbitration/DiskArbitrationPrivate.h>
35#include <IOKit/IOBSD.h>
36#include <IOKit/storage/IOBlockStorageDevice.h>
37#include <IOKit/storage/IOMedia.h>
38#include <IOKit/storage/IOBDMedia.h>
39#include <IOKit/storage/IOCDMedia.h>
40#include <IOKit/storage/IODVDMedia.h>
41#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
42
43struct __DADisk
44{
45    CFRuntimeBase          _base;
46    CFAbsoluteTime         _busy;
47    io_object_t            _busyNotification;
48    CFURLRef               _bypath;
49    DACallbackRef          _claim;
50    CFTypeRef              _context;
51    CFTypeRef              _contextRe;
52    CFMutableDictionaryRef _description;
53    CFURLRef               _device;
54    char *                 _deviceLink[2];
55    dev_t                  _deviceNode;
56    char *                 _devicePath[2];
57    SInt32                 _deviceUnit;
58    DAFileSystemRef        _filesystem;
59    char *                 _id;
60    io_service_t           _media;
61    mode_t                 _mode;
62    DADiskOptions          _options;
63    io_object_t            _propertyNotification;
64    CFDataRef              _serialization;
65    DADiskState            _state;
66    gid_t                  _userGID;
67    uid_t                  _userUID;
68};
69
70typedef struct __DADisk __DADisk;
71
72static CFStringRef __DADiskCopyDescription( CFTypeRef object );
73static CFStringRef __DADiskCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options );
74static void        __DADiskDeallocate( CFTypeRef object );
75static Boolean     __DADiskEqual( CFTypeRef object1, CFTypeRef object2 );
76static CFHashCode  __DADiskHash( CFTypeRef object );
77
78static const CFRuntimeClass __DADiskClass =
79{
80    0,
81    "DADisk",
82    NULL,
83    NULL,
84    __DADiskDeallocate,
85    __DADiskEqual,
86    __DADiskHash,
87    __DADiskCopyFormattingDescription,
88    __DADiskCopyDescription
89};
90
91static CFTypeID __kDADiskTypeID = _kCFRuntimeNotATypeID;
92
93extern CFHashCode CFHashBytes( UInt8 * bytes, CFIndex length );
94
95static CFStringRef __DADiskCopyDescription( CFTypeRef object )
96{
97    DADiskRef disk = ( DADiskRef ) object;
98
99    return CFStringCreateWithFormat( CFGetAllocator( object ), NULL, CFSTR( "<DADisk %p [%p]>{id = %s}" ), object, CFGetAllocator( object ), disk->_id );
100}
101
102static CFStringRef __DADiskCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options )
103{
104    DADiskRef disk = ( DADiskRef ) object;
105
106    return CFStringCreateWithFormat( CFGetAllocator( object ), NULL, CFSTR( "%s" ), disk->_id );
107}
108
109static DADiskRef __DADiskCreate( CFAllocatorRef allocator, const char * id )
110{
111    __DADisk * disk;
112
113    disk = ( void * ) _CFRuntimeCreateInstance( allocator, __kDADiskTypeID, sizeof( __DADisk ) - sizeof( CFRuntimeBase ), NULL );
114
115    if ( disk )
116    {
117        CFDataRef data;
118
119        disk->_busy                 = 0;
120        disk->_busyNotification     = IO_OBJECT_NULL;
121        disk->_bypath               = NULL;
122        disk->_claim                = NULL;
123        disk->_context              = NULL;
124        disk->_contextRe            = NULL;
125        disk->_description          = CFDictionaryCreateMutable( allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
126        disk->_device               = NULL;
127        disk->_deviceLink[0]        = NULL;
128        disk->_deviceLink[1]        = NULL;
129        disk->_deviceNode           = 0;
130        disk->_devicePath[0]        = NULL;
131        disk->_devicePath[1]        = NULL;
132        disk->_deviceUnit           = -1;
133        disk->_filesystem           = NULL;
134        disk->_id                   = strdup( id );
135        disk->_media                = IO_OBJECT_NULL;
136        disk->_mode                 = 0750;
137        disk->_options              = 0;
138        disk->_propertyNotification = IO_OBJECT_NULL;
139        disk->_serialization        = NULL;
140        disk->_state                = 0;
141        disk->_userGID              = ___GID_WHEEL;
142        disk->_userUID              = ___UID_ROOT;
143
144        assert( disk->_description );
145        assert( disk->_id          );
146
147        data = CFDataCreate( allocator, ( void * ) id, strlen( id ) + 1 );
148
149        if ( data )
150        {
151            CFDictionarySetValue( disk->_description, _kDADiskIDKey, data );
152
153            CFRelease( data );
154        }
155    }
156
157    return disk;
158}
159
160static void __DADiskDeallocate( CFTypeRef object )
161{
162    DADiskRef disk = ( DADiskRef ) object;
163
164    if ( disk->_busyNotification     )  IOObjectRelease( disk->_busyNotification );
165    if ( disk->_bypath               )  CFRelease( disk->_bypath );
166    if ( disk->_claim                )  CFRelease( disk->_claim );
167    if ( disk->_context              )  CFRelease( disk->_context );
168    if ( disk->_contextRe            )  CFRelease( disk->_contextRe );
169    if ( disk->_description          )  CFRelease( disk->_description );
170    if ( disk->_device               )  CFRelease( disk->_device );
171    if ( disk->_deviceLink[0]        )  free( disk->_deviceLink[0] );
172    if ( disk->_deviceLink[1]        )  free( disk->_deviceLink[1] );
173    if ( disk->_devicePath[0]        )  free( disk->_devicePath[0] );
174    if ( disk->_devicePath[1]        )  free( disk->_devicePath[1] );
175    if ( disk->_filesystem           )  CFRelease( disk->_filesystem );
176    if ( disk->_id                   )  free( disk->_id );
177    if ( disk->_media                )  IOObjectRelease( disk->_media );
178    if ( disk->_propertyNotification )  IOObjectRelease( disk->_propertyNotification );
179    if ( disk->_serialization        )  CFRelease( disk->_serialization );
180}
181
182static Boolean __DADiskEqual( CFTypeRef object1, CFTypeRef object2 )
183{
184    DADiskRef disk1 = ( DADiskRef ) object1;
185    DADiskRef disk2 = ( DADiskRef ) object2;
186
187    return ( strcmp( disk1->_id, disk2->_id ) == 0 );
188}
189
190static CFHashCode __DADiskHash( CFTypeRef object )
191{
192    DADiskRef disk = ( DADiskRef ) object;
193
194    return CFHashBytes( ( void * ) disk->_id, MIN( strlen( disk->_id ), 16 ) );
195}
196
197static void __DADiskMatch( const void * key, const void * value, void * context )
198{
199    DADiskRef disk = *( ( void * * ) context );
200
201    if ( disk )
202    {
203        if ( CFEqual( key, kDADiskDescriptionMediaMatchKey ) )
204        {
205            boolean_t match = FALSE;
206
207            IOServiceMatchPropertyTable( disk->_media, value, &match );
208
209            if ( match == FALSE )
210            {
211                *( ( void * * ) context ) = NULL;
212            }
213        }
214        else
215        {
216            CFTypeRef compare;
217
218            compare = CFDictionaryGetValue( disk->_description, key );
219
220            if ( compare == NULL || CFEqual( value, compare ) == FALSE )
221            {
222                *( ( void * * ) context ) = NULL;
223            }
224        }
225    }
226}
227
228CFComparisonResult DADiskCompareDescription( DADiskRef disk, CFStringRef description, CFTypeRef value )
229{
230    CFTypeRef object1 = CFDictionaryGetValue( disk->_description, description );
231    CFTypeRef object2 = value;
232
233    if ( object1 == object2 )  return kCFCompareEqualTo;
234    if ( object1 == NULL    )  return kCFCompareLessThan;
235    if ( object2 == NULL    )  return kCFCompareGreaterThan;
236
237    return CFEqual( object1, object2 ) ? kCFCompareEqualTo : kCFCompareLessThan;
238}
239
240DADiskRef DADiskCreateFromIOMedia( CFAllocatorRef allocator, io_service_t media )
241{
242    io_service_t           bus        = IO_OBJECT_NULL;
243    uint32_t               busy;
244    io_service_t           device     = IO_OBJECT_NULL;
245    DADiskRef              disk       = NULL;
246    UInt32                 major;
247    UInt32                 minor;
248    io_name_t              name;
249    CFMutableDictionaryRef properties = NULL;
250    CFTypeRef              object;
251    ___io_path_t           path;
252    io_iterator_t          services;
253    kern_return_t          status;
254    CFDictionaryRef        sub;
255    double                 time;
256
257    /*
258     * Obtain the media properties.
259     */
260
261    status = IORegistryEntryCreateCFProperties( media, &properties, allocator, 0 );
262    if ( status != KERN_SUCCESS )  goto DADiskCreateFromIOMediaErr;
263
264    /*
265     * Create the disk object.
266     */
267
268    object = CFDictionaryGetValue( properties, CFSTR( kIOBSDNameKey ) );
269    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
270
271    status = CFStringGetCString( object, name, sizeof( name ), kCFStringEncodingUTF8 );
272    if ( status == FALSE )  goto DADiskCreateFromIOMediaErr;
273
274    strlcpy( path, _PATH_DEV, sizeof( path ) );
275    strlcat( path, name,      sizeof( path ) );
276
277    disk = __DADiskCreate( allocator, path );
278    if ( disk == NULL )  goto DADiskCreateFromIOMediaErr;
279
280    disk->_device = CFURLCreateFromFileSystemRepresentation( allocator, ( void * ) path, strlen( path ), FALSE );
281    if ( disk->_device == NULL )  goto DADiskCreateFromIOMediaErr;
282
283    disk->_devicePath[0] = strdup( path );
284
285    strlcpy( path, _PATH_DEV, sizeof( path ) );
286    strlcat( path, "r",       sizeof( path ) );
287    strlcat( path, name,      sizeof( path ) );
288
289    disk->_devicePath[1] = strdup( path );
290
291    IOObjectRetain( media );
292
293    disk->_media = media;
294
295    CFDictionarySetValue( disk->_description, kDADiskDescriptionVolumeNetworkKey, kCFBooleanFalse );
296
297    /*
298     * Create the disk description -- media block size.
299     */
300
301    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaPreferredBlockSizeKey ) );
302    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
303
304    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaBlockSizeKey, object );
305
306    /*
307     * Create the disk description -- media BSD name.
308     */
309
310    object = CFDictionaryGetValue( properties, CFSTR( kIOBSDNameKey ) );
311    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
312
313    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaBSDNameKey, object );
314
315    /*
316     * Create the disk description -- media BSD node.
317     */
318
319    object = CFDictionaryGetValue( properties, CFSTR( kIOBSDMajorKey ) );
320    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
321
322    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaBSDMajorKey, object );
323
324    CFNumberGetValue( object, kCFNumberSInt32Type, &major );
325
326    object = CFDictionaryGetValue( properties, CFSTR( kIOBSDMinorKey ) );
327    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
328
329    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaBSDMinorKey, object );
330
331    CFNumberGetValue( object, kCFNumberSInt32Type, &minor );
332
333    disk->_deviceNode = makedev( major, minor );
334
335    /*
336     * Create the disk description -- media BSD unit.
337     */
338
339    object = CFDictionaryGetValue( properties, CFSTR( kIOBSDUnitKey ) );
340    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
341
342    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaBSDUnitKey, object );
343
344    CFNumberGetValue( object, kCFNumberSInt32Type, &disk->_deviceUnit );
345
346    /*
347     * Create the disk description -- media content.
348     */
349
350    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaContentKey ) );
351    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
352
353    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaContentKey, object );
354
355    /*
356     * Create the disk description -- media ejectable?
357     */
358
359    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaEjectableKey ) );
360    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
361
362    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaEjectableKey, object );
363
364    /*
365     * Create the disk description -- media icon.
366     */
367
368    object = IORegistryEntrySearchCFProperty( media,
369                                              kIOServicePlane,
370                                              CFSTR( kIOMediaIconKey ),
371                                              allocator,
372                                              kIORegistryIterateParents | kIORegistryIterateRecursively );
373    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
374
375    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaIconKey, object );
376    CFRelease( object );
377
378    /*
379     * Create the disk description -- media kind.
380     */
381
382    if ( IOObjectConformsTo( media, kIOBDMediaClass ) )
383    {
384        object = CFSTR( kIOBDMediaClass );
385
386        CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaKindKey, object );
387
388        /*
389         * Create the disk description -- media type.
390         */
391
392        object = CFDictionaryGetValue( properties, CFSTR( kIOBDMediaTypeKey ) );
393        if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
394
395        CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaTypeKey, object );
396    }
397    else if ( IOObjectConformsTo( media, kIODVDMediaClass ) )
398    {
399        object = CFSTR( kIODVDMediaClass );
400
401        CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaKindKey, object );
402
403        /*
404         * Create the disk description -- media type.
405         */
406
407        object = CFDictionaryGetValue( properties, CFSTR( kIODVDMediaTypeKey ) );
408        if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
409
410        CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaTypeKey, object );
411    }
412    else if ( IOObjectConformsTo( media, kIOCDMediaClass ) )
413    {
414        object = CFSTR( kIOCDMediaClass );
415
416        CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaKindKey, object );
417
418        /*
419         * Create the disk description -- media type.
420         */
421
422        object = CFDictionaryGetValue( properties, CFSTR( kIOCDMediaTypeKey ) );
423        if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
424
425        CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaTypeKey, object );
426    }
427    else
428    {
429        object = CFSTR( kIOMediaClass );
430
431        CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaKindKey, object );
432    }
433
434    /*
435     * Create the disk description -- media leaf?
436     */
437
438    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaLeafKey ) );
439    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
440
441    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaLeafKey, object );
442
443    /*
444     * Create the disk description -- media name.
445     */
446
447    status = IORegistryEntryGetName( media, name );
448    if ( status != KERN_SUCCESS )  goto DADiskCreateFromIOMediaErr;
449
450    object = CFStringCreateWithCString( allocator, name, kCFStringEncodingUTF8 );
451    if ( object == NULL )  object = CFStringCreateWithCString( allocator, name, kCFStringEncodingMacRoman );
452    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
453
454    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaNameKey, object );
455    CFRelease( object );
456
457    /*
458     * Create the disk description -- media path.
459     */
460
461    status = ___IORegistryEntryGetPath( media, kIODeviceTreePlane, path );
462    if ( status != KERN_SUCCESS )  status = ___IORegistryEntryGetPath( media, kIOServicePlane, path );
463    if ( status != KERN_SUCCESS )  goto DADiskCreateFromIOMediaErr;
464
465    object = CFStringCreateWithCString( allocator, path, kCFStringEncodingUTF8 );
466    if ( object == NULL )  object = CFStringCreateWithCString( allocator, path, kCFStringEncodingMacRoman );
467    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
468
469    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaPathKey, object );
470    CFRelease( object );
471
472    /*
473     * Create the disk description -- media removable?
474     */
475
476    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaRemovableKey ) );
477    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
478
479    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaRemovableKey, object );
480
481    /*
482     * Create the disk description -- media size.
483     */
484
485    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaSizeKey ) );
486    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
487
488    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaSizeKey, object );
489
490    /*
491     * Create the disk description -- media UUID.
492     */
493
494    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaUUIDKey ) );
495
496    if ( object )
497    {
498        object = ___CFUUIDCreateFromString( allocator, object );
499        if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
500
501        CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaUUIDKey, object );
502        CFRelease( object );
503    }
504
505    /*
506     * Create the disk description -- media whole?
507     */
508
509    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaWholeKey ) );
510    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
511
512    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaWholeKey, object );
513
514    /*
515     * Create the disk description -- media writable?
516     */
517
518    object = CFDictionaryGetValue( properties, CFSTR( kIOMediaWritableKey ) );
519    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
520
521    CFDictionarySetValue( disk->_description, kDADiskDescriptionMediaWritableKey, object );
522
523    CFRelease( properties );
524    properties = NULL;
525
526    /*
527     * Obtain the device object.
528     */
529
530    status = IORegistryEntryCreateIterator( media,
531                                            kIOServicePlane,
532                                            kIORegistryIterateParents | kIORegistryIterateRecursively,
533                                            &services );
534
535    while ( ( device = IOIteratorNext( services ) ) )
536    {
537        if ( IOObjectConformsTo( device, kIOBlockStorageDeviceClass ) )  break;
538
539        IOObjectRelease( device );
540    }
541
542    IOObjectRelease( services );
543
544    if ( device == IO_OBJECT_NULL )  goto DADiskCreateFromIOMediaErr;
545
546    /*
547     * Obtain the device properties.
548     */
549
550    status = IORegistryEntryCreateCFProperties( device, &properties, allocator, 0 );
551    if ( status != KERN_SUCCESS )  goto DADiskCreateFromIOMediaErr;
552
553    /*
554     * Obtain the device protocol subproperties.
555     */
556
557    sub = CFDictionaryGetValue( properties, CFSTR( kIOPropertyProtocolCharacteristicsKey ) );
558
559    if ( sub )
560    {
561        /*
562         * Create the disk description -- device internal?
563         */
564
565        object = CFDictionaryGetValue( sub, CFSTR( kIOPropertyPhysicalInterconnectLocationKey ) );
566
567        if ( object )
568        {
569            if ( CFStringCompare( object, CFSTR( kIOPropertyInternalKey ), 0 ) == 0 )
570            {
571                object = kCFBooleanTrue;
572
573                CFDictionarySetValue( disk->_description, kDADiskDescriptionDeviceInternalKey, object );
574            }
575            else if ( CFStringCompare( object, CFSTR( kIOPropertyExternalKey ), 0 ) == 0 )
576            {
577                object = kCFBooleanFalse;
578
579                CFDictionarySetValue( disk->_description, kDADiskDescriptionDeviceInternalKey, object );
580            }
581        }
582
583        /*
584         * Create the disk description -- device protocol.
585         */
586
587        object = CFDictionaryGetValue( sub, CFSTR( kIOPropertyPhysicalInterconnectTypeKey ) );
588
589        if ( object )
590        {
591            CFDictionarySetValue( disk->_description, kDADiskDescriptionDeviceProtocolKey, object );
592        }
593    }
594
595    /*
596     * Obtain the device model subproperties.
597     */
598
599    sub = CFDictionaryGetValue( properties, CFSTR( kIOPropertyDeviceCharacteristicsKey ) );
600
601    if ( sub )
602    {
603        /*
604         * Create the disk description -- device model.
605         */
606
607        object = CFDictionaryGetValue( sub, CFSTR( kIOPropertyProductNameKey ) );
608
609        if ( object )
610        {
611            CFDictionarySetValue( disk->_description, kDADiskDescriptionDeviceModelKey, object );
612        }
613
614        /*
615         * Create the disk description -- device revision.
616         */
617
618        object = CFDictionaryGetValue( sub, CFSTR( kIOPropertyProductRevisionLevelKey ) );
619
620        if ( object )
621        {
622            CFDictionarySetValue( disk->_description, kDADiskDescriptionDeviceRevisionKey, object );
623        }
624
625        /*
626         * Create the disk description -- device vendor.
627         */
628
629        object = CFDictionaryGetValue( sub, CFSTR( kIOPropertyVendorNameKey ) );
630
631        if ( object )
632        {
633            CFDictionarySetValue( disk->_description, kDADiskDescriptionDeviceVendorKey, object );
634        }
635    }
636
637    /*
638     * Create the disk description -- device path.
639     */
640
641    status = ___IORegistryEntryGetPath( device, kIOServicePlane, path );
642    if ( status != KERN_SUCCESS )  goto DADiskCreateFromIOMediaErr;
643
644    object = CFStringCreateWithCString( allocator, path, kCFStringEncodingUTF8 );
645    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
646
647    CFDictionarySetValue( disk->_description, kDADiskDescriptionDevicePathKey, object );
648    CFRelease( object );
649
650    /*
651     * Create the disk description -- device unit.
652     */
653
654    object = IORegistryEntrySearchCFProperty( device,
655                                              kIOServicePlane,
656                                              CFSTR( "IOUnit" ),
657                                              allocator,
658                                              kIORegistryIterateParents | kIORegistryIterateRecursively );
659
660    if ( object )
661    {
662        CFDictionarySetValue( disk->_description, kDADiskDescriptionDeviceUnitKey, object );
663        CFRelease( object );
664    }
665
666    /*
667     * Create the disk description -- device GUID (IEEE EUI-64).
668     */
669
670    object = IORegistryEntrySearchCFProperty( device,
671                                              kIOServicePlane,
672                                              CFSTR( "GUID" ),
673                                              allocator,
674                                              kIORegistryIterateParents | kIORegistryIterateRecursively );
675
676    if ( object )
677    {
678        UInt64 value;
679
680        CFNumberGetValue( object, kCFNumberSInt64Type, &value );
681        CFRelease( object );
682
683        value = OSSwapHostToBigInt64( value );
684
685        object = CFDataCreate( allocator, ( void * ) &value, sizeof( value ) );
686        if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
687
688        CFDictionarySetValue( disk->_description, kDADiskDescriptionDeviceGUIDKey, object );
689        CFRelease( object );
690    }
691
692    CFRelease( properties );
693    properties = NULL;
694
695    /*
696     * Obtain the bus object.
697     */
698
699    status = IORegistryEntryCreateIterator( device,
700                                            kIOServicePlane,
701                                            kIORegistryIterateParents | kIORegistryIterateRecursively,
702                                            &services );
703
704    while ( ( bus = IOIteratorNext( services ) ) )
705    {
706        if ( IORegistryEntryInPlane( bus, kIODeviceTreePlane ) )  break;
707
708        IOObjectRelease( bus );
709    }
710
711    IOObjectRelease( services );
712
713    if ( bus )
714    {
715        /*
716         * Create the disk description -- bus name.
717         */
718
719        status = IORegistryEntryGetNameInPlane( bus, kIODeviceTreePlane, name );
720        if ( status != KERN_SUCCESS )  goto DADiskCreateFromIOMediaErr;
721
722        object = CFStringCreateWithCString( allocator, name, kCFStringEncodingUTF8 );
723        if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
724
725        CFDictionarySetValue( disk->_description, kDADiskDescriptionBusNameKey, object );
726        CFRelease( object );
727
728        /*
729         * Create the disk description -- bus path.
730         */
731
732        status = ___IORegistryEntryGetPath( bus, kIODeviceTreePlane, path );
733        if ( status != KERN_SUCCESS )  goto DADiskCreateFromIOMediaErr;
734
735        object = CFStringCreateWithCString( allocator, path, kCFStringEncodingUTF8 );
736        if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
737
738        CFDictionarySetValue( disk->_description, kDADiskDescriptionBusPathKey, object );
739        CFRelease( object );
740
741        IOObjectRelease( bus );
742        bus = IO_OBJECT_NULL;
743    }
744
745    /*
746     * Create the disk description -- appearance time.
747     */
748
749    time = CFAbsoluteTimeGetCurrent( );
750
751    object = CFNumberCreate( allocator, kCFNumberDoubleType, &time );
752    if ( object == NULL )  goto DADiskCreateFromIOMediaErr;
753
754    CFDictionarySetValue( disk->_description, kDADiskDescriptionAppearanceTimeKey, object );
755    CFRelease( object );
756
757    /*
758     * Create the disk state -- busy?
759     */
760
761    busy = 0;
762
763    IOServiceGetBusyState( media, &busy );
764
765    if ( busy )
766    {
767        disk->_busy = CFAbsoluteTimeGetCurrent( );
768    }
769
770    /*
771     * Create the disk state -- mount automatic?
772     */
773
774    object = IORegistryEntrySearchCFProperty( media,
775                                              kIOServicePlane,
776                                              CFSTR( "autodiskmount" ),
777                                              allocator,
778                                              kIORegistryIterateParents | kIORegistryIterateRecursively );
779
780    if ( object == NULL )
781    {
782        disk->_options |= kDADiskOptionMountAutomatic;
783    }
784    else if ( object == kCFBooleanTrue )
785    {
786        disk->_options |= kDADiskOptionMountAutomatic | kDADiskOptionMountAutomaticNoDefer;
787    }
788
789    if ( object )  CFRelease( object );
790
791    /*
792     * Create the disk state -- eject upon logout?
793     */
794
795    object = IORegistryEntrySearchCFProperty( device,
796                                              kIOServicePlane,
797                                              CFSTR( "eject-upon-logout" ),
798                                              allocator,
799                                              kIORegistryIterateParents | kIORegistryIterateRecursively );
800
801    if ( object == kCFBooleanTrue )
802    {
803        disk->_options |= kDADiskOptionEjectUponLogout;
804    }
805
806    if ( object )  CFRelease( object );
807
808    /*
809     * Create the disk state -- owner.
810     */
811
812    object = IORegistryEntrySearchCFProperty( media,
813                                              kIOServicePlane,
814                                              CFSTR( "owner-uid" ),
815                                              allocator,
816                                              kIORegistryIterateParents | kIORegistryIterateRecursively );
817
818    if ( object )
819    {
820        if ( CFGetTypeID( object ) == CFNumberGetTypeID( ) )
821        {
822            struct passwd * user;
823            int             value;
824
825            CFNumberGetValue( object, kCFNumberIntType, &value );
826
827            disk->_userUID = value;
828
829            user = getpwuid( value );
830
831            if ( user )
832            {
833                disk->_userGID = user->pw_gid;
834            }
835        }
836
837        CFRelease( object );
838    }
839
840    object = IORegistryEntrySearchCFProperty( media,
841                                              kIOServicePlane,
842                                              CFSTR( "owner-gid" ),
843                                              allocator,
844                                              kIORegistryIterateParents | kIORegistryIterateRecursively );
845
846    if ( object )
847    {
848        if ( CFGetTypeID( object ) == CFNumberGetTypeID( ) )
849        {
850            int value;
851
852            CFNumberGetValue( object, kCFNumberIntType, &value );
853
854            disk->_userGID = value;
855        }
856
857        CFRelease( object );
858    }
859
860    object = IORegistryEntrySearchCFProperty( media,
861                                              kIOServicePlane,
862                                              CFSTR( "owner-mode" ),
863                                              allocator,
864                                              kIORegistryIterateParents | kIORegistryIterateRecursively );
865
866    if ( object )
867    {
868        if ( CFGetTypeID( object ) == CFNumberGetTypeID( ) )
869        {
870            int value;
871
872            CFNumberGetValue( object, kCFNumberIntType, &value );
873
874            disk->_mode = value;
875        }
876
877        CFRelease( object );
878    }
879
880    /*
881     * Create the disk state -- media BSD link.
882     */
883
884    object = IORegistryEntrySearchCFProperty( media,
885                                              kIOServicePlane,
886                                              CFSTR( "dev-name" ),
887                                              allocator,
888                                              0 );
889
890    if ( object )
891    {
892        if ( CFGetTypeID( object ) == CFStringGetTypeID( ) )
893        {
894            if ( CFStringGetCString( object, name, sizeof( name ), kCFStringEncodingUTF8 ) )
895            {
896                strlcpy( path, _PATH_DEV, sizeof( path ) );
897                strlcat( path, name,      sizeof( path ) );
898
899                disk->_deviceLink[0] = strdup( path );
900
901                strlcpy( path, _PATH_DEV, sizeof( path ) );
902                strlcat( path, "r",       sizeof( path ) );
903                strlcat( path, name,      sizeof( path ) );
904
905                disk->_deviceLink[1] = strdup( path );
906            }
907        }
908
909        CFRelease( object );
910    }
911
912    IOObjectRelease( device );
913
914    return disk;
915
916DADiskCreateFromIOMediaErr:
917
918    if ( ___IORegistryEntryGetPath( media, kIOServicePlane, path ) == KERN_SUCCESS )
919    {
920        DALogError( "unable to create disk, id = %s.", disk ? DADiskGetID( disk ) : NULL );
921    }
922
923    if ( bus        )  IOObjectRelease( bus );
924    if ( device     )  IOObjectRelease( device );
925    if ( disk       )  CFRelease( disk );
926    if ( properties )  CFRelease( properties );
927
928    return NULL;
929}
930
931DADiskRef DADiskCreateFromVolumePath( CFAllocatorRef allocator, const struct statfs * fs )
932{
933    DADiskRef disk;
934
935    disk = NULL;
936
937    if ( fs )
938    {
939        CFURLRef path;
940
941        path = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) fs->f_mntonname, strlen( fs->f_mntonname ), TRUE );
942
943        if ( path )
944        {
945            CFStringRef kind;
946
947            kind = CFStringCreateWithCString( kCFAllocatorDefault, fs->f_fstypename, kCFStringEncodingUTF8 );
948
949            if ( kind )
950            {
951                char * id;
952
953                id = _DAVolumeCopyID( fs );
954
955                if ( id )
956                {
957                    CFTypeRef object;
958
959                    disk = __DADiskCreate( allocator, id );
960
961                    if ( disk )
962                    {
963                        struct passwd * user;
964
965                        disk->_bypath = CFRetain( path );
966
967                        CFDictionarySetValue( disk->_description, kDADiskDescriptionVolumePathKey, path );
968
969                        CFDictionarySetValue( disk->_description, kDADiskDescriptionVolumeMountableKey, kCFBooleanTrue );
970
971                        CFDictionarySetValue( disk->_description, kDADiskDescriptionVolumeKindKey, kind );
972
973                        object = _DAFileSystemCopyName( NULL, path );
974
975                        if ( object )
976                        {
977                            CFDictionarySetValue( disk->_description, kDADiskDescriptionVolumeNameKey, object );
978
979                            CFRelease( object );
980                        }
981
982                        if ( ( fs->f_flags & MNT_LOCAL ) )
983                        {
984                            CFDictionarySetValue( disk->_description, kDADiskDescriptionVolumeNetworkKey, kCFBooleanFalse );
985                        }
986                        else
987                        {
988                            CFDictionarySetValue( disk->_description, kDADiskDescriptionVolumeNetworkKey, kCFBooleanTrue );
989                        }
990
991                        disk->_options |= kDADiskOptionMountAutomatic;
992                        disk->_options |= kDADiskOptionMountAutomaticNoDefer;
993
994                        disk->_state |= kDADiskStateStagedProbe;
995                        disk->_state |= kDADiskStateStagedPeek;
996                        disk->_state |= kDADiskStateStagedApprove;
997                        disk->_state |= kDADiskStateStagedAuthorize;
998                        disk->_state |= kDADiskStateStagedMount;
999
1000                        disk->_userUID = fs->f_owner;
1001
1002                        user = getpwuid( fs->f_owner );
1003
1004                        if ( user )
1005                        {
1006                            disk->_userGID = user->pw_gid;
1007                        }
1008                    }
1009
1010                    free( id );
1011                }
1012
1013                CFRelease( kind );
1014            }
1015
1016            CFRelease( path );
1017        }
1018    }
1019
1020    return disk;
1021}
1022
1023CFAbsoluteTime DADiskGetBusy( DADiskRef disk )
1024{
1025    return disk->_busy;
1026}
1027
1028io_object_t DADiskGetBusyNotification( DADiskRef disk )
1029{
1030    return disk->_busyNotification;
1031}
1032
1033CFURLRef DADiskGetBypath( DADiskRef disk )
1034{
1035    return disk->_bypath;
1036}
1037
1038const char * DADiskGetBSDLink( DADiskRef disk, Boolean raw )
1039{
1040    return disk->_deviceLink[ raw ? 1 : 0 ];
1041}
1042
1043dev_t DADiskGetBSDNode( DADiskRef disk )
1044{
1045    return disk->_deviceNode;
1046}
1047
1048const char * DADiskGetBSDPath( DADiskRef disk, Boolean raw )
1049{
1050    return disk->_devicePath[ raw ? 1 : 0 ];
1051}
1052
1053UInt32 DADiskGetBSDUnit( DADiskRef disk )
1054{
1055    return disk->_deviceUnit;
1056}
1057
1058DACallbackRef DADiskGetClaim( DADiskRef disk )
1059{
1060    return disk->_claim;
1061}
1062
1063CFTypeRef DADiskGetContext( DADiskRef disk )
1064{
1065    return disk->_context;
1066}
1067
1068CFTypeRef DADiskGetContextRe( DADiskRef disk )
1069{
1070    return disk->_contextRe;
1071}
1072
1073CFTypeRef DADiskGetDescription( DADiskRef disk, CFStringRef description )
1074{
1075    return CFDictionaryGetValue( disk->_description, description );
1076}
1077
1078CFURLRef DADiskGetDevice( DADiskRef disk )
1079{
1080    return disk->_device;
1081}
1082
1083DAFileSystemRef DADiskGetFileSystem( DADiskRef disk )
1084{
1085    return disk->_filesystem;
1086}
1087
1088const char * DADiskGetID( DADiskRef disk )
1089{
1090    return disk->_id;
1091}
1092
1093io_service_t DADiskGetIOMedia( DADiskRef disk )
1094{
1095    return disk->_media;
1096}
1097
1098mode_t DADiskGetMode( DADiskRef disk )
1099{
1100    mode_t mode;
1101
1102    mode = disk->_mode;
1103
1104    if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanFalse )
1105    {
1106        mode &= 0555;
1107    }
1108
1109    return mode;
1110}
1111
1112Boolean DADiskGetOption( DADiskRef disk, DADiskOption option )
1113{
1114    return ( disk->_options & option ) ? TRUE : FALSE;
1115}
1116
1117DADiskOptions DADiskGetOptions( DADiskRef disk )
1118{
1119    return disk->_options;
1120}
1121
1122io_object_t DADiskGetPropertyNotification( DADiskRef disk )
1123{
1124    return disk->_propertyNotification;
1125}
1126
1127CFDataRef DADiskGetSerialization( DADiskRef disk )
1128{
1129    if ( disk->_serialization == NULL )
1130    {
1131        disk->_serialization = _DASerializeDiskDescription( CFGetAllocator( disk ), disk->_description );
1132    }
1133
1134    return disk->_serialization;
1135}
1136
1137Boolean DADiskGetState( DADiskRef disk, DADiskState state )
1138{
1139    return ( disk->_state & state ) ? TRUE : FALSE;
1140}
1141
1142CFTypeID DADiskGetTypeID( void )
1143{
1144    return __kDADiskTypeID;
1145}
1146
1147gid_t DADiskGetUserGID( DADiskRef disk )
1148{
1149    return disk->_userGID;
1150}
1151
1152uid_t DADiskGetUserUID( DADiskRef disk )
1153{
1154    return disk->_userUID;
1155}
1156
1157void DADiskInitialize( void )
1158{
1159    __kDADiskTypeID = _CFRuntimeRegisterClass( &__DADiskClass );
1160}
1161
1162Boolean DADiskMatch( DADiskRef disk, CFDictionaryRef match )
1163{
1164    CFDictionaryApplyFunction( match, __DADiskMatch, &disk );
1165
1166    return disk ? TRUE : FALSE;
1167}
1168
1169void DADiskSetBusy( DADiskRef disk, CFAbsoluteTime busy )
1170{
1171    disk->_busy = busy;
1172}
1173
1174void DADiskSetBusyNotification( DADiskRef disk, io_object_t notification )
1175{
1176    if ( disk->_busyNotification )
1177    {
1178        IOObjectRelease( disk->_busyNotification );
1179
1180        disk->_busyNotification = IO_OBJECT_NULL;
1181    }
1182
1183    if ( notification )
1184    {
1185        IOObjectRetain( notification );
1186
1187        disk->_busyNotification = notification;
1188    }
1189}
1190
1191void DADiskSetBypath( DADiskRef disk, CFURLRef bypath )
1192{
1193    if ( disk->_bypath )
1194    {
1195        CFRelease( disk->_bypath );
1196
1197        disk->_bypath = NULL;
1198    }
1199
1200    if ( bypath )
1201    {
1202        CFRetain( bypath );
1203
1204        disk->_bypath = bypath;
1205    }
1206}
1207
1208void DADiskSetBSDLink( DADiskRef disk, Boolean raw, const char * link )
1209{
1210    if ( disk->_deviceLink[ raw ? 1 : 0 ] )
1211    {
1212        free( disk->_deviceLink[ raw ? 1 : 0 ] );
1213
1214        disk->_deviceLink[ raw ? 1 : 0 ] = NULL;
1215    }
1216
1217    if ( link )
1218    {
1219        disk->_deviceLink[ raw ? 1 : 0 ] = strdup( link );
1220    }
1221}
1222
1223void DADiskSetClaim( DADiskRef disk, DACallbackRef claim )
1224{
1225    if ( disk->_claim )
1226    {
1227        CFRelease( disk->_claim );
1228
1229        disk->_claim = NULL;
1230    }
1231
1232    if ( claim )
1233    {
1234        CFRetain( claim );
1235
1236        disk->_claim = claim;
1237    }
1238}
1239
1240void DADiskSetContext( DADiskRef disk, CFTypeRef context )
1241{
1242    if ( disk->_context )
1243    {
1244        CFRelease( disk->_context );
1245
1246        disk->_context = NULL;
1247    }
1248
1249    if ( context )
1250    {
1251        CFRetain( context );
1252
1253        disk->_context = context;
1254    }
1255}
1256
1257void DADiskSetContextRe( DADiskRef disk, CFTypeRef context )
1258{
1259    if ( disk->_contextRe )
1260    {
1261        CFRelease( disk->_contextRe );
1262
1263        disk->_contextRe = NULL;
1264    }
1265
1266    if ( context )
1267    {
1268        CFRetain( context );
1269
1270        disk->_contextRe = context;
1271    }
1272}
1273
1274void DADiskSetDescription( DADiskRef disk, CFStringRef description, CFTypeRef value )
1275{
1276    if ( value )
1277    {
1278        CFDictionarySetValue( disk->_description, description, value );
1279    }
1280    else
1281    {
1282        CFDictionaryRemoveValue( disk->_description, description );
1283    }
1284
1285    if ( disk->_serialization )
1286    {
1287        CFRelease( disk->_serialization );
1288
1289        disk->_serialization = NULL;
1290    }
1291}
1292
1293void DADiskSetFileSystem( DADiskRef disk, DAFileSystemRef filesystem )
1294{
1295    if ( disk->_filesystem )
1296    {
1297        CFRelease( disk->_filesystem );
1298
1299        disk->_filesystem = NULL;
1300    }
1301
1302    if ( filesystem )
1303    {
1304        CFRetain( filesystem );
1305
1306        disk->_filesystem = filesystem;
1307    }
1308}
1309
1310void DADiskSetOption( DADiskRef disk, DADiskOption option, Boolean value )
1311{
1312    DADiskSetOptions( disk, option, value );
1313}
1314
1315void DADiskSetOptions( DADiskRef disk, DADiskOptions options, Boolean value )
1316{
1317    disk->_options &= ~options;
1318    disk->_options |= value ? options : 0;
1319}
1320
1321void DADiskSetPropertyNotification( DADiskRef disk, io_object_t notification )
1322{
1323    if ( disk->_propertyNotification )
1324    {
1325        IOObjectRelease( disk->_propertyNotification );
1326
1327        disk->_propertyNotification = IO_OBJECT_NULL;
1328    }
1329
1330    if ( notification )
1331    {
1332        IOObjectRetain( notification );
1333
1334        disk->_propertyNotification = notification;
1335    }
1336}
1337
1338void DADiskSetState( DADiskRef disk, DADiskState state, Boolean value )
1339{
1340    disk->_state &= ~state;
1341    disk->_state |= value ? state : 0;
1342}
1343