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/assert.h>
25#include <IOKit/IOBufferMemoryDescriptor.h>
26#include <IOKit/IOLib.h>
27#include <IOKit/storage/IOFDiskPartitionScheme.h>
28#include <IOKit/storage/IOGUIDPartitionScheme.h>
29#include <libkern/OSByteOrder.h>
30#include <sys/utfconv.h>
31
32#define super IOPartitionScheme
33OSDefineMetaClassAndStructors(IOGUIDPartitionScheme, IOPartitionScheme);
34
35#define UCS_LITTLE_ENDIAN 0x00000001
36
37static size_t ucs2_to_utf8( const uint16_t * ucs2str,
38                            size_t           ucs2strsiz,
39                            char *           utf8str,
40                            size_t           utf8strsiz,
41                            uint32_t         flags )
42{
43    size_t ucs2strlen;
44    size_t utf8strlen;
45
46    for ( ucs2strlen = 0; ucs2strlen < ucs2strsiz; ucs2strlen++ )
47    {
48        if ( ucs2str[ucs2strlen] == 0 )  break;
49    }
50
51    utf8_encodestr( ucs2str,
52                    ucs2strlen * sizeof(uint16_t),
53                    (uint8_t *) utf8str,
54                    &utf8strlen,
55                    utf8strsiz,
56                    '/',
57#ifdef __BIG_ENDIAN__
58                    (flags & UCS_LITTLE_ENDIAN) ? UTF_REVERSE_ENDIAN : 0 );
59#else /* !__BIG_ENDIAN__ */
60                    (flags & UCS_LITTLE_ENDIAN) ? 0 : UTF_REVERSE_ENDIAN );
61#endif /* !__BIG_ENDIAN__ */
62
63    return utf8strlen;
64}
65
66static void uuid_unswap(uuid_t uu)
67{
68    uint8_t tmp;
69
70    tmp = uu[0];  uu[0] = uu[3];  uu[3] = tmp;
71    tmp = uu[2];  uu[2] = uu[1];  uu[1] = tmp;
72    tmp = uu[4];  uu[4] = uu[5];  uu[5] = tmp;
73    tmp = uu[6];  uu[6] = uu[7];  uu[7] = tmp;
74}
75
76bool IOGUIDPartitionScheme::init(OSDictionary * properties)
77{
78    //
79    // Initialize this object's minimal state.
80    //
81
82    // Ask our superclass' opinion.
83
84    if ( super::init(properties) == false )  return false;
85
86    // Initialize our state.
87
88    _partitions = 0;
89
90    return true;
91}
92
93void IOGUIDPartitionScheme::free()
94{
95    //
96    // Free all of this object's outstanding resources.
97    //
98
99    if ( _partitions )  _partitions->release();
100
101    super::free();
102}
103
104IOService * IOGUIDPartitionScheme::probe(IOService * provider, SInt32 * score)
105{
106    //
107    // Determine whether the provider media contains a GUID partition map.
108    //
109
110    // State our assumptions.
111
112    assert(OSDynamicCast(IOMedia, provider));
113
114    // Ask our superclass' opinion.
115
116    if ( super::probe(provider, score) == 0 )  return 0;
117
118    // Scan the provider media for a GUID partition map.
119
120    _partitions = scan(score);
121
122    return ( _partitions ) ? this : 0;
123}
124
125bool IOGUIDPartitionScheme::start(IOService * provider)
126{
127    //
128    // Publish the new media objects which represent our partitions.
129    //
130
131    IOMedia *    partition;
132    OSIterator * partitionIterator;
133
134    // State our assumptions.
135
136    assert(_partitions);
137
138    // Ask our superclass' opinion.
139
140    if ( super::start(provider) == false )  return false;
141
142    // Attach and register the new media objects representing our partitions.
143
144    partitionIterator = OSCollectionIterator::withCollection(_partitions);
145    if ( partitionIterator == 0 )  return false;
146
147    while ( (partition = (IOMedia *) partitionIterator->getNextObject()) )
148    {
149        if ( partition->attach(this) )
150        {
151            attachMediaObjectToDeviceTree(partition);
152
153            partition->registerService();
154        }
155    }
156
157    partitionIterator->release();
158
159    return true;
160}
161
162void IOGUIDPartitionScheme::stop(IOService * provider)
163{
164    //
165    // Clean up after the media objects we published before terminating.
166    //
167
168    IOMedia *    partition;
169    OSIterator * partitionIterator;
170
171    // State our assumptions.
172
173    assert(_partitions);
174
175    // Detach the media objects we previously attached to the device tree.
176
177    partitionIterator = OSCollectionIterator::withCollection(_partitions);
178
179    if ( partitionIterator )
180    {
181        while ( (partition = (IOMedia *) partitionIterator->getNextObject()) )
182        {
183            detachMediaObjectFromDeviceTree(partition);
184        }
185
186        partitionIterator->release();
187    }
188
189    super::stop(provider);
190}
191
192IOReturn IOGUIDPartitionScheme::requestProbe(IOOptionBits options)
193{
194    //
195    // Request that the provider media be re-scanned for partitions.
196    //
197
198    OSSet * partitions    = 0;
199    OSSet * partitionsNew;
200    SInt32  score         = 0;
201
202    // Scan the provider media for partitions.
203
204    partitionsNew = scan( &score );
205
206    if ( partitionsNew )
207    {
208        if ( lockForArbitration( false ) )
209        {
210            partitions = juxtaposeMediaObjects( _partitions, partitionsNew );
211
212            if ( partitions )
213            {
214                _partitions->release( );
215
216                _partitions = partitions;
217            }
218
219            unlockForArbitration( );
220        }
221
222        partitionsNew->release( );
223    }
224
225    return partitions ? kIOReturnSuccess : kIOReturnError;
226}
227
228OSSet * IOGUIDPartitionScheme::scan(SInt32 * score)
229{
230    //
231    // Scan the provider media for a GUID partition map.    Returns the set
232    // of media objects representing each of the partitions (the retain for
233    // the set is passed to the caller), or null should no partition map be
234    // found.  The default probe score can be adjusted up or down, based on
235    // the confidence of the scan.
236    //
237
238    IOBufferMemoryDescriptor * buffer         = 0;
239    UInt32                     bufferSize     = 0;
240    UInt32                     fdiskID        = 0;
241    disk_blk0 *                fdiskMap       = 0;
242    UInt64                     gptBlock       = 0;
243    UInt32                     gptCheck       = 0;
244    UInt32                     gptCount       = 0;
245    UInt32                     gptID          = 0;
246    gpt_ent *                  gptMap         = 0;
247    UInt32                     gptSize        = 0;
248    UInt32                     headerCheck    = 0;
249    gpt_hdr *                  headerMap      = 0;
250    UInt32                     headerSize     = 0;
251    IOMedia *                  media          = getProvider();
252    UInt64                     mediaBlockSize = media->getPreferredBlockSize();
253    bool                       mediaIsOpen    = false;
254    OSSet *                    partitions     = 0;
255    IOReturn                   status         = kIOReturnError;
256
257    // Determine whether this media is formatted.
258
259    if ( media->isFormatted() == false )  goto scanErr;
260
261    // Determine whether this media has an appropriate block size.
262
263    if ( (mediaBlockSize % sizeof(disk_blk0)) )  goto scanErr;
264
265    // Allocate a buffer large enough to hold one map, rounded to a media block.
266
267    bufferSize = IORound(sizeof(disk_blk0), mediaBlockSize);
268    buffer     = IOBufferMemoryDescriptor::withCapacity(
269                                           /* capacity      */ bufferSize,
270                                           /* withDirection */ kIODirectionIn );
271    if ( buffer == 0 )  goto scanErr;
272
273    // Allocate a set to hold the set of media objects representing partitions.
274
275    partitions = OSSet::withCapacity(8);
276    if ( partitions == 0 )  goto scanErr;
277
278    // Open the media with read access.
279
280    mediaIsOpen = open(this, 0, kIOStorageAccessReader);
281    if ( mediaIsOpen == false )  goto scanErr;
282
283    // Read the protective map into our buffer.
284
285    status = media->read(this, 0, buffer);
286    if ( status != kIOReturnSuccess )  goto scanErr;
287
288    fdiskMap = (disk_blk0 *) buffer->getBytesNoCopy();
289
290    // Determine whether the protective map signature is present.
291
292    if ( OSSwapLittleToHostInt16(fdiskMap->signature) != DISK_SIGNATURE )
293    {
294         goto scanErr;
295    }
296
297    // Scan for valid partition entries in the protective map.
298
299    for ( unsigned index = 0; index < DISK_NPART; index++ )
300    {
301        if ( fdiskMap->parts[index].systid )
302        {
303            if ( fdiskMap->parts[index].systid == 0xEE )
304            {
305                if ( fdiskID )  goto scanErr;
306
307                fdiskID = index + 1;
308            }
309        }
310    }
311
312    if ( fdiskID == 0 )  goto scanErr;
313
314    // Read the partition header into our buffer.
315
316    status = media->read(this, mediaBlockSize, buffer);
317    if ( status != kIOReturnSuccess )  goto scanErr;
318
319    headerMap = (gpt_hdr *) buffer->getBytesNoCopy();
320
321    // Determine whether the partition header signature is present.
322
323    if ( memcmp(headerMap->hdr_sig, GPT_HDR_SIG, strlen(GPT_HDR_SIG)) )
324    {
325        goto scanErr;
326    }
327
328    // Determine whether the partition header size is valid.
329
330    headerCheck = OSSwapLittleToHostInt32(headerMap->hdr_crc_self);
331    headerSize  = OSSwapLittleToHostInt32(headerMap->hdr_size);
332
333    if ( headerSize < offsetof(gpt_hdr, padding) )
334    {
335        goto scanErr;
336    }
337
338    if ( headerSize > mediaBlockSize )
339    {
340        goto scanErr;
341    }
342
343    // Determine whether the partition header checksum is valid.
344
345    headerMap->hdr_crc_self = 0;
346
347    if ( crc32(0, headerMap, headerSize) != headerCheck )
348    {
349        goto scanErr;
350    }
351
352    // Determine whether the partition entry size is valid.
353
354    gptCheck = OSSwapLittleToHostInt32(headerMap->hdr_crc_table);
355    gptSize  = OSSwapLittleToHostInt32(headerMap->hdr_entsz);
356
357    if ( gptSize < sizeof(gpt_ent) )
358    {
359        goto scanErr;
360    }
361
362    if ( gptSize > UINT16_MAX )
363    {
364        goto scanErr;
365    }
366
367    // Determine whether the partition entry count is valid.
368
369    gptBlock = OSSwapLittleToHostInt64(headerMap->hdr_lba_table);
370    gptCount = OSSwapLittleToHostInt32(headerMap->hdr_entries);
371
372    if ( gptCount > UINT16_MAX )
373    {
374        goto scanErr;
375    }
376
377    // Allocate a buffer large enough to hold one map, rounded to a media block.
378
379    buffer->release();
380
381    bufferSize = IORound(gptCount * gptSize, mediaBlockSize);
382    buffer     = IOBufferMemoryDescriptor::withCapacity(
383                                           /* capacity      */ bufferSize,
384                                           /* withDirection */ kIODirectionIn );
385    if ( buffer == 0 )  goto scanErr;
386
387    // Read the partition header into our buffer.
388
389    status = media->read(this, gptBlock * mediaBlockSize, buffer);
390    if ( status != kIOReturnSuccess )  goto scanErr;
391
392    gptMap = (gpt_ent *) buffer->getBytesNoCopy();
393
394    // Determine whether the partition entry checksum is valid.
395
396    if ( crc32(0, gptMap, gptCount * gptSize) != gptCheck )
397    {
398        goto scanErr;
399    }
400
401    // Scan for valid partition entries in the partition map.
402
403    for ( gptID = 1; gptID <= gptCount; gptID++ )
404    {
405        gptMap = (gpt_ent *) ( ((UInt8 *) buffer->getBytesNoCopy()) +
406                               (gptID * gptSize) - gptSize );
407
408        uuid_unswap( gptMap->ent_type );
409        uuid_unswap( gptMap->ent_uuid );
410
411        if ( isPartitionUsed( gptMap ) )
412        {
413            // Determine whether the partition is corrupt (fatal).
414
415            if ( isPartitionCorrupt( gptMap, gptID ) )
416            {
417                goto scanErr;
418            }
419
420            // Determine whether the partition is invalid (skipped).
421
422            if ( isPartitionInvalid( gptMap, gptID ) )
423            {
424                continue;
425            }
426
427            // Create a media object to represent this partition.
428
429            IOMedia * newMedia = instantiateMediaObject( gptMap, gptID );
430
431            if ( newMedia )
432            {
433                partitions->setObject(newMedia);
434                newMedia->release();
435            }
436        }
437    }
438
439    // Release our resources.
440
441    close(this);
442    buffer->release();
443
444    return partitions;
445
446scanErr:
447
448    // Release our resources.
449
450    if ( mediaIsOpen )  close(this);
451    if ( partitions )  partitions->release();
452    if ( buffer )  buffer->release();
453
454    return 0;
455}
456
457bool IOGUIDPartitionScheme::isPartitionUsed(gpt_ent * partition)
458{
459    //
460    // Ask whether the given partition is used.
461    //
462
463    return uuid_is_null(partition->ent_type) ? false : true;
464}
465
466bool IOGUIDPartitionScheme::isPartitionCorrupt( gpt_ent * /* partition   */ ,
467                                                UInt32    /* partitionID */ )
468{
469    //
470    // Ask whether the given partition appears to be corrupt. A partition that
471    // is corrupt will cause the failure of the GUID partition map recognition
472    // altogether.
473    //
474
475    return false;
476}
477
478bool IOGUIDPartitionScheme::isPartitionInvalid( gpt_ent * partition,
479                                                UInt32    partitionID )
480{
481    //
482    // Ask whether the given partition appears to be invalid.  A partition that
483    // is invalid will cause it to be skipped in the scan, but will not cause a
484    // failure of the GUID partition map recognition.
485    //
486
487    IOMedia * media          = getProvider();
488    UInt64    mediaBlockSize = media->getPreferredBlockSize();
489    UInt64    partitionBase  = 0;
490    UInt64    partitionSize  = 0;
491
492    // Compute the relative byte position and size of the new partition.
493
494    partitionBase  = OSSwapLittleToHostInt64(partition->ent_lba_start);
495    partitionSize  = OSSwapLittleToHostInt64(partition->ent_lba_end);
496    partitionBase *= mediaBlockSize;
497    partitionSize *= mediaBlockSize;
498
499    // Determine whether the partition is a placeholder.
500
501    if ( partitionBase == partitionSize )  return true;
502
503    // Compute the relative byte position and size of the new partition.
504
505    partitionSize -= partitionBase - mediaBlockSize;
506
507    // Determine whether the new partition leaves the confines of the container.
508
509    if ( partitionBase + partitionSize > media->getSize() )  return true;
510
511    return false;
512}
513
514IOMedia * IOGUIDPartitionScheme::instantiateMediaObject( gpt_ent * partition,
515                                                         UInt32    partitionID )
516{
517    //
518    // Instantiate a new media object to represent the given partition.
519    //
520
521    IOMedia *     media          = getProvider();
522    UInt64        mediaBlockSize = media->getPreferredBlockSize();
523    UInt64        partitionBase  = 0;
524    uuid_string_t partitionHint;
525    char          partitionName[36 * 3 + 1];
526    UInt64        partitionSize  = 0;
527
528    ucs2_to_utf8( partition->ent_name,
529                  sizeof(partition->ent_name),
530                  partitionName,
531                  sizeof(partitionName),
532                  UCS_LITTLE_ENDIAN );
533
534    uuid_unparse( partition->ent_type,
535                  partitionHint );
536
537    // Compute the relative byte position and size of the new partition.
538
539    partitionBase  = OSSwapLittleToHostInt64(partition->ent_lba_start);
540    partitionSize  = OSSwapLittleToHostInt64(partition->ent_lba_end);
541    partitionBase *= mediaBlockSize;
542    partitionSize *= mediaBlockSize;
543    partitionSize -= partitionBase - mediaBlockSize;
544
545    // Create the new media object.
546
547    IOMedia * newMedia = instantiateDesiredMediaObject(
548                                   /* partition   */ partition,
549                                   /* partitionID */ partitionID );
550
551    if ( newMedia )
552    {
553         if ( newMedia->init(
554                /* base               */ partitionBase,
555                /* size               */ partitionSize,
556                /* preferredBlockSize */ mediaBlockSize,
557                /* attributes         */ media->getAttributes(),
558                /* isWhole            */ false,
559                /* isWritable         */ media->isWritable(),
560                /* contentHint        */ partitionHint ) )
561        {
562            // Set a name for this partition.
563
564            char name[24];
565            snprintf(name, sizeof(name), "Untitled %d", (int) partitionID);
566            newMedia->setName(partitionName[0] ? partitionName : name);
567
568            // Set a location value (the partition number) for this partition.
569
570            char location[12];
571            snprintf(location, sizeof(location), "%d", (int) partitionID);
572            newMedia->setLocation(location);
573
574            // Set the "Base" key for this partition.
575
576            newMedia->setProperty(kIOMediaBaseKey, partitionBase, 64);
577
578            // Set the "Partition ID" key for this partition.
579
580            newMedia->setProperty(kIOMediaPartitionIDKey, partitionID, 32);
581
582            // Set the "Universal Unique ID" key for this partition.
583
584            uuid_string_t uuid;
585            uuid_unparse(partition->ent_uuid, uuid);
586            newMedia->setProperty(kIOMediaUUIDKey, uuid);
587        }
588        else
589        {
590            newMedia->release();
591            newMedia = 0;
592        }
593    }
594
595    return newMedia;
596}
597
598IOMedia * IOGUIDPartitionScheme::instantiateDesiredMediaObject(
599                                                         gpt_ent * partition,
600                                                         UInt32    partitionID )
601{
602    //
603    // Allocate a new media object (called from instantiateMediaObject).
604    //
605
606    return new IOMedia;
607}
608
609#ifndef __LP64__
610bool IOGUIDPartitionScheme::attachMediaObjectToDeviceTree(IOMedia * media)
611{
612    //
613    // Attach the given media object to the device tree plane.
614    //
615
616    return super::attachMediaObjectToDeviceTree(media);
617}
618
619void IOGUIDPartitionScheme::detachMediaObjectFromDeviceTree(IOMedia * media)
620{
621    //
622    // Detach the given media object from the device tree plane.
623    //
624
625    super::detachMediaObjectFromDeviceTree(media);
626}
627#endif /* !__LP64__ */
628
629OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  0);
630OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  1);
631OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  2);
632OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  3);
633OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  4);
634OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  5);
635OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  6);
636OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  7);
637OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  8);
638OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme,  9);
639OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme, 10);
640OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme, 11);
641OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme, 12);
642OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme, 13);
643OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme, 14);
644OSMetaClassDefineReservedUnused(IOGUIDPartitionScheme, 15);
645