IOCDPartitionScheme.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOLib.h>
#include <IOKit/storage/IOCDPartitionScheme.h>
#define super IOPartitionScheme
OSDefineMetaClassAndStructors(IOCDPartitionScheme, IOPartitionScheme);
#define kIOCDPartitionSchemeContentTable "Content Table"
IOCDMedia * IOCDPartitionScheme::getProvider() const
{
return (IOCDMedia *) IOService::getProvider();
}
bool IOCDPartitionScheme::init(OSDictionary * properties)
{
assert(sizeof(CDTOC) == 4); assert(sizeof(CDTOCDescriptor) == 11);
if (super::init(properties) == false) return false;
_partitions = 0;
return true;
}
void IOCDPartitionScheme::free()
{
if ( _partitions ) _partitions->release();
super::free();
}
IOService * IOCDPartitionScheme::probe(IOService * provider, SInt32 * score)
{
assert(OSDynamicCast(IOCDMedia, provider));
if (super::probe(provider, score) == 0) return 0;
_partitions = scan(score);
return ( _partitions ) ? this : 0;
}
bool IOCDPartitionScheme::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) )
{
partition->registerService();
}
}
partitionIterator->release();
return true;
}
OSSet * IOCDPartitionScheme::scan(SInt32 * score)
{
struct CDSession
{
UInt32 formed:1;
UInt32 leadOut;
};
struct CDTrack
{
UInt32 block;
CDSectorSize blockSize;
CDSectorType blockType;
CDTOCDescriptor * descriptor;
UInt32 session:8;
};
#define kCDSessionMaxIndex 0x63
#define kCDTrackMinIndex 0x01
#define kCDTrackMaxIndex 0x63
IOBufferMemoryDescriptor * buffer = 0;
IOCDMedia * media = getProvider();
UInt64 mediaBlockSize = media->getPreferredBlockSize();
bool mediaIsOpen = false;
OSSet * partitions = 0;
CDSession * sessions = 0;
UInt32 sessionMinIndex = kCDSessionMaxIndex + 1;
UInt32 sessionMaxIndex = 0;
CDTOC * toc = 0;
UInt32 tocCount = 0;
CDTrack * tracks = 0;
UInt32 trackMinIndex = kCDTrackMaxIndex + 1;
UInt32 trackMaxIndex = 0;
CDTrack * trackMaxLinked = 0;
assert(mediaBlockSize == kCDSectorSizeWhole);
if ( media->isFormatted() == false ) goto scanErr;
buffer = IOBufferMemoryDescriptor::withCapacity(
mediaBlockSize,
kIODirectionIn );
if ( buffer == 0 ) goto scanErr;
partitions = OSSet::withCapacity(2);
if ( partitions == 0 ) goto scanErr;
mediaIsOpen = media->open(this, 0, kIOStorageAccessReader);
if ( mediaIsOpen == false ) goto scanErr;
toc = media->getTOC();
if ( toc ) tocCount = CDTOCGetDescriptorCount(toc);
sessions = IONew(CDSession, kCDSessionMaxIndex + 1);
if ( sessions == 0 ) goto scanErr;
bzero(sessions, (kCDSessionMaxIndex + 1) * sizeof(CDSession));
tracks = IONew(CDTrack, kCDTrackMaxIndex + 1);
if ( tracks == 0 ) goto scanErr;
bzero(tracks, (kCDTrackMaxIndex + 1) * sizeof(CDTrack));
for ( unsigned index = 0; index < tocCount; index++ )
{
CDTOCDescriptor * descriptor = toc->descriptors + index;
if ( descriptor->point >= kCDTrackMinIndex &&
descriptor->point <= kCDTrackMaxIndex &&
descriptor->adr == 0x01 &&
descriptor->session <= kCDSessionMaxIndex )
{
CDTrack * track = tracks + descriptor->point;
track->block = CDConvertMSFToClippedLBA(descriptor->p);
track->descriptor = descriptor;
track->session = descriptor->session;
if ( (descriptor->control & 0x04) ) {
track->blockSize = kCDSectorSizeMode1;
track->blockType = kCDSectorTypeMode1;
}
else {
track->blockSize = kCDSectorSizeCDDA;
track->blockType = kCDSectorTypeCDDA;
}
trackMinIndex = min(descriptor->point, trackMinIndex);
trackMaxIndex = max(descriptor->point, trackMaxIndex);
}
else if ( descriptor->point == 0xA0 &&
descriptor->adr == 0x01 &&
descriptor->session <= kCDSessionMaxIndex )
{
CDSession * session = sessions + descriptor->session;
session->formed = ( descriptor->p.second ) ? true : false;
}
else if ( descriptor->point == 0xA2 &&
descriptor->adr == 0x01 &&
descriptor->session <= kCDSessionMaxIndex )
{
CDSession * session = sessions + descriptor->session;
session->leadOut = CDConvertMSFToClippedLBA(descriptor->p);
sessionMinIndex = min(descriptor->session, sessionMinIndex);
sessionMaxIndex = max(descriptor->session, sessionMaxIndex);
}
}
for ( unsigned index = trackMinIndex; index <= trackMaxIndex; index++ )
{
CDTrack * track = tracks + index;
if ( track->descriptor == 0 || sessions[track->session].leadOut == 0 )
{
goto scanErr;
}
if ( track->blockType == kCDSectorTypeMode1 ) {
IOReturn status;
status = media->read( this,
track->block * mediaBlockSize,
buffer );
if ( status == kIOReturnSuccess )
{
UInt8 * sector = (UInt8 *) buffer->getBytesNoCopy();
if ( sector[15] == 0x02 )
{
if ( sessions[track->session ].formed ||
sessions[sessionMinIndex].formed )
{
if ( (sector[18] & 0x20) )
{
track->blockSize = kCDSectorSizeMode2Form2;
track->blockType = kCDSectorTypeMode2Form2;
trackMaxLinked = track;
}
else
{
track->blockSize = kCDSectorSizeMode2Form1;
track->blockType = kCDSectorTypeMode2Form1;
if ( memcmp(sector + 24, "ER", 2) )
{
trackMaxLinked = track;
}
}
}
else
{
track->blockSize = kCDSectorSizeMode2;
track->blockType = kCDSectorTypeMode2;
trackMaxLinked = track;
}
}
else if ( memcmp(sector + 16, "ER", 2) )
{
trackMaxLinked = track;
}
}
else
{
trackMaxLinked = track;
}
}
}
if ( trackMinIndex > kCDTrackMaxIndex || trackMaxLinked || tracks[trackMinIndex].block ) {
CDTOCDescriptor descriptor;
UInt32 trackBlockNext;
CDSectorSize trackBlockSize;
CDSectorType trackBlockType;
UInt64 trackSize;
descriptor.session = 0x01;
descriptor.control = 0x04;
descriptor.adr = 0x01;
descriptor.tno = 0x00;
descriptor.point = 0x00;
descriptor.address.minute = 0x00;
descriptor.address.second = 0x00;
descriptor.address.frame = 0x00;
descriptor.zero = 0x00;
descriptor.p = CDConvertLBAToMSF(0);
if ( trackMinIndex > kCDTrackMaxIndex ) {
if ( sessionMinIndex > kCDSessionMaxIndex ) {
if ( media->isWritable() == false ) goto scanErr;
}
else
{
descriptor.session = sessionMaxIndex;
}
if ( media->isWritable() ) {
CDPMA * pma = (CDPMA *) buffer->getBytesNoCopy();
UInt32 pmaCount = 0;
UInt16 pmaSize = 0;
trackBlockNext = media->getSize() / mediaBlockSize;
trackBlockSize = kCDSectorSizeMode2Form1;
trackBlockType = kCDSectorTypeMode2Form1;
media->readTOC( buffer,
kCDTOCFormatPMA,
true,
0,
&pmaSize );
pmaSize = ( pmaSize <= sizeof(CDPMA) )
? pmaSize
: min( pmaSize,
OSSwapBigToHostInt16(pma->dataLength) +
sizeof(pma->dataLength) );
pmaCount = ( pmaSize <= sizeof(CDPMA) )
? 0
: ( pmaSize - sizeof(CDPMA) ) /
sizeof(CDPMADescriptor);
for ( unsigned index = 0; index < pmaCount; index++ )
{
if ( pma->descriptors[index].adr == 0x2 )
{
if ( pma->descriptors[index].p.second == 0x00 )
{
trackBlockSize = kCDSectorSizeMode1;
trackBlockType = kCDSectorTypeMode1;
break;
}
}
}
}
else
{
trackBlockNext = sessions[sessionMaxIndex].leadOut;
trackBlockSize = kCDSectorSizeMode2Form1;
trackBlockType = kCDSectorTypeMode2Form1;
}
}
else if ( trackMaxLinked ) {
descriptor.session = sessionMaxIndex;
descriptor.control = trackMaxLinked->descriptor->control;
if ( media->isWritable() ) {
trackBlockNext = media->getSize() / mediaBlockSize;
}
else
{
trackBlockNext = sessions[sessionMaxIndex].leadOut;
}
if ( trackMaxLinked->blockType == kCDSectorTypeMode1 )
{
trackBlockSize = kCDSectorSizeMode1;
trackBlockType = kCDSectorTypeMode1;
}
else
{
trackBlockSize = kCDSectorSizeMode2Form1;
trackBlockType = kCDSectorTypeMode2Form1;
}
}
else {
IOReturn status;
descriptor.session = sessionMinIndex;
trackBlockNext = tracks[trackMinIndex].block;
trackBlockSize = kCDSectorSizeMode1;
trackBlockType = kCDSectorTypeMode1;
status = media->read( this,
0,
buffer );
if ( status == kIOReturnSuccess )
{
UInt8 * sector = (UInt8 *) buffer->getBytesNoCopy();
if ( sector[ 0] != 0x00 ||
sector[ 1] != 0xFF ||
sector[ 2] != 0xFF ||
sector[ 3] != 0xFF ||
sector[ 4] != 0xFF ||
sector[ 5] != 0xFF ||
sector[ 6] != 0xFF ||
sector[ 7] != 0xFF ||
sector[ 8] != 0xFF ||
sector[ 9] != 0xFF ||
sector[10] != 0xFF ||
sector[11] != 0x00 )
{
trackBlockSize = kCDSectorSizeCDDA;
trackBlockType = kCDSectorTypeCDDA;
}
}
}
trackSize = trackBlockNext * trackBlockSize;
IOMedia * newMedia = instantiateMediaObject(
&descriptor,
trackSize,
trackBlockSize,
trackBlockType,
toc );
if ( newMedia )
{
partitions->setObject(newMedia);
newMedia->release();
}
}
for ( unsigned index = trackMinIndex; index <= trackMaxIndex; index++ )
{
CDTrack * track = tracks + index;
UInt32 trackBlockNext;
UInt64 trackSize;
if ( ( ( track->blockType != kCDSectorTypeCDDA ) ) &&
( ( track->blockType != kCDSectorTypeMode1 &&
track->blockType != kCDSectorTypeMode2Form1 ) ||
( trackMaxLinked ) ) )
{
continue;
}
if ( index < trackMaxIndex && track->session == (track + 1)->session )
{
trackBlockNext = (track + 1)->block;
}
else
{
trackBlockNext = sessions[track->session].leadOut;
}
if ( track->block >= trackBlockNext )
{
goto scanErr;
}
trackSize = (trackBlockNext - track->block) * track->blockSize;
if ( isPartitionCorrupt( track->descriptor,
trackSize,
track->blockSize,
track->blockType,
toc ) )
{
goto scanErr;
}
if ( isPartitionInvalid( track->descriptor,
trackSize,
track->blockSize,
track->blockType,
toc ) )
{
continue;
}
IOMedia * newMedia = instantiateMediaObject(
track->descriptor,
trackSize,
track->blockSize,
track->blockType,
toc );
if ( newMedia )
{
partitions->setObject(newMedia);
newMedia->release();
}
}
media->close(this);
buffer->release();
IODelete(tracks, CDTrack, kCDTrackMaxIndex + 1);
IODelete(sessions, CDSession, kCDSessionMaxIndex + 1);
return partitions;
scanErr:
if ( mediaIsOpen ) media->close(this);
if ( partitions ) partitions->release();
if ( buffer ) buffer->release();
if ( tracks ) IODelete(tracks, CDTrack, kCDTrackMaxIndex + 1);
if ( sessions ) IODelete(sessions, CDSession, kCDSessionMaxIndex + 1);
return 0;
}
bool IOCDPartitionScheme::isPartitionCorrupt(
CDTOCDescriptor * ,
UInt64 ,
UInt32 ,
CDSectorType ,
CDTOC * )
{
return false;
}
bool IOCDPartitionScheme::isPartitionInvalid(
CDTOCDescriptor * partition,
UInt64 partitionSize,
UInt32 partitionBlockSize,
CDSectorType partitionBlockType,
CDTOC * toc )
{
IOMedia * media = getProvider();
UInt64 mediaBlockSize = media->getPreferredBlockSize();
UInt64 partitionBase = 0;
partitionBase = CDConvertMSFToClippedLBA(partition->p) * mediaBlockSize;
partitionSize = (partitionSize / partitionBlockSize) * mediaBlockSize;
if ( partitionBase + partitionSize > media->getSize() ) return true;
return false;
}
IOMedia * IOCDPartitionScheme::instantiateMediaObject(
CDTOCDescriptor * partition,
UInt64 partitionSize,
UInt32 partitionBlockSize,
CDSectorType partitionBlockType,
CDTOC * toc )
{
IOMedia * media = getProvider();
UInt64 partitionBase = 0;
char * partitionHint = 0;
bool partitionIsWritable = media->isWritable();
partitionBase = CDConvertMSFToClippedLBA(partition->p);
partitionBase *= partitionBlockSize;
partitionBase += ((UInt64) partitionBlockType) << 32;
OSDictionary * hintTable = OSDynamicCast(
OSDictionary,
getProperty(kIOCDPartitionSchemeContentTable) );
if ( hintTable )
{
char hintIndex[5];
OSString * hintValue;
sprintf(hintIndex, "0x%02X", partitionBlockType & 0xFF);
hintValue = OSDynamicCast(OSString, hintTable->getObject(hintIndex));
if ( hintValue ) partitionHint = (char *) hintValue->getCStringNoCopy();
}
if ( partition->point ) partitionIsWritable = false;
IOMedia * newMedia = instantiateDesiredMediaObject(
partition,
partitionSize,
partitionBlockSize,
partitionBlockType,
toc );
if ( newMedia )
{
if ( newMedia->init(
partitionBase,
partitionSize,
partitionBlockSize,
media->getAttributes(),
false,
partitionIsWritable,
partitionHint ) )
{
char name[24];
sprintf(name, "Untitled %d", partition->point);
newMedia->setName(name);
char location[12];
sprintf(location, "%d", partition->point);
newMedia->setLocation(location);
newMedia->setProperty(kIOMediaPartitionIDKey, partition->point, 32);
newMedia->setProperty(kIOMediaSessionIDKey, partition->session, 32);
}
else
{
newMedia->release();
newMedia = 0;
}
}
return newMedia;
}
IOMedia * IOCDPartitionScheme::instantiateDesiredMediaObject(
CDTOCDescriptor * ,
UInt64 ,
UInt32 ,
CDSectorType ,
CDTOC * )
{
return new IOMedia;
}
void IOCDPartitionScheme::read( IOService * client,
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageCompletion completion )
{
getProvider()->readCD( this,
(byteStart & 0xFFFFFFFF),
buffer,
(CDSectorArea) kCDSectorAreaUser,
(CDSectorType) (byteStart >> 32),
completion );
}
void IOCDPartitionScheme::write( IOService * client,
UInt64 byteStart,
IOMemoryDescriptor * buffer,
IOStorageCompletion completion )
{
getProvider()->writeCD( this,
(byteStart & 0xFFFFFFFF),
buffer,
(CDSectorArea) kCDSectorAreaUser,
(CDSectorType) (byteStart >> 32),
completion );
}
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 0);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 1);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 2);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 3);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 4);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 5);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 6);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 7);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 8);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 9);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 10);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 11);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 12);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 13);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 14);
OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 15);