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