1/*
2 * Copyright (c) 1998-2014 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 <IOKit/IODeviceTreeSupport.h>
25#include <IOKit/IOMessage.h>
26#include <IOKit/storage/IOPartitionScheme.h>
27
28#define super IOStorage
29OSDefineMetaClassAndStructors(IOPartitionScheme, IOStorage)
30
31#ifndef __LP64__
32extern IOStorageAttributes gIOStorageAttributesUnsupported;
33#endif /* !__LP64__ */
34
35static SInt32 partitionComparison( const OSMetaClassBase * object1,
36                                   const OSMetaClassBase * object2,
37                                   void *                  context )
38{
39    //
40    // Internal comparison routine for sorting partitions in an ordered set.
41    //
42
43    UInt64 base1 = ( ( IOMedia * ) object1 )->getBase( );
44    UInt64 base2 = ( ( IOMedia * ) object2 )->getBase( );
45
46#if TARGET_OS_EMBEDDED
47    OSString * uuid1 = OSDynamicCast( OSString, ( ( IOMedia * ) object1 )->getProperty( kIOMediaUUIDKey ) );
48    OSString * uuid2 = OSDynamicCast( OSString, ( ( IOMedia * ) object2 )->getProperty( kIOMediaUUIDKey ) );
49
50    if ( uuid1 || uuid2 )
51    {
52        if ( uuid1 == 0 ) return -1;
53        if ( uuid2 == 0 ) return  1;
54
55        return strcmp( uuid2->getCStringNoCopy( ), uuid1->getCStringNoCopy( ) );
56    }
57#endif /* TARGET_OS_EMBEDDED */
58
59    if ( base1 < base2 ) return  1;
60    if ( base1 > base2 ) return -1;
61
62    return 0;
63}
64
65IOMedia * IOPartitionScheme::getProvider() const
66{
67    //
68    // Obtain this object's provider.  We override the superclass's method
69    // to return a more specific subclass of OSObject -- an IOMedia.  This
70    // method serves simply as a convenience to subclass developers.
71    //
72
73    return (IOMedia *) IOService::getProvider();
74}
75
76bool IOPartitionScheme::init(OSDictionary * properties)
77{
78    //
79    // Initialize this object's minimal state.
80    //
81
82    if (super::init(properties) == false)  return false;
83
84    _openLevel         = kIOStorageAccessNone;
85    _openReaders       = OSSet::withCapacity(16);
86    _openReaderWriters = OSSet::withCapacity(16);
87
88    if (_openReaders == 0 || _openReaderWriters == 0) return false;
89
90    return true;
91}
92
93void IOPartitionScheme::free()
94{
95    //
96    // Free all of this object's outstanding resources.
97    //
98
99    if (_openReaders)        _openReaders->release();
100    if (_openReaderWriters)  _openReaderWriters->release();
101
102    super::free();
103}
104
105bool IOPartitionScheme::handleOpen(IOService *  client,
106                                   IOOptionBits options,
107                                   void *       argument)
108{
109    //
110    // The handleOpen method grants or denies permission to access this object
111    // to an interested client.  The argument is an IOStorageAccess value that
112    // specifies the level of access desired -- reader or reader-writer.
113    //
114    // This method can be invoked to upgrade or downgrade the access level for
115    // an existing client as well.  The previous access level will prevail for
116    // upgrades that fail, of course.   A downgrade should never fail.  If the
117    // new access level should be the same as the old for a given client, this
118    // method will do nothing and return success.  In all cases, one, singular
119    // close-per-client is expected for all opens-per-client received.
120    //
121    // This implementation replaces the IOService definition of handleOpen().
122    //
123    // We are guaranteed that no other opens or closes will be processed until
124    // we make our decision, change our state, and return from this method.
125    //
126
127    IOStorageAccess access  = (uintptr_t) argument;
128    IOStorageAccess level;
129
130    assert(client);
131    assert( access == kIOStorageAccessReader       ||
132            access == kIOStorageAccessReaderWriter );
133
134    //
135    // A partition scheme multiplexes the opens it receives from several clients
136    // and sends one open to the level below that satisfies the highest level of
137    // access.
138    //
139
140    unsigned writers = _openReaderWriters->getCount();
141
142    if (_openReaderWriters->containsObject(client))  writers--;
143    if (access == kIOStorageAccessReaderWriter)  writers++;
144
145    level = (writers) ? kIOStorageAccessReaderWriter : kIOStorageAccessReader;
146
147    //
148    // Determine whether the levels below us accept this open or not (we avoid
149    // the open if the required access is the access we already hold).
150    //
151
152    if ( _openLevel != level )
153    {
154        IOStorage * provider;
155
156        provider = OSDynamicCast( IOStorage, getProvider( ) );
157
158        if ( provider )
159        {
160            bool success;
161
162            level = ( level | kIOStorageAccessSharedLock );
163
164            success = provider->open( this, options, level );
165
166            level = ( level & kIOStorageAccessReaderWriter );
167
168            if ( success == false )
169            {
170                return false;
171            }
172        }
173    }
174
175    //
176    // Process the open.
177    //
178
179    if (access == kIOStorageAccessReader)
180    {
181        _openReaders->setObject(client);
182
183        _openReaderWriters->removeObject(client);           // (for a downgrade)
184    }
185    else // (access == kIOStorageAccessReaderWriter)
186    {
187        _openReaderWriters->setObject(client);
188
189        _openReaders->removeObject(client);                  // (for an upgrade)
190    }
191
192    _openLevel = level;
193
194    return true;
195}
196
197bool IOPartitionScheme::handleIsOpen(const IOService * client) const
198{
199    //
200    // The handleIsOpen method determines whether the specified client, or any
201    // client if none is specificed, presently has an open on this object.
202    //
203    // This implementation replaces the IOService definition of handleIsOpen().
204    //
205    // We are guaranteed that no other opens or closes will be processed until
206    // we return from this method.
207    //
208
209    if (client == 0)  return (_openLevel != kIOStorageAccessNone);
210
211    return ( _openReaderWriters->containsObject(client) ||
212             _openReaders->containsObject(client)       );
213}
214
215void IOPartitionScheme::handleClose(IOService * client, IOOptionBits options)
216{
217    //
218    // The handleClose method closes the client's access to this object.
219    //
220    // This implementation replaces the IOService definition of handleClose().
221    //
222    // We are guaranteed that no other opens or closes will be processed until
223    // we change our state and return from this method.
224    //
225
226    assert(client);
227
228    //
229    // Process the close.
230    //
231
232    if (_openReaderWriters->containsObject(client))  // (is it a reader-writer?)
233    {
234        _openReaderWriters->removeObject(client);
235    }
236    else if (_openReaders->containsObject(client))  // (is the client a reader?)
237    {
238        _openReaders->removeObject(client);
239    }
240    else                                      // (is the client is an imposter?)
241    {
242        assert(0);
243        return;
244    }
245
246    //
247    // Reevaluate the open we have on the level below us.  If no opens remain,
248    // we close, or if no reader-writer remains, but readers do, we downgrade.
249    //
250
251    IOStorageAccess level;
252
253    if (_openReaderWriters->getCount())  level = kIOStorageAccessReaderWriter;
254    else if (_openReaders->getCount())   level = kIOStorageAccessReader;
255    else                                 level = kIOStorageAccessNone;
256
257    if ( _openLevel != level )
258    {
259        IOStorage * provider;
260
261        provider = OSDynamicCast( IOStorage, getProvider( ) );
262
263        if ( provider )
264        {
265            if ( level == kIOStorageAccessNone )
266            {
267                provider->close( this, options );
268            }
269            else
270            {
271                bool success;
272
273                level = ( level | kIOStorageAccessSharedLock );
274
275                success = provider->open( this, 0, level );
276
277                level = ( level & kIOStorageAccessReaderWriter );
278
279                assert( success );
280            }
281         }
282
283         _openLevel = level;
284    }
285}
286
287void IOPartitionScheme::read(IOService *           client,
288                             UInt64                byteStart,
289                             IOMemoryDescriptor *  buffer,
290                             IOStorageAttributes * attributes,
291                             IOStorageCompletion * completion)
292{
293    //
294    // Read data from the storage object at the specified byte offset into the
295    // specified buffer, asynchronously.   When the read completes, the caller
296    // will be notified via the specified completion action.
297    //
298    // The buffer will be retained for the duration of the read.
299    //
300    // For simple partition schemes, the default behavior is to simply pass the
301    // read through to the provider media.  More complex partition schemes such
302    // as RAID will need to do extra processing here.
303    //
304
305#ifndef __LP64__
306    if ( IOStorage::_expansionData )
307    {
308        if ( attributes == &gIOStorageAttributesUnsupported )
309        {
310            attributes = NULL;
311        }
312        else
313        {
314            IOStorage::read( client, byteStart, buffer, attributes, completion );
315
316            return;
317        }
318    }
319#endif /* !__LP64__ */
320
321    getProvider( )->read( this, byteStart, buffer, attributes, completion );
322}
323
324void IOPartitionScheme::write(IOService *           client,
325                              UInt64                byteStart,
326                              IOMemoryDescriptor *  buffer,
327                              IOStorageAttributes * attributes,
328                              IOStorageCompletion * completion)
329{
330    //
331    // Write data into the storage object at the specified byte offset from the
332    // specified buffer, asynchronously.   When the write completes, the caller
333    // will be notified via the specified completion action.
334    //
335    // The buffer will be retained for the duration of the write.
336    //
337    // For simple partition schemes, the default behavior is to simply pass the
338    // write through to the provider media. More complex partition schemes such
339    // as RAID will need to do extra processing here.
340    //
341
342#ifndef __LP64__
343    if ( IOStorage::_expansionData )
344    {
345        if ( attributes == &gIOStorageAttributesUnsupported )
346        {
347            attributes = NULL;
348        }
349        else
350        {
351            IOStorage::write( client, byteStart, buffer, attributes, completion );
352
353            return;
354        }
355    }
356#endif /* !__LP64__ */
357
358    getProvider( )->write( this, byteStart, buffer, attributes, completion );
359}
360
361IOReturn IOPartitionScheme::synchronizeCache(IOService * client)
362{
363    //
364    // Flush the cached data in the storage object, if any, synchronously.
365    //
366
367    return getProvider()->synchronizeCache(this);
368}
369
370IOReturn IOPartitionScheme::unmap(IOService *       client,
371                                  IOStorageExtent * extents,
372                                  UInt32            extentsCount,
373                                  UInt32            options)
374{
375    //
376    // Delete unused data from the storage object at the specified byte offsets,
377    // synchronously.
378    //
379
380    return getProvider( )->unmap( this, extents, extentsCount, options );
381}
382
383bool IOPartitionScheme::lockPhysicalExtents(IOService * client)
384{
385    //
386    // Lock the contents of the storage object against relocation temporarily,
387    // for the purpose of getting physical extents.
388    //
389
390    return getProvider( )->lockPhysicalExtents( this );
391}
392
393IOStorage * IOPartitionScheme::copyPhysicalExtent(IOService * client,
394                                                  UInt64 *    byteStart,
395                                                  UInt64 *    byteCount)
396{
397    //
398    // Convert the specified byte offset into a physical byte offset, relative
399    // to a physical storage object.  This call should only be made within the
400    // context of lockPhysicalExtents().
401    //
402
403    return getProvider( )->copyPhysicalExtent( this, byteStart, byteCount );
404}
405
406void IOPartitionScheme::unlockPhysicalExtents(IOService * client)
407{
408    //
409    // Unlock the contents of the storage object for relocation again.  This
410    // call must balance a successful call to lockPhysicalExtents().
411    //
412
413    getProvider( )->unlockPhysicalExtents( this );
414}
415
416IOReturn IOPartitionScheme::setPriority(IOService *       client,
417                                        IOStorageExtent * extents,
418                                        UInt32            extentsCount,
419                                        IOStoragePriority priority)
420{
421    //
422    // Reprioritize read or write requests at the specified byte offsets.
423    //
424
425    return getProvider( )->setPriority( this, extents, extentsCount, priority );
426}
427
428#ifdef __LP64__
429bool IOPartitionScheme::attachMediaObjectToDeviceTree(IOMedia * media)
430#else /* !__LP64__ */
431bool IOPartitionScheme::attachMediaObjectToDeviceTree(IOMedia *    media,
432                                                      IOOptionBits options)
433#endif /* !__LP64__ */
434{
435    //
436    // Attach the given media object to the device tree plane.
437    //
438
439    IORegistryEntry * parent = this;
440
441    while ( (parent = parent->getParentEntry(gIOServicePlane)) )
442    {
443        if ( parent->inPlane(gIODTPlane) )
444        {
445            char         location[ 32 ];
446            const char * locationOfParent = parent->getLocation(gIODTPlane);
447            const char * nameOfParent     = parent->getName(gIODTPlane);
448
449            if ( locationOfParent == 0 )  break;
450
451            if ( OSDynamicCast(IOMedia, parent) == 0 )  break;
452
453            parent = parent->getParentEntry(gIODTPlane);
454
455            if ( parent == 0 )  break;
456
457            if ( media->attachToParent(parent, gIODTPlane) == false )  break;
458
459            strlcpy(location, locationOfParent, sizeof(location));
460            if ( strchr(location, ':') )  *(strchr(location, ':') + 1) = 0;
461            strlcat(location, media->getLocation(), sizeof(location) - strlen(location));
462            media->setLocation(location, gIODTPlane);
463            media->setName(nameOfParent, gIODTPlane);
464
465            return true;
466        }
467    }
468
469    return false;
470}
471
472#ifdef __LP64__
473void IOPartitionScheme::detachMediaObjectFromDeviceTree(IOMedia * media)
474#else /* !__LP64__ */
475void IOPartitionScheme::detachMediaObjectFromDeviceTree(IOMedia *    media,
476                                                        IOOptionBits options)
477#endif /* !__LP64__ */
478{
479    //
480    // Detach the given media object from the device tree plane.
481    //
482
483    IORegistryEntry * parent;
484
485    if ( (parent = media->getParentEntry(gIODTPlane)) )
486    {
487        media->detachFromParent(parent, gIODTPlane);
488    }
489}
490
491OSSet * IOPartitionScheme::juxtaposeMediaObjects(OSSet * partitionsOld,
492                                                 OSSet * partitionsNew)
493{
494    //
495    // Updates a set of existing partitions, represented by partitionsOld,
496    // with possible updates from a rescan of the disk, represented by
497    // partitionsNew.  It returns a new set of partitions with the results,
498    // removing partitions from partitionsOld where applicable, adding
499    // partitions from partitionsNew where applicable, and folding in property
500    // changes to partitions from partitionsNew into partitionsOld where
501    // applicable.
502    //
503
504    OSIterator *   iterator    = 0;
505    OSIterator *   iterator1   = 0;
506    OSIterator *   iterator2   = 0;
507    OSSymbol *     key;
508    OSSet *        keys        = 0;
509    IOMedia *      partition;
510    IOMedia *      partition1;
511    IOMedia *      partition2;
512    OSSet *        partitions  = 0;
513    OSOrderedSet * partitions1 = 0;
514    OSOrderedSet * partitions2 = 0;
515    UInt32         partitionID = 0;
516    OSDictionary * properties;
517
518    // Allocate a set to hold the set of media objects representing partitions.
519
520    partitions = OSSet::withCapacity( partitionsNew->getCapacity( ) );
521    if ( partitions == 0 ) goto juxtaposeErr;
522
523    // Prepare the reference set of partitions.
524
525    partitions1 = OSOrderedSet::withCapacity( partitionsOld->getCapacity( ), partitionComparison, 0 );
526    if ( partitions1 == 0 ) goto juxtaposeErr;
527
528    iterator1 = OSCollectionIterator::withCollection( partitionsOld );
529    if ( iterator1 == 0 ) goto juxtaposeErr;
530
531    while ( ( partition1 = ( IOMedia * ) iterator1->getNextObject( ) ) )
532    {
533        partitionID = max( partitionID, strtoul( partition1->getLocation( ), NULL, 10 ) );
534
535        partitions1->setObject( partition1 );
536    }
537
538    iterator1->release( );
539    iterator1 = 0;
540
541    // Prepare the comparison set of partitions.
542
543    partitions2 = OSOrderedSet::withCapacity( partitionsNew->getCapacity( ), partitionComparison, 0 );
544    if ( partitions2 == 0 ) goto juxtaposeErr;
545
546    iterator2 = OSCollectionIterator::withCollection( partitionsNew );
547    if ( iterator2 == 0 ) goto juxtaposeErr;
548
549    while ( ( partition2 = ( IOMedia * ) iterator2->getNextObject( ) ) )
550    {
551        partitionID = max( partitionID, strtoul( partition2->getLocation( ), NULL, 10 ) );
552
553        partitions2->setObject( partition2 );
554    }
555
556    iterator2->release( );
557    iterator2 = 0;
558
559    // Juxtapose the partitions.
560
561    iterator1 = OSCollectionIterator::withCollection( partitions1 );
562    if ( iterator1 == 0 ) goto juxtaposeErr;
563
564    iterator2 = OSCollectionIterator::withCollection( partitions2 );
565    if ( iterator2 == 0 ) goto juxtaposeErr;
566
567    partition1 = ( IOMedia * ) iterator1->getNextObject( );
568    partition2 = ( IOMedia * ) iterator2->getNextObject( );
569
570    while ( partition1 || partition2 )
571    {
572        UInt64 base1;
573        UInt64 base2;
574
575        base1 = partition1 ? partition1->getBase( ) : UINT64_MAX;
576        base2 = partition2 ? partition2->getBase( ) : UINT64_MAX;
577
578#if TARGET_OS_EMBEDDED
579        if ( partition1 && partition2 )
580        {
581            OSString * uuid1;
582            OSString * uuid2;
583
584            uuid1 = OSDynamicCast( OSString, partition1->getProperty( kIOMediaUUIDKey ) );
585            uuid2 = OSDynamicCast( OSString, partition2->getProperty( kIOMediaUUIDKey ) );
586
587            if ( uuid1 || uuid2 )
588            {
589                if ( uuid1 == 0 )
590                {
591                   base1 = UINT64_MAX;
592                }
593                else if ( uuid2 == 0 )
594                {
595                   base2 = UINT64_MAX;
596                }
597                else
598                {
599                    int compare;
600
601                    compare = strcmp( uuid1->getCStringNoCopy( ), uuid2->getCStringNoCopy( ) );
602
603                    if ( compare > 0 )
604                    {
605                        base1 = UINT64_MAX;
606                    }
607                    else if ( compare < 0 )
608                    {
609                        base2 = UINT64_MAX;
610                    }
611                    else
612                    {
613                        base1 = base2;
614                    }
615                }
616            }
617        }
618#endif /* TARGET_OS_EMBEDDED */
619
620        if ( base1 > base2 )
621        {
622            // A partition was added.
623
624            partition2->setProperty( kIOMediaLiveKey, true );
625
626            iterator = OSCollectionIterator::withCollection( partitions1 );
627            if ( iterator == 0 ) goto juxtaposeErr;
628
629            while ( ( partition = ( IOMedia * ) iterator->getNextObject( ) ) )
630            {
631                if ( strcmp( partition->getLocation( ), partition2->getLocation( ) ) == 0 )
632                {
633                    // Set a location value for this partition.
634
635                    char location[ 12 ];
636
637                    partitionID++;
638
639                    snprintf( location, sizeof( location ), "%d", ( int ) partitionID );
640
641                    partition2->setLocation( location );
642
643                    partition2->setProperty( kIOMediaLiveKey, false );
644
645                    break;
646                }
647            }
648
649            iterator->release( );
650            iterator = 0;
651
652            if ( partition2->attach( this ) )
653            {
654                attachMediaObjectToDeviceTree( partition2 );
655
656                partition2->registerService( kIOServiceAsynchronous );
657            }
658
659            partitions->setObject( partition2 );
660
661            partition2 = ( IOMedia * ) iterator2->getNextObject( );
662        }
663        else if ( base1 < base2 )
664        {
665            // A partition was removed.
666
667            partition1->setProperty( kIOMediaLiveKey, false );
668
669            if ( handleIsOpen( partition1 ) == false )
670            {
671                partition1->terminate( kIOServiceSynchronous );
672
673                detachMediaObjectFromDeviceTree( partition1 );
674            }
675            else
676            {
677                partition1->removeProperty( kIOMediaPartitionIDKey );
678
679                partitions->setObject( partition1 );
680            }
681
682            partition1 = ( IOMedia * ) iterator1->getNextObject( );
683        }
684        else
685        {
686            // A partition was matched.
687
688            bool edit;
689            bool move;
690
691            edit = false;
692            move = false;
693
694            keys = OSSet::withCapacity( 1 );
695            if ( keys == 0 ) goto juxtaposeErr;
696
697            properties = partition2->getPropertyTable( );
698
699            // Determine which properties were updated.
700
701            if ( partition1->getBase( )               != partition2->getBase( )               ||
702                 partition1->getSize( )               != partition2->getSize( )               ||
703                 partition1->getPreferredBlockSize( ) != partition2->getPreferredBlockSize( ) ||
704                 partition1->getAttributes( )         != partition2->getAttributes( )         ||
705                 partition1->isWhole( )               != partition2->isWhole( )               ||
706                 partition1->isWritable( )            != partition2->isWritable( )            ||
707                 strcmp( partition1->getContentHint( ), partition2->getContentHint( ) )       )
708            {
709                edit = true;
710            }
711
712            if ( strcmp( partition1->getName( ),     partition2->getName( )     ) ||
713                 strcmp( partition1->getLocation( ), partition2->getLocation( ) ) )
714            {
715                move = true;
716            }
717
718            iterator = OSCollectionIterator::withCollection( properties );
719            if ( iterator == 0 ) goto juxtaposeErr;
720
721            while ( ( key = ( OSSymbol * ) iterator->getNextObject( ) ) )
722            {
723                OSObject * value1;
724                OSObject * value2;
725
726                if ( key->isEqualTo( kIOMediaContentHintKey        ) ||
727                     key->isEqualTo( kIOMediaEjectableKey          ) ||
728                     key->isEqualTo( kIOMediaPreferredBlockSizeKey ) ||
729                     key->isEqualTo( kIOMediaRemovableKey          ) ||
730                     key->isEqualTo( kIOMediaSizeKey               ) ||
731                     key->isEqualTo( kIOMediaWholeKey              ) ||
732                     key->isEqualTo( kIOMediaWritableKey           ) )
733                {
734                    continue;
735                }
736
737                if ( key->isEqualTo( kIOMediaContentKey ) ||
738                     key->isEqualTo( kIOMediaLeafKey    ) ||
739                     key->isEqualTo( kIOMediaLiveKey    ) ||
740                     key->isEqualTo( kIOMediaOpenKey    ) )
741                {
742                    continue;
743                }
744
745                value1 = partition1->getProperty( key );
746                value2 = partition2->getProperty( key );
747
748                if ( value1 == 0 || value1->isEqualTo( value2 ) == false )
749                {
750                    keys->setObject( key );
751                }
752            }
753
754            iterator->release( );
755            iterator = 0;
756
757            // A partition was updated.
758
759            partition1->setProperty( kIOMediaLiveKey, ( move == false ) );
760
761            if ( edit )
762            {
763                partition1->init( partition2->getBase( ),
764                                  partition2->getSize( ),
765                                  partition2->getPreferredBlockSize( ),
766                                  partition2->getAttributes( ),
767                                  partition2->isWhole( ),
768                                  partition2->isWritable( ),
769                                  partition2->getContentHint( ) );
770            }
771
772            if ( keys->getCount( ) )
773            {
774                iterator = OSCollectionIterator::withCollection( keys );
775                if ( iterator == 0 ) goto juxtaposeErr;
776
777                while ( ( key = ( OSSymbol * ) iterator->getNextObject( ) ) )
778                {
779                    partition1->setProperty( key, partition2->getProperty( key ) );
780                }
781
782                iterator->release( );
783                iterator = 0;
784            }
785
786            if ( edit || keys->getCount( ) )
787            {
788                partition1->messageClients( kIOMessageServicePropertyChange );
789
790                partition1->registerService( kIOServiceAsynchronous );
791            }
792
793            keys->release( );
794            keys = 0;
795
796            partitions->setObject( partition1 );
797
798            partition1 = ( IOMedia * ) iterator1->getNextObject( );
799            partition2 = ( IOMedia * ) iterator2->getNextObject( );
800        }
801    }
802
803    // Release our resources.
804
805    iterator1->release( );
806    iterator2->release( );
807    partitions1->release( );
808    partitions2->release( );
809
810    return partitions;
811
812juxtaposeErr:
813
814    // Release our resources.
815
816    if ( iterator    ) iterator->release( );
817    if ( iterator1   ) iterator1->release( );
818    if ( iterator2   ) iterator2->release( );
819    if ( keys        ) keys->release( );
820    if ( partitions  ) partitions->release( );
821    if ( partitions1 ) partitions1->release( );
822    if ( partitions2 ) partitions2->release( );
823
824    return 0;
825}
826
827#ifdef __LP64__
828OSMetaClassDefineReservedUnused(IOPartitionScheme,  0);
829OSMetaClassDefineReservedUnused(IOPartitionScheme,  1);
830OSMetaClassDefineReservedUnused(IOPartitionScheme,  2);
831#else /* !__LP64__ */
832OSMetaClassDefineReservedUsed(IOPartitionScheme,  0);
833OSMetaClassDefineReservedUsed(IOPartitionScheme,  1);
834OSMetaClassDefineReservedUsed(IOPartitionScheme,  2);
835#endif /* !__LP64__ */
836OSMetaClassDefineReservedUnused(IOPartitionScheme,  3);
837OSMetaClassDefineReservedUnused(IOPartitionScheme,  4);
838OSMetaClassDefineReservedUnused(IOPartitionScheme,  5);
839OSMetaClassDefineReservedUnused(IOPartitionScheme,  6);
840OSMetaClassDefineReservedUnused(IOPartitionScheme,  7);
841OSMetaClassDefineReservedUnused(IOPartitionScheme,  8);
842OSMetaClassDefineReservedUnused(IOPartitionScheme,  9);
843OSMetaClassDefineReservedUnused(IOPartitionScheme, 10);
844OSMetaClassDefineReservedUnused(IOPartitionScheme, 11);
845OSMetaClassDefineReservedUnused(IOPartitionScheme, 12);
846OSMetaClassDefineReservedUnused(IOPartitionScheme, 13);
847OSMetaClassDefineReservedUnused(IOPartitionScheme, 14);
848OSMetaClassDefineReservedUnused(IOPartitionScheme, 15);
849OSMetaClassDefineReservedUnused(IOPartitionScheme, 16);
850OSMetaClassDefineReservedUnused(IOPartitionScheme, 17);
851OSMetaClassDefineReservedUnused(IOPartitionScheme, 18);
852OSMetaClassDefineReservedUnused(IOPartitionScheme, 19);
853OSMetaClassDefineReservedUnused(IOPartitionScheme, 20);
854OSMetaClassDefineReservedUnused(IOPartitionScheme, 21);
855OSMetaClassDefineReservedUnused(IOPartitionScheme, 22);
856OSMetaClassDefineReservedUnused(IOPartitionScheme, 23);
857OSMetaClassDefineReservedUnused(IOPartitionScheme, 24);
858OSMetaClassDefineReservedUnused(IOPartitionScheme, 25);
859OSMetaClassDefineReservedUnused(IOPartitionScheme, 26);
860OSMetaClassDefineReservedUnused(IOPartitionScheme, 27);
861OSMetaClassDefineReservedUnused(IOPartitionScheme, 28);
862OSMetaClassDefineReservedUnused(IOPartitionScheme, 29);
863OSMetaClassDefineReservedUnused(IOPartitionScheme, 30);
864OSMetaClassDefineReservedUnused(IOPartitionScheme, 31);
865
866#ifndef __LP64__
867extern "C" void _ZN17IOPartitionScheme4readEP9IOServiceyP18IOMemoryDescriptor19IOStorageCompletion( IOPartitionScheme * scheme, IOService * client, UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion )
868{
869    scheme->read( client, byteStart, buffer, NULL, &completion );
870}
871
872extern "C" void _ZN17IOPartitionScheme5writeEP9IOServiceyP18IOMemoryDescriptor19IOStorageCompletion( IOPartitionScheme * scheme, IOService * client, UInt64 byteStart, IOMemoryDescriptor * buffer, IOStorageCompletion completion )
873{
874    scheme->write( client, byteStart, buffer, NULL, &completion );
875}
876#endif /* !__LP64__ */
877