IOMediaBSDClient.cpp [plain text]
#include <sys/types.h> // (miscfs/devfs/devfs.h, ...)
#include <miscfs/devfs/devfs.h> // (devfs_make_node, ...)
#include <sys/buf.h> // (buf_t, ...)
#include <sys/fcntl.h> // (FWRITE, ...)
#include <sys/ioccom.h> // (IOCGROUP, ...)
#include <sys/proc.h> // (proc_is64bit, ...)
#include <sys/stat.h> // (S_ISBLK, ...)
#include <sys/systm.h> // (DEV_BSIZE, ...)
#include <IOKit/assert.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOSubMemoryDescriptor.h>
#include <IOKit/storage/IOBlockStorageDevice.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOMediaBSDClient.h>
#define super IOService
OSDefineMetaClassAndStructors(IOMediaBSDClient, IOService)
const SInt32 kMajor = 14;
const UInt32 kMinorsAddCountBits = 6;
const UInt32 kMinorsAddCountMask = (1 << kMinorsAddCountBits) - 1;
const UInt32 kMinorsAddCount = (1 << kMinorsAddCountBits);
const UInt32 kMinorsMaxCountBits = 16;
const UInt32 kMinorsMaxCountMask = (1 << kMinorsMaxCountBits) - 1;
const UInt32 kMinorsMaxCount = (1 << kMinorsMaxCountBits);
const UInt32 kMinorsBucketCount = kMinorsMaxCount / kMinorsAddCount;
const UInt32 kAnchorsAddCount = 2;
const UInt32 kAnchorsMaxCount = kMinorsMaxCount;
#define kMsgNoWhole "%s: No whole media found for media \"%s\".\n", getName()
#define kMsgNoLocation "%s: No location is found for media \"%s\".\n", getName()
extern "C"
{
int dkclose(dev_t dev, int flags, int devtype, proc_t proc);
int dkioctl(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc);
int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc);
int dkopen(dev_t dev, int flags, int devtype, proc_t proc);
int dkread(dev_t dev, uio_t uio, int flags);
int dksize(dev_t dev);
void dkstrategy(buf_t bp);
int dkwrite(dev_t dev, uio_t uio, int flags);
}
static struct bdevsw bdevswFunctions =
{
dkopen,
dkclose,
dkstrategy,
dkioctl_bdev,
eno_dump,
dksize,
D_DISK
};
struct cdevsw cdevswFunctions =
{
dkopen,
dkclose,
dkread,
dkwrite,
dkioctl,
eno_stop,
eno_reset,
0,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
D_DISK
};
struct dio { dev_t dev; uio_t uio; void * drvdata; };
typedef struct dio * dio_t;
typedef void * dkr_t;
typedef enum { DKRTYPE_BUF, DKRTYPE_DIO } dkrtype_t;
static int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype);
static void dkreadwritecompletion(void *, void *, IOReturn, UInt64);
inline int32_t getminor(dev_t dev)
{
return minor(dev);
}
const UInt32 kInvalidAnchorID = (UInt32) (-1);
struct AnchorSlot
{
UInt32 isAssigned:1; UInt32 isObsolete:1;
IOService * anchor; void * key; IONotifier * notifier; };
class AnchorTable
{
protected:
AnchorSlot * _table;
UInt32 _tableCount;
static IOReturn anchorWasNotified( void * target,
void * parameter,
UInt32 messageType,
IOService * provider,
void * messageArgument,
vm_size_t messageArgumentSize );
public:
AnchorTable();
~AnchorTable();
UInt32 insert(IOService * anchor, void * key);
UInt32 locate(IOService * anchor);
UInt32 locate(IOService * anchor, void * key);
void obsolete(UInt32 anchorID);
void remove(UInt32 anchorID);
UInt32 update(IOService * anchor, void * key);
bool isObsolete(UInt32 anchorID);
};
const UInt32 kInvalidMinorID = (UInt32) (-1);
struct MinorSlot
{
UInt32 isAssigned:1; UInt32 isObsolete:1; UInt32 isOrphaned:1;
UInt32 anchorID; IOMediaBSDClient * client; IOMedia * media; char * name;
UInt64 bdevBlockSize; void * bdevNode; UInt32 bdevOpen; IOStorageAccess bdevOpenLevel;
void * cdevNode; UInt32 cdevOpen; IOStorageAccess cdevOpenLevel; };
class MinorTable
{
protected:
class
{
public:
MinorSlot ** buckets;
inline MinorSlot & operator[](const int i)
{
return (buckets[i >> kMinorsAddCountBits])[i & kMinorsAddCountMask];
}
} _table;
UInt32 _tableCount;
public:
MinorTable();
~MinorTable();
UInt32 insert( IOMedia * media,
UInt32 anchorID,
IOMediaBSDClient * client,
char * slicePath );
UInt32 update( IOMedia * media,
UInt32 anchorID,
IOMediaBSDClient * client,
char * slicePath );
UInt32 locate(IOMedia * media);
void obsolete(UInt32 minorID);
void remove(UInt32 minorID);
bool isObsolete(UInt32 minorID);
MinorSlot * getMinor(UInt32 minorID);
UInt32 getOpenCountForAnchorID(UInt32 anchorID);
bool hasReferencesToAnchorID(UInt32 anchorID, bool excludeOrphans);
};
class IOMediaBSDClientGlobals
{
protected:
AnchorTable * _anchors; MinorTable * _minors;
UInt32 _bdevswInstalled:1; UInt32 _cdevswInstalled:1;
IOLock * _openLock; IOLock * _stateLock;
public:
IOMediaBSDClientGlobals();
~IOMediaBSDClientGlobals();
AnchorTable * getAnchors();
MinorTable * getMinors();
MinorSlot * getMinor(UInt32 minorID);
bool isValid();
void lockOpen();
void unlockOpen();
void lockState();
void unlockState();
};
static IOMediaBSDClientGlobals gIOMediaBSDClientGlobals;
bool IOMediaBSDClient::init(OSDictionary * properties)
{
if ( super::init(properties) == false ) return false;
if ( gIOMediaBSDClientGlobals.isValid() == false ) return false;
_anchors = gIOMediaBSDClientGlobals.getAnchors();
_minors = gIOMediaBSDClientGlobals.getMinors();
return true;
}
void IOMediaBSDClient::free()
{
super::free();
}
bool IOMediaBSDClient::start(IOService * provider)
{
IOMedia * media = (IOMedia *) provider;
if ( super::start(provider) == false ) return false;
gIOMediaBSDClientGlobals.lockState();
createNodes(media);
gIOMediaBSDClientGlobals.unlockState();
registerService();
return true;
}
bool IOMediaBSDClient::terminate(IOOptionBits options)
{
UInt32 minorID;
if ( super::terminate(options) == false ) return false;
gIOMediaBSDClientGlobals.lockState();
minorID = gIOMediaBSDClientGlobals.getMinors()->locate(getProvider());
if ( minorID != kInvalidMinorID )
{
MinorSlot * minor;
minor = gIOMediaBSDClientGlobals.getMinors()->getMinor(minorID);
if ( minor->bdevOpen || minor->cdevOpen )
{
gIOMediaBSDClientGlobals.getMinors()->obsolete(minorID);
}
else
{
gIOMediaBSDClientGlobals.getMinors()->remove(minorID);
}
}
gIOMediaBSDClientGlobals.unlockState();
return true;
}
IOMedia * IOMediaBSDClient::getWholeMedia( IOMedia * media,
UInt32 * slicePathSize,
char * slicePath )
{
UInt32 depth = 1;
UInt32 position = sizeof('\0');
IOService * service = 0;
assert(slicePath == 0 || slicePathSize != 0);
for ( service = media; service; service = service->getProvider() )
{
if ( OSDynamicCast(IOMedia, service) ) {
if ( ((IOMedia *)service)->isWhole() ) {
if ( slicePath ) {
slicePath[*slicePathSize - 1] = 0;
if ( position < *slicePathSize ) {
memmove( slicePath, slicePath + (*slicePathSize - position),
position );
}
}
else if ( slicePathSize ) {
*slicePathSize = position;
}
return (IOMedia *)service; }
const char * location = service->getLocation();
if ( location == 0 ) {
if ( service == media ) IOLog(kMsgNoLocation, media->getName());
return 0;
}
position += sizeof('s') + strlen(location);
if ( slicePath ) {
char * path = slicePath + *slicePathSize - position;
if ( position > *slicePathSize ) { assert(0); return 0; }
*path = 's';
strncpy(path + sizeof('s'), location, strlen(location));
}
depth += 1;
}
}
if ( depth == 1 ) IOLog(kMsgNoWhole, media->getName());
return 0;
}
bool IOMediaBSDClient::createNodes(IOMedia * media)
{
IOService * anchor;
AnchorTable * anchors = gIOMediaBSDClientGlobals.getAnchors();
UInt32 anchorID;
bool anchorNew = false;
MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
UInt32 minorID;
char * slicePath = 0;
UInt32 slicePathSize;
IOMedia * whole;
whole = getWholeMedia(media, &slicePathSize);
if ( whole == 0 ) return false;
anchor = whole->getProvider();
if ( anchor == 0 ) return false;
anchorID = anchors->locate(anchor, whole);
if ( anchorID == kInvalidAnchorID )
{
anchorID = anchors->update(anchor, whole);
}
if ( anchorID == kInvalidAnchorID )
{
anchorID = anchors->insert(anchor, whole); if ( anchorID == kInvalidAnchorID ) return false;
anchorNew = true;
}
slicePath = (char *) IOMalloc(slicePathSize);
if ( slicePath == 0 ) goto createNodesErr;
whole = getWholeMedia(media, &slicePathSize, slicePath);
assert(whole);
minorID = minors->update(media, anchorID, this, slicePath);
if ( minorID == kInvalidMinorID )
{
minorID = minors->insert(media, anchorID, this, slicePath);
if ( minorID == kInvalidMinorID ) goto createNodesErr;
}
media->setProperty(kIOBSDNameKey, minors->getMinor(minorID)->name);
media->setProperty(kIOBSDUnitKey, anchorID, 32); media->setProperty(kIOBSDMajorKey, kMajor, 32); media->setProperty(kIOBSDMinorKey, minorID, 32);
IOFree(slicePath, slicePathSize);
return true;
createNodesErr:
if (anchorNew) anchors->remove(anchorID);
if (slicePath) IOFree(slicePath, slicePathSize);
return false; }
#ifndef __LP64__
AnchorTable * IOMediaBSDClient::getAnchors()
{
return _anchors;
}
MinorTable * IOMediaBSDClient::getMinors()
{
return _minors;
}
MinorSlot * IOMediaBSDClient::getMinor(UInt32 minorID)
{
return _minors->getMinor(minorID);
}
#endif
IOMedia * IOMediaBSDClient::getProvider() const
{
return (IOMedia *) IOService::getProvider();
}
int IOMediaBSDClient::ioctl( dev_t dev,
u_long cmd,
caddr_t data,
int flags,
proc_t proc )
{
int error = 0;
switch ( cmd )
{
default:
{
IOLog( "%s: ioctl(%s\'%c\',%d,%d) is unsupported.\n",
gIOMediaBSDClientGlobals.getMinor(getminor(dev))->name,
((cmd & IOC_INOUT) == IOC_INOUT) ? ("_IOWR,") :
( ((cmd & IOC_OUT) == IOC_OUT) ? ("_IOR,") :
( ((cmd & IOC_IN) == IOC_IN) ? ("_IOW,") :
( ((cmd & IOC_VOID) == IOC_VOID) ? ("_IO,") : "" ) ) ),
(char) IOCGROUP(cmd),
(int) (cmd & 0xff),
(int) IOCPARM_LEN(cmd) );
error = ENOTTY;
} break;
}
return error; }
#ifdef __LP64__
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 0);
#else
OSMetaClassDefineReservedUsed(IOMediaBSDClient, 0);
#endif
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 1);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 2);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 3);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 4);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 5);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 6);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 7);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 8);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 9);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 10);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 11);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 12);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 13);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 14);
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 15);
typedef struct
{
user32_addr_t capacities;
uint32_t capacitiesCount;
uint8_t reserved0064[8];
} dk_format_capacities_32_t;
typedef struct
{
user64_addr_t capacities;
uint32_t capacitiesCount;
uint8_t reserved0096[4];
} dk_format_capacities_64_t;
static IOStorageAccess DK_ADD_ACCESS(IOStorageAccess a1, IOStorageAccess a2)
{
static UInt8 table[4][4] =
{
{ 000, 001, 002, 003 },
{ 001, 001, 001, 001 },
{ 002, 001, 002, 003 },
{ 003, 001, 003, 003 }
};
if ( a1 == kIOStorageAccessNone ) return a2;
if ( a2 == kIOStorageAccessNone ) return a1;
a1 = (a1 - 1) >> 1;
a2 = (a2 - 1) >> 1;
if ( a1 > 003 ) return kIOStorageAccessNone;
if ( a2 > 003 ) return kIOStorageAccessNone;
return (table[a1][a2] << 1) + 1;
}
static bool DKIOC_IS_RESERVED(caddr_t data, uint32_t reserved)
{
UInt32 index;
for ( index = 0; index < sizeof(reserved) * 8; index++, reserved >>= 1 )
{
if ( (reserved & 1) )
{
if ( data[index] ) return true;
}
}
return false;
}
int dkopen(dev_t dev, int flags, int devtype, proc_t )
{
IOStorageAccess access;
int error;
IOStorageAccess level;
IOStorageAccess levelOut;
IOMedia * media;
MinorSlot * minor;
assert(S_ISBLK(devtype) || S_ISCHR(devtype));
gIOMediaBSDClientGlobals.lockOpen(); gIOMediaBSDClientGlobals.lockState();
access = kIOStorageAccessReader;
access |= (flags & FWRITE) ? kIOStorageAccessReaderWriter : 0;
access |= (flags & O_SHLOCK) ? kIOStorageAccessSharedLock : 0;
access |= (flags & O_EXLOCK) ? kIOStorageAccessExclusiveLock : 0;
error = 0;
media = 0;
minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
if ( minor == 0 ) {
error = ENXIO;
}
else if ( minor->isOrphaned ) {
error = EBUSY;
}
else
{
#ifdef __LP64__
static int root = 0;
if ( root == 0 )
{
root = 1;
if ( minor->media->isWritable() )
{
access |= kIOStorageAccessReaderWriter;
}
}
#endif
level = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
levelOut = DK_ADD_ACCESS(level, access);
if ( levelOut == kIOStorageAccessNone ) {
error = EBUSY;
}
else if ( (flags & FWRITE) ) {
if ( minor->media->isWritable() == false )
{
error = EACCES;
}
}
}
if ( error == 0 ) {
IOStorageAccess wasOpenLevel;
if ( S_ISBLK(devtype) ) {
minor->bdevOpen++;
wasOpenLevel = minor->bdevOpenLevel;
minor->bdevOpenLevel = DK_ADD_ACCESS(wasOpenLevel, access);
}
else
{
minor->cdevOpen++;
wasOpenLevel = minor->cdevOpenLevel;
minor->cdevOpenLevel = DK_ADD_ACCESS(wasOpenLevel, access);
}
gIOMediaBSDClientGlobals.unlockState();
if ( level != levelOut ) {
bool success;
media = minor->media;
minor->media->retain();
success = minor->media->open(minor->client, 0, levelOut);
if ( success == false )
{
gIOMediaBSDClientGlobals.lockState();
if ( S_ISBLK(devtype) ) {
minor->bdevOpen--;
minor->bdevOpenLevel = wasOpenLevel;
}
else
{
minor->cdevOpen--;
minor->cdevOpenLevel = wasOpenLevel;
}
assert(minor->isOrphaned == false);
if ( !minor->bdevOpen && !minor->cdevOpen && minor->isObsolete )
{
gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
}
gIOMediaBSDClientGlobals.unlockState();
error = EBUSY;
}
}
}
else
{
gIOMediaBSDClientGlobals.unlockState(); }
gIOMediaBSDClientGlobals.unlockOpen();
if ( media )
{
media->waitQuiet();
media->release();
}
return error;
}
int dkclose(dev_t dev, int , int devtype, proc_t )
{
IOStorageAccess level;
IOStorageAccess levelOut;
IOMedia * media;
MinorSlot * minor;
assert(S_ISBLK(devtype) || S_ISCHR(devtype));
gIOMediaBSDClientGlobals.lockOpen(); gIOMediaBSDClientGlobals.lockState();
media = 0;
minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
level = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
if ( S_ISBLK(devtype) ) {
minor->bdevBlockSize = minor->media->getPreferredBlockSize();
minor->bdevOpen = 0;
minor->bdevOpenLevel = kIOStorageAccessNone;
}
else
{
minor->cdevOpen = 0;
minor->cdevOpenLevel = kIOStorageAccessNone;
}
levelOut = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
if ( minor->isOrphaned ) {
minor->isOrphaned = false;
assert(minor->bdevOpen == 0);
assert(minor->cdevOpen == 0);
if ( minor->isObsolete )
{
gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
}
gIOMediaBSDClientGlobals.unlockState(); }
else if ( !minor->bdevOpen && !minor->cdevOpen )
{
IOMediaBSDClient * client;
client = minor->client;
minor->client->retain();
media = minor->media;
minor->media->retain();
if ( minor->isObsolete )
{
gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
}
gIOMediaBSDClientGlobals.unlockState();
media->close(client);
client->release();
}
else if ( level != levelOut )
{
media = minor->media;
minor->media->retain();
gIOMediaBSDClientGlobals.unlockState();
minor->media->open(minor->client, 0, levelOut); }
else
{
gIOMediaBSDClientGlobals.unlockState(); }
gIOMediaBSDClientGlobals.unlockOpen();
if ( media )
{
media->waitQuiet();
media->release();
}
return 0;
}
int dkread(dev_t dev, uio_t uio, int )
{
struct dio dio = { dev, uio };
return dkreadwrite(&dio, DKRTYPE_DIO);
}
int dkwrite(dev_t dev, uio_t uio, int )
{
struct dio dio = { dev, uio };
return dkreadwrite(&dio, DKRTYPE_DIO);
}
void dkstrategy(buf_t bp)
{
dkreadwrite(bp, DKRTYPE_BUF);
}
int dkioctl(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc)
{
int error = 0;
MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
if ( minor->isOrphaned ) return EBADF;
switch ( cmd )
{
case DKIOCGETBLOCKSIZE: {
*(uint32_t *)data = minor->media->getPreferredBlockSize();
} break;
#ifndef __LP64__
case DKIOCGETBLOCKCOUNT32: {
if ( minor->media->getPreferredBlockSize() )
*(uint32_t *)data = ( minor->media->getSize() /
minor->media->getPreferredBlockSize() );
else
*(uint32_t *)data = 0;
} break;
#endif
case DKIOCGETBLOCKCOUNT: {
if ( minor->media->getPreferredBlockSize() )
*(uint64_t *)data = ( minor->media->getSize() /
minor->media->getPreferredBlockSize() );
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXBLOCKCOUNTREAD: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumBlockCountReadKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXBLOCKCOUNTWRITE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumBlockCountWriteKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXBYTECOUNTREAD: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumByteCountReadKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXBYTECOUNTWRITE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumByteCountWriteKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTCOUNTREAD: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentCountReadKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTCOUNTWRITE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentCountWriteKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTBYTECOUNTREAD: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentByteCountReadKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTBYTECOUNTWRITE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentByteCountWriteKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMINSEGMENTALIGNMENTBYTECOUNT: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMinimumSegmentAlignmentByteCountKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETMAXSEGMENTADDRESSABLEBITCOUNT: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOMaximumSegmentAddressableBitCountKey,
gIOServicePlane ) );
if ( number )
*(uint64_t *)data = number->unsigned64BitValue();
else
*(uint64_t *)data = 0;
} break;
case DKIOCGETPHYSICALBLOCKSIZE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOPropertyPhysicalBlockSizeKey,
gIOServicePlane ) );
if ( number )
*(uint32_t *)data = number->unsigned32BitValue();
else
*(uint32_t *)data = minor->media->getPreferredBlockSize();
} break;
case DKIOCGETCOMMANDPOOLSIZE: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOCommandPoolSizeKey,
gIOServicePlane ) );
if ( number )
*(uint32_t *)data = number->unsigned32BitValue();
else
*(uint32_t *)data = 0;
} break;
case DKIOCISFORMATTED: {
*(uint32_t *)data = minor->media->isFormatted();
} break;
case DKIOCISWRITABLE: {
*(uint32_t *)data = minor->media->isWritable();
} break;
case DKIOCEJECT: {
IOMediaBSDClient * client;
IOBlockStorageDriver * driver;
MinorTable * minors;
IOReturn status;
client = minor->client;
driver = (IOBlockStorageDriver *) minor->media->getProvider();
driver = OSDynamicCast(IOBlockStorageDriver, driver);
minors = gIOMediaBSDClientGlobals.getMinors();
if ( driver == 0 ) { error = ENOTTY; break; }
gIOMediaBSDClientGlobals.lockOpen();
gIOMediaBSDClientGlobals.lockState();
if ( minors->getOpenCountForAnchorID(minor->anchorID) == 1 &&
driver->open(client, 0, kIOStorageAccessReaderWriter) )
{
minor->isOrphaned = true;
gIOMediaBSDClientGlobals.unlockState();
gIOMediaBSDClientGlobals.unlockOpen();
minor->media->close(client);
client->retain();
status = driver->ejectMedia();
error = driver->errnoFromReturn(status);
driver->close(client);
client->release();
}
else
{
error = EBUSY;
gIOMediaBSDClientGlobals.unlockState();
gIOMediaBSDClientGlobals.unlockOpen();
}
} break;
case DKIOCFORMAT: {
IOMediaBSDClient * client;
IOBlockStorageDriver * driver;
MinorTable * minors;
dk_format_capacity_t * request;
IOReturn status;
client = minor->client;
driver = (IOBlockStorageDriver *) minor->media->getProvider();
driver = OSDynamicCast(IOBlockStorageDriver, driver);
minors = gIOMediaBSDClientGlobals.getMinors();
request = (dk_format_capacity_t *) data;
if ( DKIOC_IS_RESERVED(data, 0xF000) ) { error = EINVAL; break; }
if ( driver == 0 ) { error = ENOTTY; break; }
gIOMediaBSDClientGlobals.lockOpen();
gIOMediaBSDClientGlobals.lockState();
if ( minors->getOpenCountForAnchorID(minor->anchorID) == 1 &&
driver->open(client, 0, kIOStorageAccessReaderWriter) )
{
UInt64 capacity = request->blockCount * request->blockSize;
minor->isOrphaned = true;
gIOMediaBSDClientGlobals.unlockState();
gIOMediaBSDClientGlobals.unlockOpen();
minor->media->close(client);
client->retain();
status = driver->formatMedia(capacity);
error = driver->errnoFromReturn(status);
driver->waitQuiet();
driver->close(client);
client->release();
}
else
{
error = EBUSY;
gIOMediaBSDClientGlobals.unlockState();
gIOMediaBSDClientGlobals.unlockOpen();
}
} break;
case DKIOCGETFORMATCAPACITIES: {
UInt64 blockSize;
UInt64 * capacities;
UInt32 capacitiesCount;
UInt32 capacitiesMaxCount;
IOBlockStorageDriver * driver;
dk_format_capacities_64_t request;
dk_format_capacities_32_t * request32;
dk_format_capacities_64_t * request64;
driver = (IOBlockStorageDriver *) minor->media->getProvider();
driver = OSDynamicCast(IOBlockStorageDriver, driver);
request32 = (dk_format_capacities_32_t *) data;
request64 = (dk_format_capacities_64_t *) data;
if ( proc_is64bit(proc) )
{
if ( DKIOC_IS_RESERVED(data, 0xF000) ) { error = EINVAL; break; }
request.capacities = request64->capacities;
request.capacitiesCount = request64->capacitiesCount;
}
else
{
if ( DKIOC_IS_RESERVED(data, 0xFF00) ) { error = EINVAL; break; }
request.capacities = request32->capacities;
request.capacitiesCount = request32->capacitiesCount;
}
if ( driver == 0 ) { error = ENOTTY; break; }
capacitiesCount = request.capacitiesCount;
capacitiesMaxCount = driver->getFormatCapacities(0, 0);
if ( capacitiesCount )
{
if ( request.capacities == 0 ) { error = EINVAL; break; }
capacitiesCount = min(capacitiesCount, capacitiesMaxCount);
capacities = IONew(UInt64, capacitiesCount);
if ( capacities == 0 ) { error = ENOMEM; break; }
driver->getFormatCapacities(capacities, capacitiesCount);
blockSize = minor->media->getPreferredBlockSize();
if ( blockSize == 0 ) blockSize = DEV_BSIZE;
for ( UInt32 index = 0; index < capacitiesCount; index++ )
{
dk_format_capacity_t capacity = { 0 };
capacity.blockCount = capacities[index] / blockSize;
capacity.blockSize = blockSize;
error = copyout(
&capacity,
request.capacities + index * sizeof(dk_format_capacity_t),
sizeof(dk_format_capacity_t) );
if ( error ) break;
}
IODelete(capacities, UInt64, capacitiesCount);
if ( capacitiesCount < capacitiesMaxCount ) { error = E2BIG; }
}
if ( proc_is64bit(proc) )
{
request64->capacitiesCount = request.capacitiesCount;
}
else
{
request32->capacitiesCount = request.capacitiesCount;
}
} break;
case DKIOCSYNCHRONIZECACHE: {
IOReturn status;
status = minor->media->synchronizeCache(minor->client);
error = minor->media->errnoFromReturn(status);
} break;
case DKIOCDISCARD: {
dk_discard_t * request;
IOReturn status;
request = (dk_discard_t *) data;
if ( DKIOC_IS_RESERVED(data, 0xFFFF0000) ) { error = EINVAL; break; }
status = minor->media->discard( minor->client,
request->offset,
request->length );
error = minor->media->errnoFromReturn(status);
} break;
case DKIOCREQUESTIDLE: {
IOBlockStorageDriver * driver;
IOReturn status;
driver = (IOBlockStorageDriver *) minor->media->getProvider();
driver = OSDynamicCast(IOBlockStorageDriver, driver);
if ( driver == 0 ) { error = ENOTTY; break; }
status = driver->requestIdle();
error = minor->media->errnoFromReturn(status);
} break;
case DKIOCGETBSDUNIT: {
OSNumber * number = OSDynamicCast(
OSNumber,
minor->media->getProperty(
kIOBSDUnitKey ) );
if ( number )
*(uint32_t *)data = number->unsigned32BitValue();
else
*(uint32_t *)data = 0;
} break;
case DKIOCGETFIRMWAREPATH: {
int l = sizeof(((dk_firmware_path_t *)data)->path);
char * p = ((dk_firmware_path_t *)data)->path;
if ( minor->media->getPath(p, &l, gIODTPlane) && strchr(p, ':') )
strlcpy(p, strchr(p, ':') + 1, l); else
error = EINVAL;
} break;
case DKIOCISVIRTUAL: {
OSDictionary * dictionary = OSDynamicCast(
OSDictionary,
minor->media->getProperty(
kIOPropertyProtocolCharacteristicsKey,
gIOServicePlane ) );
*(uint32_t *)data = false;
if ( dictionary )
{
OSString * string = OSDynamicCast(
OSString,
dictionary->getObject(
kIOPropertyPhysicalInterconnectTypeKey ) );
if ( string && string->isEqualTo( kIOPropertyPhysicalInterconnectTypeVirtual ) )
*(uint32_t *)data = true;
}
} break;
case DKIOCGETBASE: {
*(uint64_t *)data = minor->media->getBase();
} break;
case DKIOCGETFEATURES: {
OSDictionary * dictionary = OSDynamicCast(
OSDictionary,
minor->media->getProperty(
kIOStorageFeaturesKey,
gIOServicePlane ) );
*(uint32_t *)data = 0;
if ( dictionary )
{
OSBoolean * boolean;
boolean = OSDynamicCast(
OSBoolean,
dictionary->getObject(
kIOStorageFeatureDiscard ) );
if ( boolean == kOSBooleanTrue )
*(uint32_t *)data |= DK_FEATURE_DISCARD;
boolean = OSDynamicCast(
OSBoolean,
dictionary->getObject(
kIOStorageFeatureForceUnitAccess ) );
if ( boolean == kOSBooleanTrue )
*(uint32_t *)data |= DK_FEATURE_FORCE_UNIT_ACCESS;
}
} break;
default:
{
error = minor->client->ioctl(dev, cmd, data, flags, proc);
} break;
}
return error; }
int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc)
{
int error = 0;
MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
if ( minor->isOrphaned ) return EBADF;
switch ( cmd )
{
case DKIOCGETBLOCKSIZE: {
*(uint32_t *)data = minor->bdevBlockSize;
} break;
case DKIOCSETBLOCKSIZE: {
if ( *(uint32_t *)data > 0 )
minor->bdevBlockSize = (UInt64) (*(uint32_t *)data);
else
error = EINVAL;
} break;
#ifndef __LP64__
case DKIOCGETBLOCKCOUNT32: {
if ( minor->bdevBlockSize )
*(uint32_t *)data = ( minor->media->getSize() /
minor->bdevBlockSize );
else
*(uint32_t *)data = 0;
} break;
#endif
case DKIOCGETBLOCKCOUNT: {
if ( minor->bdevBlockSize )
*(uint64_t *)data = ( minor->media->getSize() /
minor->bdevBlockSize );
else
*(uint64_t *)data = 0;
} break;
default:
{
error = dkioctl(dev, cmd, data, flags, proc);
} break;
}
return error; }
int dksize(dev_t dev)
{
MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
if ( minor->isOrphaned ) return 0;
return (int) minor->bdevBlockSize; }
extern "C" task_t get_aiotask();
inline task_t get_kernel_task()
{
return kernel_task;
}
inline task_t get_user_task()
{
task_t task;
task = get_aiotask();
if ( task == 0 ) task = current_task();
return task;
}
inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? buf_device((buf_t)dkr)
: ((dio_t)dkr)->dev;
}
inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? buf_count((buf_t)dkr)
: uio_resid(((dio_t)dkr)->uio);
}
inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
{
if (dkrtype == DKRTYPE_BUF)
{
buf_t bp = (buf_t)dkr;
MinorSlot * minor;
minor = gIOMediaBSDClientGlobals.getMinor(getminor(buf_device(bp)));
return (UInt64)buf_blkno(bp) * minor->bdevBlockSize;
}
return uio_offset(((dio_t)dkr)->uio);
}
inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? ((buf_flags((buf_t)dkr) & B_READ) == B_READ)
: ((uio_rw(((dio_t)dkr)->uio)) == UIO_READ);
}
inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? true
: false;
}
inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? false
: true;
}
inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount)
{
if (dkrtype == DKRTYPE_BUF)
buf_setresid((buf_t)dkr, buf_count((buf_t)dkr) - bcount);
else
uio_setresid(((dio_t)dkr)->uio, uio_resid(((dio_t)dkr)->uio) - bcount);
}
inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
{
if (dkrtype == DKRTYPE_BUF)
{
buf_t bp = (buf_t)dkr;
MinorSlot * minor;
minor = gIOMediaBSDClientGlobals.getMinor(getminor(buf_device(bp)));
buf_seterror(bp, minor->media->errnoFromReturn(status)); buf_biodone(bp); }
}
inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
{
if (dkrtype == DKRTYPE_BUF)
{
buf_t bp = (buf_t)dkr;
int flags;
flags = buf_flags(bp);
if ( (flags & B_CLUSTER) )
{
IOOptionBits options = kIOMemoryTypeUPL | kIOMemoryAsReference;
options |= (flags & B_READ) ? kIODirectionIn : kIODirectionOut;
return IOMemoryDescriptor::withOptions( buf_upl(bp),
buf_count(bp),
buf_uploffset(bp),
0,
options );
}
else
{
return IOMemoryDescriptor::withAddressRange( buf_dataptr(bp),
buf_count(bp),
(flags & B_READ) ? kIODirectionIn : kIODirectionOut,
(flags & B_PHYS) ? get_user_task() : get_kernel_task() );
}
}
else
{
IOOptionBits options = kIOMemoryTypeUIO | kIOMemoryAsReference;
uio_t uio = ((dio_t)dkr)->uio;
options |= (uio_rw(uio) == UIO_READ) ? kIODirectionIn : kIODirectionOut;
return IOMemoryDescriptor::withOptions( uio,
uio_iovcnt(uio),
0,
(uio_isuserspace(uio)) ? get_user_task() : get_kernel_task(),
options );
}
}
inline void * DKR_GET_DRIVER_DATA(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? buf_drvdata((buf_t)dkr)
: ((dio_t)dkr)->drvdata;
}
inline void DKR_SET_DRIVER_DATA(dkr_t dkr, dkrtype_t dkrtype, void * drvdata)
{
if (dkrtype == DKRTYPE_BUF)
buf_setdrvdata((buf_t)dkr, drvdata);
else
((dio_t)dkr)->drvdata = drvdata;
}
inline IOStorageAttributes DKR_GET_ATTRIBUTES(dkr_t dkr, dkrtype_t dkrtype)
{
IOStorageAttributes attributes = { 0 };
if (dkrtype == DKRTYPE_BUF)
{
buf_t bp = (buf_t)dkr;
int flags;
flags = buf_flags(bp);
attributes.options |= (flags & B_FUA) ? kIOStorageOptionForceUnitAccess : 0;
}
return attributes;
}
int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype)
{
IOStorageAttributes attributes;
IOMemoryDescriptor * buffer;
register UInt64 byteCount;
register UInt64 byteStart;
UInt64 mediaSize;
MinorSlot * minor;
IOReturn status;
DKR_SET_DRIVER_DATA(dkr, dkrtype, 0);
minor = gIOMediaBSDClientGlobals.getMinor(getminor(DKR_GET_DEV(dkr, dkrtype)));
if ( minor->isOrphaned ) {
status = kIOReturnNoMedia;
goto dkreadwriteErr;
}
if ( minor->media->isFormatted() == false ) {
status = kIOReturnUnformattedMedia;
goto dkreadwriteErr;
}
byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype); byteStart = DKR_GET_BYTE_START(dkr, dkrtype); mediaSize = minor->media->getSize();
if ( byteStart >= mediaSize ) {
status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError;
goto dkreadwriteErr;
}
if ( DKR_IS_RAW(dkr, dkrtype) )
{
UInt64 mediaBlockSize = minor->media->getPreferredBlockSize();
if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
{
status = kIOReturnNotAligned;
goto dkreadwriteErr;
}
}
buffer = DKR_GET_BUFFER(dkr, dkrtype);
if ( buffer == 0 ) {
status = kIOReturnNoMemory;
goto dkreadwriteErr;
}
if ( byteCount > mediaSize - byteStart ) {
IOMemoryDescriptor * originalBuffer = buffer;
buffer = IOSubMemoryDescriptor::withSubRange(
originalBuffer,
0,
mediaSize - byteStart,
originalBuffer->getDirection() );
originalBuffer->release();
if ( buffer == 0 ) {
status = kIOReturnNoMemory;
goto dkreadwriteErr;
}
}
if ( buffer->prepare() != kIOReturnSuccess ) {
buffer->release();
status = kIOReturnVMError; goto dkreadwriteErr;
}
attributes = DKR_GET_ATTRIBUTES(dkr, dkrtype);
DKR_SET_DRIVER_DATA(dkr, dkrtype, buffer);
if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) ) {
IOStorageCompletion completion;
completion.target = dkr;
completion.action = dkreadwritecompletion;
completion.parameter = (void *) dkrtype;
if ( DKR_IS_READ(dkr, dkrtype) ) {
minor->media->read( minor->client,
byteStart,
buffer,
&attributes,
&completion ); }
else {
minor->media->write( minor->client,
byteStart,
buffer,
&attributes,
&completion ); }
status = kIOReturnSuccess;
}
else {
if ( DKR_IS_READ(dkr, dkrtype) ) {
status = minor->media->read(
minor->client,
byteStart,
buffer,
#ifdef __LP64__
&attributes,
#endif
&byteCount ); }
else {
status = minor->media->write(
minor->client,
byteStart,
buffer,
#ifdef __LP64__
&attributes,
#endif
&byteCount ); }
dkreadwritecompletion(dkr, (void *)dkrtype, status, byteCount);
}
return minor->media->errnoFromReturn(status);
dkreadwriteErr:
dkreadwritecompletion(dkr, (void *)dkrtype, status, 0);
return minor->media->errnoFromReturn(status); }
void dkreadwritecompletion( void * target,
void * parameter,
IOReturn status,
UInt64 actualByteCount )
{
dkr_t dkr = (dkr_t) target;
dkrtype_t dkrtype = (dkrtype_t) (uintptr_t) parameter;
dev_t dev = DKR_GET_DEV(dkr, dkrtype);
void * drvdata = DKR_GET_DRIVER_DATA(dkr, dkrtype);
MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
if ( drvdata ) {
IOMemoryDescriptor * buffer = (IOMemoryDescriptor *) drvdata;
buffer->complete(); buffer->release(); }
if ( status != kIOReturnSuccess ) {
IOLog("%s: %s.\n", minor->name, minor->media->stringFromReturn(status));
}
if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) ) {
DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount); DKR_RUN_COMPLETION(dkr, dkrtype, status); }
else
{
DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount); }
}
AnchorTable::AnchorTable()
{
_table = 0;
_tableCount = 0;
}
AnchorTable::~AnchorTable()
{
for ( UInt32 anchorID = 0; anchorID < _tableCount; anchorID++ )
if ( _table[anchorID].isAssigned ) remove(anchorID);
if ( _table ) IODelete(_table, AnchorSlot, _tableCount);
}
UInt32 AnchorTable::insert(IOService * anchor, void * key)
{
UInt32 anchorID;
IONotifier * notifier;
for ( anchorID = 0; anchorID < _tableCount; anchorID++ )
if ( _table[anchorID].isAssigned == false ) break;
if ( anchorID == _tableCount )
{
AnchorSlot * newTable;
UInt32 newTableCount;
if ( _tableCount >= kAnchorsMaxCount ) return kInvalidAnchorID;
newTableCount = min(kAnchorsAddCount + _tableCount, kAnchorsMaxCount);
newTable = IONew(AnchorSlot, newTableCount);
if ( newTable == 0 ) return kInvalidAnchorID;
bzero(newTable, newTableCount * sizeof(AnchorSlot));
if ( _table )
{
bcopy(_table, newTable, _tableCount * sizeof(AnchorSlot));
IODelete(_table, AnchorSlot, _tableCount);
}
anchorID = _tableCount;
_table = newTable;
_tableCount = newTableCount;
}
notifier = anchor->registerInterest(
gIOGeneralInterest,
anchorWasNotified,
this,
0 );
if ( notifier == 0 ) return kInvalidAnchorID;
bzero(&_table[anchorID], sizeof(AnchorSlot));
_table[anchorID].isAssigned = true; _table[anchorID].isObsolete = false;
_table[anchorID].anchor = anchor;
_table[anchorID].key = key;
_table[anchorID].notifier = notifier;
_table[anchorID].anchor->retain();
return anchorID;
}
void AnchorTable::remove(UInt32 anchorID)
{
assert(anchorID < _tableCount);
assert(_table[anchorID].isAssigned);
_table[anchorID].notifier->remove();
_table[anchorID].anchor->release();
bzero(&_table[anchorID], sizeof(AnchorSlot)); }
void AnchorTable::obsolete(UInt32 anchorID)
{
assert(anchorID < _tableCount);
assert(_table[anchorID].isAssigned);
_table[anchorID].isObsolete = true;
}
UInt32 AnchorTable::locate(IOService * anchor)
{
for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
{
if ( _table[anchorID].isAssigned != false &&
_table[anchorID].isObsolete == false &&
_table[anchorID].anchor == anchor ) return anchorID;
}
return kInvalidAnchorID;
}
UInt32 AnchorTable::locate(IOService * anchor, void * key)
{
for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
{
if ( _table[anchorID].isAssigned != false &&
_table[anchorID].isObsolete == false &&
_table[anchorID].anchor == anchor &&
_table[anchorID].key == key ) return anchorID;
}
return kInvalidAnchorID;
}
UInt32 AnchorTable::update(IOService * anchor, void * key)
{
MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
{
if ( _table[anchorID].isAssigned != false &&
_table[anchorID].isObsolete == false &&
_table[anchorID].anchor == anchor )
{
if ( minors->hasReferencesToAnchorID(anchorID, true) == false )
{
_table[anchorID].key = key;
return anchorID;
}
}
}
return kInvalidAnchorID;
}
bool AnchorTable::isObsolete(UInt32 anchorID)
{
assert(anchorID < _tableCount);
assert(_table[anchorID].isAssigned);
return _table[anchorID].isObsolete ? true : false;
}
IOReturn AnchorTable::anchorWasNotified( void * ,
void * ,
UInt32 messageType,
IOService * anchor,
void * ,
vm_size_t )
{
AnchorTable * anchors = gIOMediaBSDClientGlobals.getAnchors();
UInt32 anchorID;
MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
if ( messageType != kIOMessageServiceIsTerminated )
return kIOReturnSuccess;
gIOMediaBSDClientGlobals.lockState();
while ( (anchorID = anchors->locate(anchor)) != kInvalidAnchorID )
{
if ( minors->hasReferencesToAnchorID(anchorID, false) )
anchors->obsolete(anchorID);
else
anchors->remove(anchorID);
}
gIOMediaBSDClientGlobals.unlockState();
return kIOReturnSuccess;
}
MinorTable::MinorTable()
{
_table.buckets = IONew(MinorSlot *, kMinorsBucketCount);
_tableCount = 0;
if ( _table.buckets )
bzero(_table.buckets, kMinorsBucketCount * sizeof(MinorSlot *));
}
MinorTable::~MinorTable()
{
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
if ( _table[minorID].isAssigned ) remove(minorID);
if ( _table.buckets )
{
for ( UInt32 bucketID = 0; _table.buckets[bucketID]; bucketID++ )
IODelete(_table.buckets[bucketID], MinorSlot, kMinorsAddCount);
IODelete(_table.buckets, MinorSlot *, kMinorsBucketCount);
}
}
UInt32 MinorTable::insert( IOMedia * media,
UInt32 anchorID,
IOMediaBSDClient * client,
char * slicePath )
{
void * bdevNode;
void * cdevNode;
UInt32 minorID;
char * minorName;
UInt32 minorNameSize;
if ( _table.buckets == 0 ) return kInvalidMinorID;
for ( minorID = 0; minorID < _tableCount; minorID++ )
if ( _table[minorID].isAssigned == false ) break;
if ( minorID == _tableCount )
{
UInt32 bucketID = _tableCount / kMinorsAddCount;
if ( bucketID >= kMinorsBucketCount ) return kInvalidMinorID;
_table.buckets[bucketID] = IONew(MinorSlot, kMinorsAddCount);
if ( _table.buckets[bucketID] == 0 ) return kInvalidMinorID;
bzero(_table.buckets[bucketID], kMinorsAddCount * sizeof(MinorSlot));
_tableCount += kMinorsAddCount;
}
minorNameSize = strlen("disk#");
for (unsigned temp = anchorID; temp >= 10; temp /= 10) minorNameSize++;
minorNameSize += strlen(slicePath);
minorNameSize += 1;
minorName = IONew(char, minorNameSize);
bdevNode = devfs_make_node( makedev(kMajor, minorID),
DEVFS_BLOCK,
UID_ROOT,
GID_OPERATOR,
0640,
"disk%d%s",
anchorID,
slicePath );
cdevNode = devfs_make_node( makedev(kMajor, minorID),
DEVFS_CHAR,
UID_ROOT,
GID_OPERATOR,
0640,
"rdisk%d%s",
anchorID,
slicePath );
if ( minorName == 0 || bdevNode == 0 || cdevNode == 0 )
{
if ( cdevNode ) devfs_remove(cdevNode);
if ( bdevNode ) devfs_remove(bdevNode);
if ( minorName ) IODelete(minorName, char, minorNameSize);
return kInvalidMinorID;
}
snprintf(minorName, minorNameSize, "disk%d%s", (int) anchorID, slicePath);
assert(strlen(minorName) + 1 == minorNameSize);
bzero(&_table[minorID], sizeof(MinorSlot));
_table[minorID].isAssigned = true; _table[minorID].isObsolete = false;
_table[minorID].isOrphaned = false;
_table[minorID].anchorID = anchorID;
_table[minorID].client = client;
_table[minorID].media = media;
_table[minorID].name = minorName;
_table[minorID].bdevBlockSize = media->getPreferredBlockSize();
_table[minorID].bdevNode = bdevNode;
_table[minorID].bdevOpen = 0;
_table[minorID].bdevOpenLevel = kIOStorageAccessNone;
_table[minorID].cdevNode = cdevNode;
_table[minorID].cdevOpen = 0;
_table[minorID].cdevOpenLevel = kIOStorageAccessNone;
_table[minorID].client->retain(); _table[minorID].media->retain();
return minorID;
}
void MinorTable::remove(UInt32 minorID)
{
UInt32 anchorID;
assert(minorID < _tableCount);
assert(_table[minorID].isAssigned);
assert(_table[minorID].isOrphaned == false);
assert(_table[minorID].bdevOpen == 0);
assert(_table[minorID].cdevOpen == 0);
anchorID = _table[minorID].anchorID;
devfs_remove(_table[minorID].cdevNode);
devfs_remove(_table[minorID].bdevNode);
IODelete(_table[minorID].name, char, strlen(_table[minorID].name) + 1);
_table[minorID].client->release(); _table[minorID].media->release();
bzero(&_table[minorID], sizeof(MinorSlot));
if ( gIOMediaBSDClientGlobals.getAnchors()->isObsolete(anchorID) )
{
if ( hasReferencesToAnchorID(anchorID, false) == false )
gIOMediaBSDClientGlobals.getAnchors()->remove(anchorID);
}
}
UInt32 MinorTable::update( IOMedia * media,
UInt32 anchorID,
IOMediaBSDClient * client,
char * slicePath )
{
UInt32 minorID;
char * minorName;
UInt32 minorNameSize;
minorNameSize = strlen("disk#");
for (unsigned temp = anchorID; temp >= 10; temp /= 10) minorNameSize++;
minorNameSize += strlen(slicePath);
minorNameSize += 1;
minorName = IONew(char, minorNameSize);
if ( minorName == 0 ) return kInvalidMinorID;
snprintf(minorName, minorNameSize, "disk%d%s", (int) anchorID, slicePath);
assert(strlen(minorName) + 1 == minorNameSize);
for ( minorID = 0; minorID < _tableCount; minorID++ )
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].isObsolete != false &&
_table[minorID].isOrphaned != false &&
_table[minorID].anchorID == anchorID &&
strcmp(_table[minorID].name, minorName) == 0 ) break;
}
IODelete(minorName, char, minorNameSize);
if ( minorID == _tableCount ) return kInvalidMinorID;
_table[minorID].client->release(); _table[minorID].media->release();
_table[minorID].isObsolete = false; _table[minorID].isOrphaned = false;
_table[minorID].client = client;
_table[minorID].media = media;
_table[minorID].client->retain(); _table[minorID].media->retain();
return minorID;
}
UInt32 MinorTable::locate(IOMedia * media)
{
for (UInt32 minorID = 0; minorID < _tableCount; minorID++)
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].isObsolete == false &&
_table[minorID].media == media ) return minorID;
}
return kInvalidMinorID;
}
UInt32 MinorTable::getOpenCountForAnchorID(UInt32 anchorID)
{
UInt32 opens = 0;
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].anchorID == anchorID )
{
opens += _table[minorID].bdevOpen;
opens += _table[minorID].cdevOpen;
}
}
return opens;
}
bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID, bool excludeOrphans)
{
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].anchorID == anchorID )
{
if ( excludeOrphans == false ) return true;
if ( _table[minorID].isObsolete == false ) return true;
if ( _table[minorID].isOrphaned == false ) return true;
}
}
return false;
}
MinorSlot * MinorTable::getMinor(UInt32 minorID)
{
if ( minorID < _tableCount && _table[minorID].isAssigned )
return &_table[minorID];
else
return 0;
}
void MinorTable::obsolete(UInt32 minorID)
{
assert(minorID < _tableCount);
assert(_table[minorID].isAssigned);
_table[minorID].isObsolete = true;
}
bool MinorTable::isObsolete(UInt32 minorID)
{
assert(minorID < _tableCount);
assert(_table[minorID].isAssigned);
return _table[minorID].isObsolete ? true : false;
}
IOMediaBSDClientGlobals::IOMediaBSDClientGlobals()
{
_anchors = new AnchorTable();
_minors = new MinorTable();
_bdevswInstalled = (bdevsw_add(kMajor, &bdevswFunctions) == kMajor);
_cdevswInstalled = (cdevsw_add(kMajor, &cdevswFunctions) == kMajor);
_openLock = IOLockAlloc();
_stateLock = IOLockAlloc();
}
IOMediaBSDClientGlobals::~IOMediaBSDClientGlobals()
{
if ( _openLock ) IOLockFree(_openLock);
if ( _stateLock ) IOLockFree(_stateLock);
if ( _cdevswInstalled ) cdevsw_remove(kMajor, &cdevswFunctions);
if ( _bdevswInstalled ) bdevsw_remove(kMajor, &bdevswFunctions);
if ( _minors ) delete _minors;
if ( _anchors ) delete _anchors;
}
AnchorTable * IOMediaBSDClientGlobals::getAnchors()
{
return _anchors;
}
MinorTable * IOMediaBSDClientGlobals::getMinors()
{
return _minors;
}
MinorSlot * IOMediaBSDClientGlobals::getMinor(UInt32 minorID)
{
return _minors->getMinor(minorID);
}
bool IOMediaBSDClientGlobals::isValid()
{
return ( _anchors ) &&
( _minors ) &&
( _bdevswInstalled ) &&
( _cdevswInstalled ) &&
( _openLock ) &&
( _stateLock );
}
void IOMediaBSDClientGlobals::lockOpen()
{
IOLockLock(_openLock);
}
void IOMediaBSDClientGlobals::unlockOpen()
{
IOLockUnlock(_openLock);
}
void IOMediaBSDClientGlobals::lockState()
{
IOLockLock(_stateLock);
}
void IOMediaBSDClientGlobals::unlockState()
{
IOLockUnlock(_stateLock);
}