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