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/IOApplePartitionScheme.h> 28#include <libkern/OSByteOrder.h> 29 30#define super IOPartitionScheme 31OSDefineMetaClassAndStructors(IOApplePartitionScheme, IOPartitionScheme); 32 33// 34// Notes 35// 36// o the on-disk structure's fields are big-endian formatted 37// o the dpme_pblock_start and dpme_pblocks block values are: 38// o for media without a driver map: 39// o natural block size based 40// o for media with a driver map: 41// o driver map block size based, unless the driver map block size is 2048 42// and a valid partition entry exists at a 512 byte offset into the disk, 43// in which case, assume a 512 byte block size, except for the partition 44// entries that lie on a 2048 byte multiple and are one of the following 45// types: Apple_Patches, Apple_Driver, Apple_Driver43, Apple_Driver43_CD, 46// Apple_Driver_ATA, Apple_Driver_ATAPI; in which case, we assume a 2048 47// byte block size (for the one partition) 48// o the dpme_pblock_start block value is relative to the media container 49// 50 51bool IOApplePartitionScheme::init(OSDictionary * properties) 52{ 53 // 54 // Initialize this object's minimal state. 55 // 56 57 // State our assumptions. 58 59 assert(sizeof(dpme) == 512); // (compiler/platform check) 60 assert(sizeof(DDMap) == 8); // (compiler/platform check) 61 assert(sizeof(Block0) == 512); // (compiler/platform check) 62 63 // Ask our superclass' opinion. 64 65 if (super::init(properties) == false) return false; 66 67 // Initialize our state. 68 69 _partitions = 0; 70 71 return true; 72} 73 74void IOApplePartitionScheme::free() 75{ 76 // 77 // Free all of this object's outstanding resources. 78 // 79 80 if ( _partitions ) _partitions->release(); 81 82 super::free(); 83} 84 85IOService * IOApplePartitionScheme::probe(IOService * provider, SInt32 * score) 86{ 87 // 88 // Determine whether the provider media contains an Apple partition map. 89 // 90 91 // State our assumptions. 92 93 assert(OSDynamicCast(IOMedia, provider)); 94 95 // Ask superclass' opinion. 96 97 if (super::probe(provider, score) == 0) return 0; 98 99 // Scan the provider media for an Apple partition map. 100 101 _partitions = scan(score); 102 103 return ( _partitions ) ? this : 0; 104} 105 106bool IOApplePartitionScheme::start(IOService * provider) 107{ 108 // 109 // Publish the new media objects which represent our partitions. 110 // 111 112 IOMedia * partition; 113 OSIterator * partitionIterator; 114 115 // State our assumptions. 116 117 assert(_partitions); 118 119 // Ask our superclass' opinion. 120 121 if ( super::start(provider) == false ) return false; 122 123 // Attach and register the new media objects representing our partitions. 124 125 partitionIterator = OSCollectionIterator::withCollection(_partitions); 126 if ( partitionIterator == 0 ) return false; 127 128 while ( (partition = (IOMedia *) partitionIterator->getNextObject()) ) 129 { 130 if ( partition->attach(this) ) 131 { 132 attachMediaObjectToDeviceTree(partition); 133 134 partition->registerService(); 135 } 136 } 137 138 partitionIterator->release(); 139 140 return true; 141} 142 143void IOApplePartitionScheme::stop(IOService * provider) 144{ 145 // 146 // Clean up after the media objects we published before terminating. 147 // 148 149 IOMedia * partition; 150 OSIterator * partitionIterator; 151 152 // State our assumptions. 153 154 assert(_partitions); 155 156 // Detach the media objects we previously attached to the device tree. 157 158 partitionIterator = OSCollectionIterator::withCollection(_partitions); 159 160 if ( partitionIterator ) 161 { 162 while ( (partition = (IOMedia *) partitionIterator->getNextObject()) ) 163 { 164 detachMediaObjectFromDeviceTree(partition); 165 } 166 167 partitionIterator->release(); 168 } 169 170 super::stop(provider); 171} 172 173IOReturn IOApplePartitionScheme::requestProbe(IOOptionBits options) 174{ 175 // 176 // Request that the provider media be re-scanned for partitions. 177 // 178 179 OSSet * partitions = 0; 180 OSSet * partitionsNew; 181 SInt32 score = 0; 182 183 // Scan the provider media for partitions. 184 185 partitionsNew = scan( &score ); 186 187 if ( partitionsNew ) 188 { 189 if ( lockForArbitration( false ) ) 190 { 191 partitions = juxtaposeMediaObjects( _partitions, partitionsNew ); 192 193 if ( partitions ) 194 { 195 _partitions->release( ); 196 197 _partitions = partitions; 198 } 199 200 unlockForArbitration( ); 201 } 202 203 partitionsNew->release( ); 204 } 205 206 return partitions ? kIOReturnSuccess : kIOReturnError; 207} 208 209OSSet * IOApplePartitionScheme::scan(SInt32 * score) 210{ 211 // 212 // Scan the provider media for an Apple partition map. Returns the set 213 // of media objects representing each of the partitions (the retain for 214 // the set is passed to the caller), or null should no partition map be 215 // found. The default probe score can be adjusted up or down, based on 216 // the confidence of the scan. 217 // 218 219 IOBufferMemoryDescriptor * buffer = 0; 220 UInt32 bufferReadAt = 0; 221 UInt32 bufferSize = 0; 222 UInt32 dpmeBlockSize = 0; 223 UInt32 dpmeCount = 0; 224 UInt32 dpmeID = 0; 225 dpme * dpmeMap = 0; 226 UInt32 dpmeMaxCount = 0; 227 bool dpmeOldSchool = false; 228 Block0 * driverMap = 0; 229 IOMedia * media = getProvider(); 230 UInt64 mediaBlockSize = media->getPreferredBlockSize(); 231 bool mediaIsOpen = false; 232 OSSet * partitions = 0; 233 IOReturn status = kIOReturnError; 234 235 // Determine whether this media is formatted. 236 237 if ( media->isFormatted() == false ) goto scanErr; 238 239 // Determine whether this media has an appropriate block size. 240 241 if ( (mediaBlockSize % sizeof(dpme)) ) goto scanErr; 242 243 // Allocate a buffer large enough to hold one map, rounded to a media block. 244 245 bufferSize = IORound(max(sizeof(Block0), sizeof(dpme)), mediaBlockSize); 246 buffer = IOBufferMemoryDescriptor::withCapacity( 247 /* capacity */ bufferSize, 248 /* withDirection */ kIODirectionIn ); 249 if ( buffer == 0 ) goto scanErr; 250 251 // Allocate a set to hold the set of media objects representing partitions. 252 253 partitions = OSSet::withCapacity(8); 254 if ( partitions == 0 ) goto scanErr; 255 256 // Open the media with read access. 257 258 mediaIsOpen = open(this, 0, kIOStorageAccessReader); 259 if ( mediaIsOpen == false ) goto scanErr; 260 261 // Read the driver map into our buffer. 262 263 bufferReadAt = 0; 264 265 status = media->read(this, bufferReadAt, buffer); 266 if ( status != kIOReturnSuccess ) goto scanErr; 267 268 driverMap = (Block0 *) buffer->getBytesNoCopy(); 269 270 // Determine the official block size to use to scan the partition entries. 271 272 dpmeBlockSize = mediaBlockSize; // (natural block size) 273 274 if ( OSSwapBigToHostInt16(driverMap->sbSig) == BLOCK0_SIGNATURE ) 275 { 276 dpmeBlockSize = OSSwapBigToHostInt16(driverMap->sbBlkSize); 277 278 // Increase the probe score when a driver map is detected, since we are 279 // more confident in the match when it is present. This will eliminate 280 // conflicts with FDisk when it shares the same block as the driver map. 281 282 *score += 2000; 283 } 284 285 // Determine whether we have an old school partition map, where there is 286 // a partition entry at a 512 byte offset into the disk, even though the 287 // driver map block size is 2048. 288 289 if ( dpmeBlockSize == 2048 ) 290 { 291 if ( bufferSize >= sizeof(Block0) + sizeof(dpme) ) // (in buffer?) 292 { 293 dpmeMap = (dpme *) (driverMap + 1); 294 } 295 else // (not in buffer) 296 { 297 // Read the partition entry at byte offset 512 into our buffer. 298 299 bufferReadAt = sizeof(dpme); 300 301 status = media->read(this, bufferReadAt, buffer); 302 if ( status != kIOReturnSuccess ) goto scanErr; 303 304 dpmeMap = (dpme *) buffer->getBytesNoCopy(); 305 } 306 307 // Determine whether the partition entry signature is present. 308 309 if ( OSSwapBigToHostInt16(dpmeMap->dpme_signature) == DPME_SIGNATURE ) 310 { 311 dpmeBlockSize = sizeof(dpme); // (old school block size) 312 dpmeOldSchool = true; 313 } 314 } 315 316 // Scan the media for Apple partition entries. 317 318 for ( dpmeID = 1, dpmeCount = 1; dpmeID <= dpmeCount; dpmeID++ ) 319 { 320 UInt32 partitionBlockSize = dpmeBlockSize; 321 322 // Determine whether we've exhausted the current buffer of entries. 323 324 if ( dpmeID * dpmeBlockSize + sizeof(dpme) > bufferReadAt + bufferSize ) 325 { 326 // Read the next partition entry into our buffer. 327 328 bufferReadAt = dpmeID * dpmeBlockSize; 329 330 status = media->read(this, bufferReadAt, buffer); 331 if ( status != kIOReturnSuccess ) goto scanErr; 332 } 333 334 dpmeMap = (dpme *) ( ((UInt8 *) buffer->getBytesNoCopy()) + 335 (dpmeID * dpmeBlockSize) - bufferReadAt ); 336 337 // Determine whether the partition entry signature is present. 338 339 if ( OSSwapBigToHostInt16(dpmeMap->dpme_signature) != DPME_SIGNATURE ) 340 { 341 goto scanErr; 342 } 343 344 // Obtain an accurate number of entries in the partition map. 345 346 if ( !strncmp(dpmeMap->dpme_type, "Apple_partition_map", sizeof(dpmeMap->dpme_type)) || 347 !strncmp(dpmeMap->dpme_type, "Apple_Partition_Map", sizeof(dpmeMap->dpme_type)) || 348 !strncmp(dpmeMap->dpme_type, "Apple_patition_map", sizeof(dpmeMap->dpme_type)) ) 349 { 350 dpmeCount = OSSwapBigToHostInt32(dpmeMap->dpme_map_entries); 351 dpmeMaxCount = OSSwapBigToHostInt32(dpmeMap->dpme_pblocks); 352 } 353 else if ( dpmeCount == 1 ) 354 { 355 dpmeCount = OSSwapBigToHostInt32(dpmeMap->dpme_map_entries); 356 } 357 358 // Obtain an accurate block size for an old school partition map. 359 360 if ( dpmeOldSchool && (dpmeID % 4) == 0 ) 361 { 362 if ( !strncmp(dpmeMap->dpme_type, "Apple_Driver", sizeof(dpmeMap->dpme_type)) || 363 !strncmp(dpmeMap->dpme_type, "Apple_Driver43", sizeof(dpmeMap->dpme_type)) || 364 !strncmp(dpmeMap->dpme_type, "Apple_Driver43_CD", sizeof(dpmeMap->dpme_type)) || 365 !strncmp(dpmeMap->dpme_type, "Apple_Driver_ATA", sizeof(dpmeMap->dpme_type)) || 366 !strncmp(dpmeMap->dpme_type, "Apple_Driver_ATAPI", sizeof(dpmeMap->dpme_type)) || 367 !strncmp(dpmeMap->dpme_type, "Apple_Patches", sizeof(dpmeMap->dpme_type)) ) 368 { 369 partitionBlockSize = 2048; 370 } 371 } 372 373 // Determine whether the partition is corrupt (fatal). 374 375 if ( isPartitionCorrupt( 376 /* partition */ dpmeMap, 377 /* partitionID */ dpmeID, 378 /* partitionBlockSize */ partitionBlockSize ) ) 379 { 380 goto scanErr; 381 } 382 383 // Determine whether the partition is invalid (skipped). 384 385 if ( isPartitionInvalid( 386 /* partition */ dpmeMap, 387 /* partitionID */ dpmeID, 388 /* partitionBlockSize */ partitionBlockSize ) ) 389 { 390 continue; 391 } 392 393 // Create a media object to represent this partition. 394 395 IOMedia * newMedia = instantiateMediaObject( 396 /* partition */ dpmeMap, 397 /* partitionID */ dpmeID, 398 /* partitionBlockSize */ partitionBlockSize ); 399 400 if ( newMedia ) 401 { 402 partitions->setObject(newMedia); 403 newMedia->release(); 404 } 405 } 406 407 // Determine whether we ever came accross an Apple_partition_map partition. 408 409 if ( dpmeMaxCount == 0 ) goto scanErr; 410 411 // Release our resources. 412 413 close(this); 414 buffer->release(); 415 416 return partitions; 417 418scanErr: 419 420 // Release our resources. 421 422 if ( mediaIsOpen ) close(this); 423 if ( partitions ) partitions->release(); 424 if ( buffer ) buffer->release(); 425 426 return 0; 427} 428 429bool IOApplePartitionScheme::isPartitionCorrupt( dpme * partition, 430 UInt32 partitionID, 431 UInt32 partitionBlockSize ) 432{ 433 // 434 // Ask whether the given partition appears to be corrupt. A partition that 435 // is corrupt will cause the failure of the Apple partition map recognition 436 // altogether. 437 // 438 439 if ( !strncmp(partition->dpme_type, "CD_ROM_Mode_1", sizeof(partition->dpme_type)) ) return true; 440 441 return false; 442} 443 444bool IOApplePartitionScheme::isPartitionInvalid( dpme * partition, 445 UInt32 partitionID, 446 UInt32 partitionBlockSize ) 447{ 448 // 449 // Ask whether the given partition appears to be invalid. A partition that 450 // is invalid will cause it to be skipped in the scan, but will not cause a 451 // failure of the Apple partition map recognition. 452 // 453 454 IOMedia * media = getProvider(); 455 UInt64 partitionBase = 0; 456 UInt64 partitionSize = 0; 457 458 // Compute the relative byte position and size of the new partition. 459 460 partitionBase = OSSwapBigToHostInt32(partition->dpme_pblock_start); 461 partitionSize = OSSwapBigToHostInt32(partition->dpme_pblocks); 462 partitionBase *= partitionBlockSize; 463 partitionSize *= partitionBlockSize; 464 465 // Determine whether the partition is a placeholder. 466 467 if ( partitionSize == 0 ) return true; 468 469 // Determine whether the partition starts at (or past) the end-of-media. 470 471 if ( partitionBase >= media->getSize() ) return true; 472 473 return false; 474} 475 476IOMedia * IOApplePartitionScheme::instantiateMediaObject( 477 dpme * partition, 478 UInt32 partitionID, 479 UInt32 partitionBlockSize ) 480{ 481 // 482 // Instantiate a new media object to represent the given partition. 483 // 484 485 IOMedia * media = getProvider(); 486 UInt64 mediaBlockSize = media->getPreferredBlockSize(); 487 UInt64 partitionBase = 0; 488 char partitionHint[DPISTRLEN + 1]; 489 bool partitionIsWritable = media->isWritable(); 490 char partitionName[DPISTRLEN + 1]; 491 UInt64 partitionSize = 0; 492 493 strncpy(partitionHint, partition->dpme_type, DPISTRLEN); 494 strncpy(partitionName, partition->dpme_name, DPISTRLEN); 495 496 partitionHint[DPISTRLEN] = 0; 497 partitionName[DPISTRLEN] = 0; 498 499 // Compute the relative byte position and size of the new partition. 500 501 partitionBase = OSSwapBigToHostInt32(partition->dpme_pblock_start); 502 partitionSize = OSSwapBigToHostInt32(partition->dpme_pblocks); 503 partitionBase *= partitionBlockSize; 504 partitionSize *= partitionBlockSize; 505 506 // Clip the size of the new partition if it extends past the end-of-media. 507 508 if ( partitionBase + partitionSize > media->getSize() ) 509 { 510 partitionSize = media->getSize() - partitionBase; 511 } 512 513 // Determine whether the new partition type is Apple_Free, which we choose 514 // not to publish because it is an internal concept to the partition map. 515 516 if ( !strncmp(partition->dpme_type, "Apple_Free", sizeof(partition->dpme_type)) ) return 0; 517 518 // Determine whether the new partition is read-only. 519 // 520 // Note that we treat the misspelt Apple_patition_map entries as equivalent 521 // to Apple_partition_map entries due to the messed up CDs noted in 2513960. 522 523 if ( !strncmp(partition->dpme_type, "Apple_partition_map", sizeof(partition->dpme_type)) || 524 !strncmp(partition->dpme_type, "Apple_Partition_Map", sizeof(partition->dpme_type)) || 525 !strncmp(partition->dpme_type, "Apple_patition_map", sizeof(partition->dpme_type)) || 526 ( OSSwapBigToHostInt32(partition->dpme_flags) & 527 ( DPME_FLAGS_WRITABLE | DPME_FLAGS_VALID ) ) == DPME_FLAGS_VALID ) 528 { 529 partitionIsWritable = false; 530 } 531 532 // Create the new media object. 533 534 IOMedia * newMedia = instantiateDesiredMediaObject( 535 /* partition */ partition, 536 /* partitionID */ partitionID, 537 /* partitionBlockSize */ partitionBlockSize ); 538 539 if ( newMedia ) 540 { 541 if ( newMedia->init( 542 /* base */ partitionBase, 543 /* size */ partitionSize, 544 /* preferredBlockSize */ mediaBlockSize, 545 /* attributes */ media->getAttributes(), 546 /* isWhole */ false, 547 /* isWritable */ partitionIsWritable, 548 /* contentHint */ partitionHint ) ) 549 { 550 // Set a name for this partition. 551 552 char name[24]; 553 snprintf(name, sizeof(name), "Untitled %d", (int) partitionID); 554 newMedia->setName(partitionName[0] ? partitionName : name); 555 556 // Set a location value (the partition number) for this partition. 557 558 char location[12]; 559 snprintf(location, sizeof(location), "%d", (int) partitionID); 560 newMedia->setLocation(location); 561 562 // Set the "Base" key for this partition. 563 564 newMedia->setProperty(kIOMediaBaseKey, partitionBase, 64); 565 566 // Set the "Partition ID" key for this partition. 567 568 newMedia->setProperty(kIOMediaPartitionIDKey, partitionID, 32); 569 } 570 else 571 { 572 newMedia->release(); 573 newMedia = 0; 574 } 575 } 576 577 return newMedia; 578} 579 580IOMedia * IOApplePartitionScheme::instantiateDesiredMediaObject( 581 dpme * partition, 582 UInt32 partitionID, 583 UInt32 partitionBlockSize ) 584{ 585 // 586 // Allocate a new media object (called from instantiateMediaObject). 587 // 588 589 return new IOMedia; 590} 591 592#ifndef __LP64__ 593bool IOApplePartitionScheme::attachMediaObjectToDeviceTree(IOMedia * media) 594{ 595 // 596 // Attach the given media object to the device tree plane. 597 // 598 599 return super::attachMediaObjectToDeviceTree(media); 600} 601 602void IOApplePartitionScheme::detachMediaObjectFromDeviceTree(IOMedia * media) 603{ 604 // 605 // Detach the given media object from the device tree plane. 606 // 607 608 super::detachMediaObjectFromDeviceTree(media); 609} 610#endif /* !__LP64__ */ 611 612OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 0); 613OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 1); 614OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 2); 615OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 3); 616OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 4); 617OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 5); 618OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 6); 619OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 7); 620OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 8); 621OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 9); 622OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 10); 623OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 11); 624OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 12); 625OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 13); 626OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 14); 627OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 15); 628