IOApplePartitionScheme.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOLib.h>
#include <IOKit/storage/IOApplePartitionScheme.h>
#include <libkern/OSByteOrder.h>
#define super IOPartitionScheme
OSDefineMetaClassAndStructors(IOApplePartitionScheme, IOPartitionScheme);
bool IOApplePartitionScheme::init(OSDictionary * properties)
{
assert(sizeof(dpme) == 512); assert(sizeof(DDMap) == 8); assert(sizeof(Block0) == 512);
if (super::init(properties) == false) return false;
_partitions = 0;
return true;
}
void IOApplePartitionScheme::free()
{
if ( _partitions ) _partitions->release();
super::free();
}
IOService * IOApplePartitionScheme::probe(IOService * provider, SInt32 * score)
{
assert(OSDynamicCast(IOMedia, provider));
if (super::probe(provider, score) == 0) return 0;
_partitions = scan(score);
return ( _partitions ) ? this : 0;
}
bool IOApplePartitionScheme::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();
return true;
}
void IOApplePartitionScheme::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 IOApplePartitionScheme::requestProbe(IOOptionBits options)
{
OSSet * partitions = 0;
OSSet * partitionsNew;
SInt32 score = 0;
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 * IOApplePartitionScheme::scan(SInt32 * score)
{
IOBufferMemoryDescriptor * buffer = 0;
UInt32 bufferReadAt = 0;
IOByteCount bufferSize = 0;
UInt32 dpmeBlockSize = 0;
UInt32 dpmeCount = 0;
UInt32 dpmeID = 0;
dpme * dpmeMap = 0;
UInt32 dpmeMaxCount = 0;
bool dpmeOldSchool = false;
Block0 * driverMap = 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(dpme)) ) goto scanErr;
bufferSize = IORound(max(sizeof(Block0), sizeof(dpme)), mediaBlockSize);
buffer = IOBufferMemoryDescriptor::withCapacity(
bufferSize,
kIODirectionIn );
if ( buffer == 0 ) goto scanErr;
partitions = OSSet::withCapacity(8);
if ( partitions == 0 ) goto scanErr;
mediaIsOpen = open(this, 0, kIOStorageAccessReader);
if ( mediaIsOpen == false ) goto scanErr;
bufferReadAt = 0;
status = media->read(this, bufferReadAt, buffer);
if ( status != kIOReturnSuccess ) goto scanErr;
driverMap = (Block0 *) buffer->getBytesNoCopy();
dpmeBlockSize = (UInt32) mediaBlockSize;
if ( OSSwapBigToHostInt16(driverMap->sbSig) == BLOCK0_SIGNATURE )
{
dpmeBlockSize = OSSwapBigToHostInt16(driverMap->sbBlkSize);
*score += 2000;
}
if ( dpmeBlockSize == 2048 )
{
if ( bufferSize >= sizeof(Block0) + sizeof(dpme) ) {
dpmeMap = (dpme *) (driverMap + 1);
}
else {
bufferReadAt = sizeof(dpme);
status = media->read(this, bufferReadAt, buffer);
if ( status != kIOReturnSuccess ) goto scanErr;
dpmeMap = (dpme *) buffer->getBytesNoCopy();
}
if ( OSSwapBigToHostInt16(dpmeMap->dpme_signature) == DPME_SIGNATURE )
{
dpmeBlockSize = sizeof(dpme); dpmeOldSchool = true;
}
}
for ( dpmeID = 1, dpmeCount = 1; dpmeID <= dpmeCount; dpmeID++ )
{
UInt32 partitionBlockSize = dpmeBlockSize;
if ( dpmeID * dpmeBlockSize + sizeof(dpme) > bufferReadAt + bufferSize )
{
bufferReadAt = dpmeID * dpmeBlockSize;
status = media->read(this, bufferReadAt, buffer);
if ( status != kIOReturnSuccess ) goto scanErr;
}
dpmeMap = (dpme *) ( ((UInt8 *) buffer->getBytesNoCopy()) +
(dpmeID * dpmeBlockSize) - bufferReadAt );
if ( OSSwapBigToHostInt16(dpmeMap->dpme_signature) != DPME_SIGNATURE )
{
goto scanErr;
}
if ( !strncmp(dpmeMap->dpme_type, "Apple_partition_map", sizeof(dpmeMap->dpme_type)) ||
!strncmp(dpmeMap->dpme_type, "Apple_Partition_Map", sizeof(dpmeMap->dpme_type)) ||
!strncmp(dpmeMap->dpme_type, "Apple_patition_map", sizeof(dpmeMap->dpme_type)) )
{
dpmeCount = OSSwapBigToHostInt32(dpmeMap->dpme_map_entries);
dpmeMaxCount = OSSwapBigToHostInt32(dpmeMap->dpme_pblocks);
}
else if ( dpmeCount == 1 )
{
dpmeCount = OSSwapBigToHostInt32(dpmeMap->dpme_map_entries);
}
if ( dpmeOldSchool && (dpmeID % 4) == 0 )
{
if ( !strncmp(dpmeMap->dpme_type, "Apple_Driver", sizeof(dpmeMap->dpme_type)) ||
!strncmp(dpmeMap->dpme_type, "Apple_Driver43", sizeof(dpmeMap->dpme_type)) ||
!strncmp(dpmeMap->dpme_type, "Apple_Driver43_CD", sizeof(dpmeMap->dpme_type)) ||
!strncmp(dpmeMap->dpme_type, "Apple_Driver_ATA", sizeof(dpmeMap->dpme_type)) ||
!strncmp(dpmeMap->dpme_type, "Apple_Driver_ATAPI", sizeof(dpmeMap->dpme_type)) ||
!strncmp(dpmeMap->dpme_type, "Apple_Patches", sizeof(dpmeMap->dpme_type)) )
{
partitionBlockSize = 2048;
}
}
if ( isPartitionCorrupt(
dpmeMap,
dpmeID,
partitionBlockSize ) )
{
goto scanErr;
}
if ( isPartitionInvalid(
dpmeMap,
dpmeID,
partitionBlockSize ) )
{
continue;
}
IOMedia * newMedia = instantiateMediaObject(
dpmeMap,
dpmeID,
partitionBlockSize );
if ( newMedia )
{
partitions->setObject(newMedia);
newMedia->release();
}
}
if ( dpmeMaxCount == 0 ) goto scanErr;
close(this);
buffer->release();
return partitions;
scanErr:
if ( mediaIsOpen ) close(this);
if ( partitions ) partitions->release();
if ( buffer ) buffer->release();
return 0;
}
bool IOApplePartitionScheme::isPartitionCorrupt( dpme * partition,
UInt32 partitionID,
UInt32 partitionBlockSize )
{
if ( !strncmp(partition->dpme_type, "CD_ROM_Mode_1", sizeof(partition->dpme_type)) ) return true;
return false;
}
bool IOApplePartitionScheme::isPartitionInvalid( dpme * partition,
UInt32 partitionID,
UInt32 partitionBlockSize )
{
IOMedia * media = getProvider();
UInt64 partitionBase = 0;
UInt64 partitionSize = 0;
partitionBase = OSSwapBigToHostInt32(partition->dpme_pblock_start);
partitionSize = OSSwapBigToHostInt32(partition->dpme_pblocks);
partitionBase *= partitionBlockSize;
partitionSize *= partitionBlockSize;
if ( partitionSize == 0 ) return true;
if ( partitionBase >= media->getSize() ) return true;
return false;
}
IOMedia * IOApplePartitionScheme::instantiateMediaObject(
dpme * partition,
UInt32 partitionID,
UInt32 partitionBlockSize )
{
IOMedia * media = getProvider();
UInt64 mediaBlockSize = media->getPreferredBlockSize();
UInt64 partitionBase = 0;
char partitionHint[DPISTRLEN + 1];
bool partitionIsWritable = media->isWritable();
char partitionName[DPISTRLEN + 1];
UInt64 partitionSize = 0;
strncpy(partitionHint, partition->dpme_type, DPISTRLEN);
strncpy(partitionName, partition->dpme_name, DPISTRLEN);
partitionHint[DPISTRLEN] = 0;
partitionName[DPISTRLEN] = 0;
partitionBase = OSSwapBigToHostInt32(partition->dpme_pblock_start);
partitionSize = OSSwapBigToHostInt32(partition->dpme_pblocks);
partitionBase *= partitionBlockSize;
partitionSize *= partitionBlockSize;
if ( partitionBase + partitionSize > media->getSize() )
{
partitionSize = media->getSize() - partitionBase;
}
if ( !strncmp(partition->dpme_type, "Apple_Free", sizeof(partition->dpme_type)) ) return 0;
if ( !strncmp(partition->dpme_type, "Apple_partition_map", sizeof(partition->dpme_type)) ||
!strncmp(partition->dpme_type, "Apple_Partition_Map", sizeof(partition->dpme_type)) ||
!strncmp(partition->dpme_type, "Apple_patition_map", sizeof(partition->dpme_type)) ||
( OSSwapBigToHostInt32(partition->dpme_flags) &
( DPME_FLAGS_WRITABLE | DPME_FLAGS_VALID ) ) == DPME_FLAGS_VALID )
{
partitionIsWritable = false;
}
IOMedia * newMedia = instantiateDesiredMediaObject(
partition,
partitionID,
partitionBlockSize );
if ( newMedia )
{
if ( newMedia->init(
partitionBase,
partitionSize,
mediaBlockSize,
media->getAttributes(),
false,
partitionIsWritable,
partitionHint ) )
{
char name[24];
snprintf(name, sizeof(name), "Untitled %d", (int) partitionID);
newMedia->setName(partitionName[0] ? partitionName : 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 * IOApplePartitionScheme::instantiateDesiredMediaObject(
dpme * partition,
UInt32 partitionID,
UInt32 partitionBlockSize )
{
return new IOMedia;
}
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 0);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 1);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 2);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 3);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 4);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 5);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 6);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 7);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 8);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 9);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 10);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 11);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 12);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 13);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 14);
OSMetaClassDefineReservedUnused(IOApplePartitionScheme, 15);