IOFDiskPartitionScheme.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOLib.h>
#include <IOKit/storage/IOFDiskPartitionScheme.h>
#include <libkern/OSByteOrder.h>
#define super IOPartitionScheme
OSDefineMetaClassAndStructors(IOFDiskPartitionScheme, IOPartitionScheme);
#define kIOFDiskPartitionSchemeContentTable "Content Table"
bool IOFDiskPartitionScheme::init(OSDictionary * properties)
{
assert(sizeof(fdisk_part) == 16); assert(sizeof(disk_blk0) == 512);
if ( super::init(properties) == false ) return false;
_partitions = 0;
return true;
}
void IOFDiskPartitionScheme::free()
{
if ( _partitions ) _partitions->release();
super::free();
}
IOService * IOFDiskPartitionScheme::probe(IOService * provider, SInt32 * score)
{
assert(OSDynamicCast(IOMedia, provider));
if ( super::probe(provider, score) == 0 ) return 0;
_partitions = scan(score);
if ( _partitions && _partitions->getCount() == 0 )
{
_partitions->release();
_partitions = 0;
}
return ( _partitions ) ? this : 0;
}
bool IOFDiskPartitionScheme::start(IOService * provider)
{
IOMedia * partition;
OSIterator * partitionIterator;
assert(_partitions);
if ( super::start(provider) == false ) return false;
partitionIterator = OSCollectionIterator::withCollection(_partitions);
if ( partitionIterator == 0 ) return false;
while ( (partition = (IOMedia *) partitionIterator->getNextObject()) )
{
if ( partition->attach(this) )
{
attachMediaObjectToDeviceTree(partition);
partition->registerService();
}
}
partitionIterator->release();
_partitionSchemeState |= kIOPartitionScheme_partition_valid;
return true;
}
void IOFDiskPartitionScheme::stop(IOService * provider)
{
IOMedia * partition;
OSIterator * partitionIterator;
assert(_partitions);
partitionIterator = OSCollectionIterator::withCollection(_partitions);
if ( partitionIterator )
{
while ( (partition = (IOMedia *) partitionIterator->getNextObject()) )
{
detachMediaObjectFromDeviceTree(partition);
}
partitionIterator->release();
}
super::stop(provider);
}
IOReturn IOFDiskPartitionScheme::requestProbe(IOOptionBits options)
{
OSSet * partitions = 0;
OSSet * partitionsNew;
SInt32 score = 0;
if ( ( _partitionSchemeState & kIOPartitionScheme_partition_valid ) == 0 )
{
return kIOReturnError;
}
partitionsNew = scan( &score );
if ( partitionsNew )
{
if ( lockForArbitration( false ) )
{
partitions = juxtaposeMediaObjects( _partitions, partitionsNew );
if ( partitions )
{
_partitions->release( );
_partitions = partitions;
}
unlockForArbitration( );
}
partitionsNew->release( );
}
return partitions ? kIOReturnSuccess : kIOReturnError;
}
OSSet * IOFDiskPartitionScheme::scan(SInt32 * score)
{
IOBufferMemoryDescriptor * buffer = 0;
IOByteCount bufferSize = 0;
UInt32 fdiskBlock = 0;
UInt32 fdiskBlockExtn = 0;
UInt32 fdiskBlockNext = 0;
UInt32 fdiskID = 0;
disk_blk0 * fdiskMap = 0;
IOMedia * media = getProvider();
UInt64 mediaBlockSize = media->getPreferredBlockSize();
bool mediaIsOpen = false;
OSSet * partitions = 0;
IOReturn status = kIOReturnError;
if ( media->isFormatted() == false ) goto scanErr;
if ( (mediaBlockSize % sizeof(disk_blk0)) ) goto scanErr;
bufferSize = IORound(sizeof(disk_blk0), mediaBlockSize);
buffer = IOBufferMemoryDescriptor::withCapacity(
bufferSize,
kIODirectionIn );
if ( buffer == 0 ) goto scanErr;
partitions = OSSet::withCapacity(4);
if ( partitions == 0 ) goto scanErr;
mediaIsOpen = open(this, 0, kIOStorageAccessReader);
if ( mediaIsOpen == false ) goto scanErr;
do
{
status = media->read(this, fdiskBlock * mediaBlockSize, buffer);
if ( status != kIOReturnSuccess ) goto scanErr;
fdiskMap = (disk_blk0 *) buffer->getBytesNoCopy();
if ( OSSwapLittleToHostInt16(fdiskMap->signature) != DISK_SIGNATURE )
{
goto scanErr;
}
fdiskBlockNext = 0;
for ( unsigned index = 0; index < DISK_NPART; index++ )
{
if ( isPartitionExtended(fdiskMap->parts + index) ) {
if ( fdiskBlockNext == 0 ) {
fdiskBlockNext = fdiskBlockExtn +
OSSwapLittleToHostInt32(
fdiskMap->parts[index].relsect );
if ( fdiskBlockNext * mediaBlockSize >= media->getSize() )
{
fdiskBlockNext = 0; }
}
}
else if ( isPartitionUsed(fdiskMap->parts + index) ) {
fdiskID = ( fdiskBlock == 0 ) ? (index + 1) : (fdiskID + 1);
if ( isPartitionCorrupt(
fdiskMap->parts + index,
fdiskID,
fdiskBlock ) )
{
goto scanErr;
}
if ( isPartitionInvalid(
fdiskMap->parts + index,
fdiskID,
fdiskBlock ) )
{
continue;
}
IOMedia * newMedia = instantiateMediaObject(
fdiskMap->parts + index,
fdiskID,
fdiskBlock );
if ( newMedia )
{
partitions->setObject(newMedia);
newMedia->release();
}
}
}
if ( fdiskBlock == 0 )
{
fdiskID = DISK_NPART;
fdiskBlockExtn = fdiskBlockNext;
}
} while ( (fdiskBlock = fdiskBlockNext) );
close(this);
buffer->release();
return partitions;
scanErr:
if ( mediaIsOpen ) close(this);
if ( partitions ) partitions->release();
if ( buffer ) buffer->release();
return 0;
}
bool IOFDiskPartitionScheme::isPartitionExtended(fdisk_part * partition)
{
return ( partition->systid == 0x05 ||
partition->systid == 0x0F ||
partition->systid == 0x85 );
}
bool IOFDiskPartitionScheme::isPartitionUsed(fdisk_part * partition)
{
return ( partition->systid != 0 && partition->numsect != 0 );
}
bool IOFDiskPartitionScheme::isPartitionCorrupt( fdisk_part * partition,
UInt32 partitionID,
UInt32 fdiskBlock )
{
if ( (partition->bootid & 0x7F) ) return true;
return false;
}
bool IOFDiskPartitionScheme::isPartitionInvalid( fdisk_part * partition,
UInt32 partitionID,
UInt32 fdiskBlock )
{
IOMedia * media = getProvider();
UInt64 mediaBlockSize = media->getPreferredBlockSize();
UInt64 partitionBase = 0;
UInt64 partitionSize = 0;
partitionBase = OSSwapLittleToHostInt32(partition->relsect) + fdiskBlock;
partitionSize = OSSwapLittleToHostInt32(partition->numsect);
partitionBase *= mediaBlockSize;
partitionSize *= mediaBlockSize;
if ( partitionBase == fdiskBlock * mediaBlockSize ) return true;
if ( partitionBase >= media->getSize() ) return true;
return false;
}
IOMedia * IOFDiskPartitionScheme::instantiateMediaObject(
fdisk_part * partition,
UInt32 partitionID,
UInt32 fdiskBlock )
{
IOMedia * media = getProvider();
UInt64 mediaBlockSize = media->getPreferredBlockSize();
UInt64 partitionBase = 0;
char * partitionHint = 0;
UInt64 partitionSize = 0;
partitionBase = OSSwapLittleToHostInt32(partition->relsect) + fdiskBlock;
partitionSize = OSSwapLittleToHostInt32(partition->numsect);
partitionBase *= mediaBlockSize;
partitionSize *= mediaBlockSize;
if ( partitionBase + partitionSize > media->getSize() )
{
partitionSize = media->getSize() - partitionBase;
}
char hintIndex[5];
snprintf(hintIndex, sizeof(hintIndex), "0x%02X", partition->systid & 0xFF);
partitionHint = hintIndex;
OSDictionary * hintTable = OSDynamicCast(
OSDictionary,
getProperty(kIOFDiskPartitionSchemeContentTable) );
if ( hintTable )
{
OSString * hintValue;
hintValue = OSDynamicCast(OSString, hintTable->getObject(hintIndex));
if ( hintValue ) partitionHint = (char *) hintValue->getCStringNoCopy();
}
IOMedia * newMedia = instantiateDesiredMediaObject(
partition,
partitionID,
fdiskBlock );
if ( newMedia )
{
if ( newMedia->init(
partitionBase,
partitionSize,
mediaBlockSize,
media->getAttributes(),
false,
media->isWritable(),
partitionHint ) )
{
char name[24];
snprintf(name, sizeof(name), "Untitled %d", (int) partitionID);
newMedia->setName(name);
char location[12];
snprintf(location, sizeof(location), "%d", (int) partitionID);
newMedia->setLocation(location);
newMedia->setProperty(kIOMediaBaseKey, partitionBase, 64);
newMedia->setProperty(kIOMediaPartitionIDKey, partitionID, 32);
}
else
{
newMedia->release();
newMedia = 0;
}
}
return newMedia;
}
IOMedia * IOFDiskPartitionScheme::instantiateDesiredMediaObject(
fdisk_part * partition,
UInt32 partitionID,
UInt32 fdiskBlock )
{
return new IOMedia;
}
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 0);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 1);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 2);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 3);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 4);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 5);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 6);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 7);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 8);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 9);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 10);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 11);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 12);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 13);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 14);
OSMetaClassDefineReservedUnused(IOFDiskPartitionScheme, 15);