IOMediaBSDClient.cpp [plain text]
#include <dev/disk.h> // (DKIOCGETBLOCKSIZE, ...)
#include <mach/vm_types.h> // (mach/vm_region.h, ...)
#include <mach/vm_region.h> // (VM_REGION_BASIC_INFO, ...)
#include <miscfs/devfs/devfs.h> // (devfs_make_node, ...)
#include <sys/buf.h> // (struct buf, ...)
#include <sys/conf.h> // (bdevsw_add, ...)
#include <sys/fcntl.h> // (FWRITE, ...)
#include <sys/ioccom.h> // (IOCGROUP, ...)
#include <sys/stat.h> // (S_ISBLK, ...)
#include <sys/uio.h> // (struct uio, ...)
#include <IOKit/assert.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOMediaBSDClient.h>
#define super IOService
OSDefineMetaClassAndStructors(IOMediaBSDClient, IOService)
static IOMediaBSDClient * gIOMediaBSDClient = 0;
const signed kMajor = 14; const unsigned kMinorsGrowCount = 16; const unsigned kMinorsMaxCount = 1 << 24; const unsigned kAnchorsGrowCount = 2; const unsigned kAnchorsMaxCount = kMinorsMaxCount;
#define kMsgBadWhole "%s: Peer whole media \"%s\" is not allowed.", getName()
#define kMsgNoWhole "%s: No whole media found for media \"%s\".\n", getName()
#define kMsgNoLocation "%s: No location is found for media \"%s\".\n", getName()
#define IOMEDIABSDCLIENT_IOSTAT_SUPPORT // (enable iostat support for bsd)
extern "C"
{
int dkclose(dev_t dev, int flags, int devtype, struct proc *);
int dkioctl(dev_t dev, u_long cmd, caddr_t data, int, struct proc *);
int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int, struct proc *);
int dkopen(dev_t dev, int flags, int devtype, struct proc *);
int dkread(dev_t dev, struct uio * uio, int flags);
int dksize(dev_t dev);
void dkstrategy(struct buf * bp);
int dkwrite(dev_t dev, struct uio * 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_TAPE
};
struct dio { dev_t dev; struct uio * uio; };
typedef void * dkr_t;
typedef enum { DKRTYPE_BUF, DKRTYPE_DIO } dkrtype_t;
int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype);
void dkreadwritecompletion(void *, void *, IOReturn, UInt64);
#define get_kernel_task() kernel_task
#define get_user_task() current_task()
#ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
#include <sys/dkstat.h>
IOBlockStorageDriver * dk_drive[DK_NDRIVE];
#endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
const UInt32 kInvalidAnchorID = (UInt32) (-1);
struct AnchorSlot
{
UInt32 isAssigned:1, isObsolete:1;
IOService * anchor; IONotifier * notifier; };
class AnchorTable
{
protected:
AnchorSlot * _table;
UInt32 _tableCount;
UInt32 _tableGrowCount;
UInt32 _tableMaxCount;
static IOReturn anchorWasNotified( void * target,
void * parameter,
UInt32 messageType,
IOService * provider,
void * messageArgument,
vm_size_t messageArgumentSize );
public:
AnchorTable(UInt32 growCount, UInt32 maxCount);
~AnchorTable();
UInt32 insert(IOService * anchor);
UInt32 locate(IOService * anchor);
void obsolete(UInt32 anchorID);
void remove(UInt32 anchorID);
bool isObsolete(UInt32 anchorID);
};
const UInt32 kInvalidMinorID = (UInt32) (-1);
struct MinorSlot
{
UInt32 isAssigned:1, isEjecting:1, isObsolete:1;
UInt32 anchorID; IOMedia * media; char * name;
UInt64 bdevBlockSize; void * bdevNode; UInt32 bdevOpen:1, bdevWriter:1;
void * cdevNode; UInt32 cdevOpen:1, cdevWriter:1; };
class MinorTable
{
protected:
MinorSlot * _table;
UInt32 _tableCount;
UInt32 _tableGrowCount;
UInt32 _tableMaxCount;
public:
MinorTable(UInt32 growCount, UInt32 maxCount);
~MinorTable();
UInt32 insert(IOMedia * media, UInt32 anchorID, 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);
IOMedia * getWholeMediaAtAnchorID(UInt32 anchorID);
bool hasReferencesToAnchorID(UInt32 anchorID);
};
bool IOMediaBSDClient::init(OSDictionary * properties = 0)
{
if ( super::init(properties) == false ) return false;
_anchors = new AnchorTable(kAnchorsGrowCount, kAnchorsMaxCount);
_bdevswInstalled = false;
_cdevswInstalled = false;
_minors = new MinorTable(kMinorsGrowCount, kMinorsMaxCount);
_notifier = 0;
if ( _anchors == 0 || _minors == 0 ) return false;
return true;
}
void IOMediaBSDClient::free()
{
if ( _notifier ) _notifier->remove();
if ( _cdevswInstalled ) cdevsw_remove(kMajor, &cdevswFunctions);
if ( _bdevswInstalled ) bdevsw_remove(kMajor, &bdevswFunctions);
if ( _minors ) delete _minors;
if ( _anchors ) delete _anchors;
super::free();
}
bool IOMediaBSDClient::start(IOService * provider)
{
assert(gIOMediaBSDClient == 0);
if ( super::start(provider) == false ) return false;
gIOMediaBSDClient = this;
_bdevswInstalled = (bdevsw_add(kMajor, &bdevswFunctions) == kMajor);
_cdevswInstalled = (cdevsw_add(kMajor, &cdevswFunctions) == kMajor);
if ( _bdevswInstalled == false && _cdevswInstalled == false ) return false;
_notifier = addNotification( gIOFirstPublishNotification,
serviceMatching("IOMedia"),
mediaHasArrived,
this,
0,
10 );
if ( _notifier == 0 ) return false;
registerService();
return true;
}
void IOMediaBSDClient::stop(IOService * provider)
{
IOMedia * media = (IOMedia *) provider;
UInt32 minorID = 0;
gIOMediaBSDClient->lockForArbitration();
minorID = _minors->locate(media);
assert(minorID != kInvalidMinorID);
assert(media->isOpen() == false);
if ( _minors->getMinor(minorID)->isEjecting ) {
assert(_minors->isObsolete(minorID) == false);
_minors->obsolete(minorID);
}
else
{
assert(_minors->getMinor(minorID)->bdevOpen == false);
assert(_minors->getMinor(minorID)->cdevOpen == false);
_minors->remove(minorID);
}
gIOMediaBSDClient->unlockForArbitration();
super::stop(media);
}
bool IOMediaBSDClient::mediaHasArrived( void * ,
void * ,
IOService * service )
{
IOMedia * media = OSDynamicCast(IOMedia, service);
bool success = false;
assert(gIOMediaBSDClient);
if ( media && gIOMediaBSDClient->attach(media) )
{
gIOMediaBSDClient->lockForArbitration();
success = gIOMediaBSDClient->createNodes(media);
gIOMediaBSDClient->unlockForArbitration();
if (success == false) gIOMediaBSDClient->detach(media);
}
return true; }
IOMedia * IOMediaBSDClient::getWholeMedia( IOMedia * media,
UInt32 * slicePathSize = 0,
char * slicePath = 0 )
{
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;
UInt32 anchorID;
bool anchorNew = false;
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);
if ( anchorID != kInvalidAnchorID )
{
IOMedia * wholeInTable = _minors->getWholeMediaAtAnchorID(anchorID);
if ( wholeInTable == 0 ) {
if ( _minors->hasReferencesToAnchorID(anchorID) ) {
_anchors->obsolete(anchorID); anchorID = kInvalidAnchorID; } }
else if ( whole != wholeInTable ) {
if ( wholeInTable->isInactive() ) {
_anchors->obsolete(anchorID); anchorID = kInvalidAnchorID; }
else {
if ( whole == media ) IOLog(kMsgBadWhole, whole->getName());
return false;
}
} }
if ( anchorID == kInvalidAnchorID )
{
anchorID = _anchors->insert(anchor); 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->insert(media, anchorID, 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; }
AnchorTable * IOMediaBSDClient::getAnchors()
{
return _anchors;
}
MinorTable * IOMediaBSDClient::getMinors()
{
return _minors;
}
MinorSlot * IOMediaBSDClient::getMinor(UInt32 minorID)
{
return _minors->getMinor(minorID);
}
OSMetaClassDefineReservedUnused(IOMediaBSDClient, 0);
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);
int dkopen(dev_t dev, int flags, int devtype, struct proc *)
{
int error;
IOStorageAccess level;
MinorSlot * minor;
assert(gIOMediaBSDClient);
assert(S_ISBLK(devtype) || S_ISCHR(devtype));
gIOMediaBSDClient->lockForArbitration();
assert(gIOMediaBSDClient->getMinors());
error = 0;
level = (flags & FWRITE) ? kIOStorageAccessReaderWriter
: kIOStorageAccessReader;
minor = gIOMediaBSDClient->getMinor(minor(dev));
if ( minor == 0 ) {
error = ENXIO;
}
else if ( minor->isEjecting ) {
error = EBUSY;
}
else if ( (flags & FWRITE) ) {
if ( minor->bdevWriter || minor->cdevWriter )
level = kIOStorageAccessNone;
}
else {
if ( minor->bdevOpen || minor->cdevOpen )
level = kIOStorageAccessNone;
}
if ( error == 0 && level != kIOStorageAccessNone ) {
if ( minor->media->open(gIOMediaBSDClient, 0, level) == false ) {
error = EBUSY;
}
}
if ( error == 0 ) {
if ( S_ISBLK(devtype) )
{
minor->bdevOpen = true;
if ( (flags & FWRITE) ) minor->bdevWriter = true;
}
else
{
minor->cdevOpen = true;
if ( (flags & FWRITE) ) minor->cdevWriter = true;
}
}
gIOMediaBSDClient->unlockForArbitration();
return error;
}
int dkclose(dev_t dev, int , int devtype, struct proc *)
{
MinorSlot * minor;
bool wasWriter;
assert(S_ISBLK(devtype) || S_ISCHR(devtype));
gIOMediaBSDClient->lockForArbitration();
minor = gIOMediaBSDClient->getMinor(minor(dev));
wasWriter = (minor->bdevWriter || minor->cdevWriter);
if ( S_ISBLK(devtype) ) {
minor->bdevBlockSize = minor->media->getPreferredBlockSize();
minor->bdevOpen = false;
minor->bdevWriter = false;
}
else
{
minor->cdevOpen = false;
minor->cdevWriter = false;
}
if ( minor->isEjecting ) {
minor->isEjecting = false;
assert(minor->bdevOpen == false);
assert(minor->cdevOpen == false);
if ( minor->isObsolete )
gIOMediaBSDClient->getMinors()->remove(minor(dev));
}
else if ( !minor->bdevOpen && !minor->cdevOpen )
{
minor->media->close(gIOMediaBSDClient); }
else if ( !minor->bdevWriter && !minor->cdevWriter && wasWriter )
{
bool s;
s = minor->media->open(gIOMediaBSDClient, 0, kIOStorageAccessReader);
assert(s); }
gIOMediaBSDClient->unlockForArbitration();
return 0;
}
int dkread(dev_t dev, struct uio * uio, int )
{
struct dio dio = { dev, uio };
return dkreadwrite(&dio, DKRTYPE_DIO);
}
int dkwrite(dev_t dev, struct uio * uio, int )
{
struct dio dio = { dev, uio };
return dkreadwrite(&dio, DKRTYPE_DIO);
}
void dkstrategy(struct buf * bp)
{
dkreadwrite(bp, DKRTYPE_BUF);
}
int dkioctl(dev_t dev, u_long cmd, caddr_t data, int, struct proc *)
{
int error = 0;
MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
if ( minor->isEjecting ) return EBADF;
switch ( cmd )
{
case DKIOCGETBLOCKSIZE: {
*(int *)data = (int) minor->media->getPreferredBlockSize();
} break;
case DKIOCGETBLOCKCOUNT: {
if ( minor->media->getPreferredBlockSize() )
*(int *)data = (int) ( minor->media->getSize() /
minor->media->getPreferredBlockSize() );
else
*(int *)data = 0;
} break;
case DKIOCISFORMATTED: {
*(int *)data = (int) minor->media->isFormatted();
} break;
case DKIOCISWRITABLE: {
*(int *)data = (int) minor->media->isWritable();
} break;
case DKIOCGETLOCATION: {
int l = sizeof(((struct drive_location *)data)->location);
char * p = ((struct drive_location *)data)->location;
if ( minor->media->getPath(p, &l, gIODTPlane) && strchr(p, ':') )
strcpy(p, strchr(p, ':') + 1); else
error = EINVAL;
} break;
case DKIOCEJECT: {
IOBlockStorageDriver * driver;
MinorTable * minors;
driver = OSDynamicCast( IOBlockStorageDriver,
minor->media->getProvider() );
minors = gIOMediaBSDClient->getMinors();
if ( driver == 0 ) { error = ENOTTY; break; }
gIOMediaBSDClient->lockForArbitration();
if ( minors->getOpenCountForAnchorID(minor->anchorID) > 1 )
{
error = EBUSY;
gIOMediaBSDClient->unlockForArbitration();
}
else
{
IOReturn status;
minor->isEjecting = true;
gIOMediaBSDClient->unlockForArbitration();
minor->media->close(gIOMediaBSDClient);
if (driver->open(gIOMediaBSDClient, 0, kIOStorageAccessReader))
{
status = driver->ejectMedia();
driver->close(gIOMediaBSDClient);
}
else
{
status = kIOReturnBusy;
}
error = gIOMediaBSDClient->errnoFromReturn(status);
}
} break;
default:
{
IOLog( "%s: ioctl(%s\'%c\',%d,%d) is unsupported.\n",
minor->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; }
int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int f, struct proc * proc)
{
int error = 0;
MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
if ( minor->isEjecting ) return EBADF;
switch ( cmd )
{
case DKIOCGETBLOCKSIZE: {
*(int *)data = (int) minor->bdevBlockSize;
} break;
case DKIOCSETBLOCKSIZE: {
if ( *(int *)data > 0 )
minor->bdevBlockSize = (UInt64) (*(int *)data);
else
error = EINVAL;
} break;
case DKIOCGETBLOCKCOUNT: {
if ( minor->bdevBlockSize )
*(int *)data = (int) ( minor->media->getSize() /
minor->bdevBlockSize );
else
*(int *)data = 0;
} break;
default:
{
error = dkioctl(dev, cmd, data, f, proc);
} break;
}
return error; }
int dksize(dev_t dev)
{
MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
if ( minor->isEjecting ) return 0;
return (int) minor->bdevBlockSize; }
inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? ((struct buf *)dkr)->b_dev
: ((struct dio *)dkr)->dev;
}
inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? ((struct buf *)dkr)->b_bcount
: ((struct dio *)dkr)->uio->uio_resid;
}
inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
{
if (dkrtype == DKRTYPE_BUF)
{
struct buf * bp = (struct buf *)dkr;
MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(bp->b_dev));
return bp->b_blkno * minor->bdevBlockSize;
}
return ((struct dio *)dkr)->uio->uio_offset;
}
inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
{
return (dkrtype == DKRTYPE_BUF)
? ((((struct buf *)dkr)->b_flags & B_READ) == B_READ)
: ((((struct dio *)dkr)->uio->uio_rw) == 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)
((struct buf *)dkr)->b_resid = ((struct buf *)dkr)->b_bcount - bcount;
else
((struct dio *)dkr)->uio->uio_resid -= bcount;
}
inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
{
if (dkrtype == DKRTYPE_BUF)
{
struct buf * bp = (struct buf *)dkr;
bp->b_error = gIOMediaBSDClient->errnoFromReturn(status); bp->b_flags |= (status != kIOReturnSuccess) ? B_ERROR : 0; biodone(bp); }
}
inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
{
if (dkrtype == DKRTYPE_BUF)
{
struct buf * bp = (struct buf *)dkr;
if ( (bp->b_flags & B_VECTORLIST) )
{
assert(sizeof(IOPhysicalRange ) == sizeof(iovec ));
assert(sizeof(IOPhysicalRange::address) == sizeof(iovec::iov_base));
assert(sizeof(IOPhysicalRange::length ) == sizeof(iovec::iov_len ));
return IOMemoryDescriptor::withPhysicalRanges( (IOPhysicalRange *) bp->b_vectorlist,
(UInt32) bp->b_vectorcount,
(bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
true );
}
return IOMemoryDescriptor::withAddress( (vm_address_t) bp->b_data,
(vm_size_t) bp->b_bcount,
(bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
(bp->b_flags & B_PHYS) ? get_user_task() : get_kernel_task() );
}
else
{
struct uio * uio = ((struct dio *)dkr)->uio;
assert(sizeof(IOVirtualRange ) == sizeof(iovec ));
assert(sizeof(IOVirtualRange::address) == sizeof(iovec::iov_base));
assert(sizeof(IOVirtualRange::length ) == sizeof(iovec::iov_len ));
return IOMemoryDescriptor::withRanges( (IOVirtualRange *) uio->uio_iov,
(UInt32) uio->uio_iovcnt,
(uio->uio_rw == UIO_READ ) ? kIODirectionIn : kIODirectionOut,
(uio->uio_segflg != UIO_SYSSPACE) ? get_user_task() : get_kernel_task(),
true );
}
}
int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype)
{
IOMemoryDescriptor * buffer;
register UInt64 byteCount;
register UInt64 byteStart;
UInt64 mediaSize;
MinorSlot * minor;
IOReturn status;
minor = gIOMediaBSDClient->getMinor(minor(DKR_GET_DEV(dkr, dkrtype)));
if ( minor->isEjecting ) {
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 = IOMemoryDescriptor::withSubRange(
originalBuffer,
0,
mediaSize - byteStart,
originalBuffer->getDirection() );
originalBuffer->release();
if ( buffer == 0 ) {
status = kIOReturnNoMemory;
goto dkreadwriteErr;
}
}
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( gIOMediaBSDClient,
byteStart,
buffer,
completion ); }
else {
minor->media->write( gIOMediaBSDClient,
byteStart,
buffer,
completion ); }
status = kIOReturnSuccess;
}
else {
if ( DKR_IS_READ(dkr, dkrtype) ) {
status = minor->media->IOStorage::read(
gIOMediaBSDClient,
byteStart,
buffer,
&byteCount ); }
else {
status = minor->media->IOStorage::write(
gIOMediaBSDClient,
byteStart,
buffer,
&byteCount ); }
dkreadwritecompletion(dkr, (void *)dkrtype, status, byteCount);
}
buffer->release();
return gIOMediaBSDClient->errnoFromReturn(status);
dkreadwriteErr:
dkreadwritecompletion(dkr, (void *)dkrtype, status, 0);
return gIOMediaBSDClient->errnoFromReturn(status); }
void dkreadwritecompletion( void * target,
void * parameter,
IOReturn status,
UInt64 actualByteCount )
{
dkr_t dkr = (dkr_t) target;
dkrtype_t dkrtype = (dkrtype_t) (int) parameter;
dev_t dev = DKR_GET_DEV(dkr, dkrtype);
#ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
UInt32 anchorID = gIOMediaBSDClient->getMinor(minor(dev))->anchorID;
if ( anchorID < DK_NDRIVE )
{
IOBlockStorageDriver * d = dk_drive[anchorID];
if ( d )
{
dk_xfer[anchorID] = (long)
( d->getStatistic(IOBlockStorageDriver::kStatisticsReads ) +
d->getStatistic(IOBlockStorageDriver::kStatisticsWrites) );
dk_wds[anchorID] = (long) (8 *
( d->getStatistic(IOBlockStorageDriver::kStatisticsBytesRead ) +
d->getStatistic(IOBlockStorageDriver::kStatisticsBytesWritten)) );
}
}
#endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
if ( status != kIOReturnSuccess ) {
IOLog( "%s: %s.\n",
gIOMediaBSDClient->getMinor(minor(dev))->name,
gIOMediaBSDClient->stringFromReturn(status) );
}
DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount); DKR_RUN_COMPLETION(dkr, dkrtype, status); }
AnchorTable::AnchorTable(UInt32 growCount, UInt32 maxCount)
{
_table = 0;
_tableCount = 0;
_tableGrowCount = growCount;
_tableMaxCount = maxCount;
}
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)
{
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 >= _tableMaxCount ) return kInvalidAnchorID;
newTableCount = min(_tableGrowCount + _tableCount, _tableMaxCount);
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].notifier = notifier;
_table[anchorID].anchor->retain();
#ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
if ( anchorID < DK_NDRIVE )
{
dk_drive[anchorID] = OSDynamicCast(IOBlockStorageDriver, anchor);
if ( anchorID + 1 > (UInt32) dk_ndrive ) dk_ndrive = anchorID + 1;
}
#endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
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));
#ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
if ( anchorID < DK_NDRIVE )
{
dk_drive[anchorID] = 0;
for (dk_ndrive = DK_NDRIVE; dk_ndrive; dk_ndrive--)
{
if ( dk_drive[dk_ndrive - 1] ) break;
}
}
#endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
}
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;
}
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 )
{
UInt32 anchorID;
assert(gIOMediaBSDClient);
if ( messageType != kIOMessageServiceIsTerminated )
return kIOReturnSuccess;
gIOMediaBSDClient->lockForArbitration();
anchorID = gIOMediaBSDClient->getAnchors()->locate(anchor);
if ( anchorID != kInvalidAnchorID )
{
if ( gIOMediaBSDClient->getMinors()->hasReferencesToAnchorID(anchorID) )
gIOMediaBSDClient->getAnchors()->obsolete(anchorID);
else
gIOMediaBSDClient->getAnchors()->remove(anchorID);
}
gIOMediaBSDClient->unlockForArbitration();
return kIOReturnSuccess;
}
MinorTable::MinorTable(UInt32 growCount, UInt32 maxCount)
{
_table = 0;
_tableCount = 0;
_tableGrowCount = growCount;
_tableMaxCount = maxCount;
}
MinorTable::~MinorTable()
{
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
if ( _table[minorID].isAssigned ) remove(minorID);
if ( _table ) IODelete(_table, MinorSlot, _tableCount);
}
UInt32 MinorTable::insert(IOMedia * media, UInt32 anchorID, char * slicePath)
{
void * bdevNode;
void * cdevNode;
UInt32 minorID;
char * minorName;
UInt32 minorNameSize;
for ( minorID = 0; minorID < _tableCount; minorID++ )
if ( _table[minorID].isAssigned == false ) break;
if ( minorID == _tableCount )
{
MinorSlot * newTable;
UInt32 newTableCount;
if ( _tableCount >= _tableMaxCount) return kInvalidMinorID;
newTableCount = min(_tableGrowCount + _tableCount, _tableMaxCount);
newTable = IONew(MinorSlot, newTableCount);
if ( newTable == 0 ) return kInvalidMinorID;
bzero(newTable, newTableCount * sizeof(MinorSlot));
if ( _table )
{
bcopy(_table, newTable, _tableCount * sizeof(MinorSlot));
IODelete(_table, MinorSlot, _tableCount);
}
minorID = _tableCount;
_table = newTable;
_tableCount = newTableCount;
}
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,
media->isWritable()?0640:0440,
"disk%d%s",
anchorID,
slicePath );
cdevNode = devfs_make_node( makedev(kMajor, minorID),
DEVFS_CHAR,
UID_ROOT,
GID_OPERATOR,
media->isWritable()?0640:0440,
"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;
}
sprintf(minorName, "disk%ld%s", anchorID, slicePath);
assert(strlen(minorName) + 1 == minorNameSize);
bzero(&_table[minorID], sizeof(MinorSlot));
_table[minorID].isAssigned = true; _table[minorID].isEjecting = false;
_table[minorID].isObsolete = false;
_table[minorID].anchorID = anchorID;
_table[minorID].media = media;
_table[minorID].name = minorName;
_table[minorID].bdevBlockSize = media->getPreferredBlockSize();
_table[minorID].bdevNode = bdevNode;
_table[minorID].bdevOpen = false;
_table[minorID].cdevNode = cdevNode;
_table[minorID].cdevOpen = false;
_table[minorID].media->retain();
return minorID;
}
void MinorTable::remove(UInt32 minorID)
{
UInt32 anchorID;
assert(minorID < _tableCount);
assert(_table[minorID].isAssigned);
assert(_table[minorID].isEjecting == false);
assert(_table[minorID].bdevOpen == false);
assert(_table[minorID].cdevOpen == false);
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].media->release();
bzero(&_table[minorID], sizeof(MinorSlot));
assert(gIOMediaBSDClient);
if ( gIOMediaBSDClient->getAnchors()->isObsolete(anchorID) )
{
if ( hasReferencesToAnchorID(anchorID) == false )
gIOMediaBSDClient->getAnchors()->remove(anchorID);
}
}
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) ? 1 : 0;
opens += (_table[minorID].cdevOpen) ? 1 : 0;
}
}
return opens;
}
IOMedia * MinorTable::getWholeMediaAtAnchorID(UInt32 anchorID)
{
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].anchorID == anchorID &&
_table[minorID].media->isWhole() ) return _table[minorID].media;
}
return 0;
}
bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID)
{
for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
{
if ( _table[minorID].isAssigned != false &&
_table[minorID].anchorID == anchorID ) 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;
}