Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
MoreIsBetterBits/MoreDisks.c
/* |
File: MoreDisks.c |
Contains: General disk driver utility routines. |
Written by: Quinn |
Copyright: Copyright © 1999 by Apple Computer, Inc., all rights reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
<2> 7/5/99 Quinn Added MoreIsDriveCDROM. Fixed bug in MoreGetDriveRefNum where |
it was calling MoreUTFindDrive rather than MoreUTFindDriveQ, and |
hence failing on disks controlled by foreign file systems. |
<1> 16/3/99 Quinn First checked in. |
*/ |
///////////////////////////////////////////////////////////////// |
// MoreIsBetter Setup |
#include "MoreSetup.h" |
// Mac OS Interfaces |
#include <Devices.h> |
#include <DriverGestalt.h> |
#include <Gestalt.h> |
#include <FSM.h> |
#include <Disks.h> |
#include <StringCompare.h> |
// MIB Prototypes |
#include "TradDriverLoaderLib.h" |
#include "MoreInterfaceLib.h" |
#include "MoreMemory.h" |
// Our Prototypes |
#include "MoreDisks.h" |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Basic Disk Driver Utilities ----- |
extern pascal DriveFlagsPtr MoreGetDriveFlags(DrvQElPtr drvQEl) |
// See comment in interface part. |
{ |
MoreAssertQ(drvQEl != nil); |
return ((DriveFlagsPtr) drvQEl) - 1; |
} |
extern pascal OSErr MoreUTFindDriveQ(SInt16 drive, DrvQElPtr *foundDrvQEl) |
// See comment in interface part. |
{ |
OSErr err; |
UInt32 fsmVers; |
MoreAssertQ(drive > 0); |
MoreAssertQ(foundDrvQEl != nil); |
// Check to see whether we have a useful version of FSM. Versions of FSM |
// prior to 1.2 do not support the documented FSM API, so we just treat |
// them as if FSM wasn't installed. |
if ((Gestalt(gestaltFSMVersion, (SInt32 *) &fsmVers) == noErr) && (fsmVers >= 0x0120)) { |
// We have FSM, let's call its version of UTFindDrive, |
// and handle the weirdo error we get for non-HFS disks. |
err = MoreUTFindDrive(drive, foundDrvQEl); |
if (err == extFSErr) { |
err = noErr; |
} |
} else { |
DrvQElPtr thisDrv; |
// No FSM, let's go poking around in low memory )-: |
*foundDrvQEl = nil; |
thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead; |
while (thisDrv != nil && *foundDrvQEl == nil) { |
if (thisDrv->dQDrive == drive) { |
*foundDrvQEl = thisDrv; |
} else { |
thisDrv = (DrvQElPtr) thisDrv->qLink; |
} |
} |
if (*foundDrvQEl == nil) { |
err = nsDrvErr; |
} else { |
err = noErr; |
} |
} |
return err; |
} |
extern pascal DrvQElPtr MoreGetIndDrive(SInt16 index) |
// See comment in interface part. |
{ |
DrvQElPtr thisDrv; |
SInt16 thisDrvIndex; |
MoreAssertQ(index > 0); |
thisDrvIndex = 1; |
thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead; |
while (thisDrv != nil && thisDrvIndex != index) { |
thisDrvIndex += 1; |
thisDrv = (DrvQElPtr) thisDrv->qLink; |
} |
return thisDrv; |
} |
extern pascal SInt16 MoreFindFreeDriveNumber(SInt16 firstDrive) |
// See comment in interface part. |
{ |
SInt16 candidate; |
DrvQElPtr junkDrvQElPtr; |
MoreAssertQ(firstDrive >= 5); |
candidate = firstDrive; |
while ( MoreUTFindDriveQ(candidate, &junkDrvQElPtr) == noErr ) { |
candidate += 1; |
} |
// This post condition checks that we didn't wrap |
// around to a negative drive number. |
MoreAssertQ(candidate >= 5); |
return candidate; |
} |
extern pascal OSErr MoreRemoveDrive(DrvQElPtr drvQEl) |
// See comment in interface part. |
{ |
OSStatus err; |
if ( MoreVolumeMountedOnDrive(drvQEl->dQDrive, false) == 0 ) { |
err = Dequeue( (QElemPtr) drvQEl, GetDrvQHdr()); |
} else { |
err = volOnLinErr; |
} |
return err; |
} |
extern pascal DriverRefNum MoreGetDriveRefNum(SInt16 drive) |
// See comment in interface part. |
{ |
DrvQElPtr foundDrvQEl; |
MoreAssertQ(drive > 0); |
if ( MoreUTFindDriveQ(drive, &foundDrvQEl) == noErr) { |
return foundDrvQEl->dQRefNum; |
} else { |
return 0; |
} |
} |
static pascal Boolean MoreDriveSupportsDriverGestaltInternal(DriverRefNum refNum) |
// An internal version of MoreDriveSupportsDriverGestalt that allows |
// you to pass in the refNum and the drive number. You can pass |
// in 0 for either refNum or drive (but not both) and the routine |
// will do the appropriate mapping. |
{ |
OSErr junk; |
DriverFlags driverFlags; |
junk = TradGetDriverInformation(refNum, nil, &driverFlags, nil, nil); |
MoreAssertQ(junk == noErr); |
return TradDriverGestaltIsOn(driverFlags); |
} |
extern pascal Boolean MoreDriveSupportsDriverGestalt(SInt16 drive) |
// See comment in interface part. |
{ |
return MoreDriveSupportsDriverGestaltInternal(MoreGetDriveRefNum(drive)); |
} |
static pascal Boolean MoreDriveSupportFileExchangeInternal(DriverRefNum refNum, SInt16 drive) |
// An internal version of MoreDriveSupportFileExchange that allows |
// you to pass in the refNum and the drive number. You can pass |
// in 0 for either refNum or drive (but not both) and the routine |
// will do the appropriate mapping. |
{ |
DriverGestaltParam pb; |
Boolean result; |
MoreAssertQ( (refNum < 0 && drive >= 0 && (drive == 0 || MoreGetDriveRefNum(drive) == refNum)) || |
(refNum == 0 && drive > 0) ); |
if (refNum == 0) { |
refNum = MoreGetDriveRefNum(drive); |
} |
result = false; |
if ( MoreDriveSupportsDriverGestaltInternal(refNum) ) { |
pb.ioVRefNum = drive; |
pb.ioCRefNum = refNum; |
pb.csCode = kDriverGestaltCode; |
pb.driverGestaltSelector = kdgAPI; |
if ( PBStatusSync((ParmBlkPtr) &pb) == noErr |
&& GetDriverGestaltAPIResponse(&pb)->partitionCmds & 0x01 ) { |
result = true; |
} |
} |
return result; |
} |
extern pascal Boolean MoreDriveSupportFileExchange(SInt16 drive) |
// See comment in interface part. |
{ |
MoreAssertQ(drive > 0); |
return MoreDriveSupportFileExchangeInternal(0, drive); |
} |
// This is the number of format list entries we allocate when issuing |
// the return format list status call. There's no way we can calculate |
// the "correct" number, but this should be more than enough. |
enum { |
kFormatListEntryCount = 16 |
}; |
extern pascal OSErr MoreGetDriveSize(SInt16 drive, UInt32 *sizeInBlocks) |
// See comment in interface part. |
{ |
OSErr err; |
DrvQElPtr drvQEl; |
CntrlParam pb; |
FormatListRec formatList[kFormatListEntryCount]; |
SInt16 formatIndex; |
Boolean foundFormat; |
Str255 driverName; |
DrvSts status; |
MoreAssertQ(drive > 0); |
MoreAssertQ(sizeInBlocks != nil); |
// Start by finding the drive queue element for |
// the drive, and by making sure that there's a disk |
// in the drive. |
err = MoreUTFindDriveQ(drive, &drvQEl); |
if (err == noErr) { |
if ( MoreGetDriveFlags(drvQEl)->diskInPlace <= 0 ) { |
err = offLinErr; |
} |
} |
// Wow, this is harder than it should be, all because of the |
// silly ".Sony" driver. The basic problem is that |
// the ".Sony" driver doesn't store the disk size in the |
// drive queue element like every other disk drive on the |
// planet. The solution is a three step process as described |
// in the comments below. |
if (err == noErr) { |
// Step 1. If the driver supports the kReturnFormatList status call, |
// use it to get a list of formats for the drive and then |
// return the format marked as current. |
pb.ioNamePtr = nil; |
pb.ioVRefNum = drvQEl->dQDrive; |
pb.ioCRefNum = drvQEl->dQRefNum; |
pb.csCode = kReturnFormatList; |
pb.csParam[0] = kFormatListEntryCount; |
*((FormatListRec **) &pb.csParam[1]) = formatList; |
err = PBStatusSync( (ParmBlkPtr) &pb); |
if (err == noErr) { |
foundFormat = false; |
for (formatIndex = 0; formatIndex < pb.csParam[0]; formatIndex++) { |
if ((formatList[formatIndex].formatFlags & diCIFmtFlagsCurrentMask) != 0) { |
*sizeInBlocks = formatList[formatIndex].volSize; |
foundFormat = true; |
} |
} |
if ( ! foundFormat ) { |
// Hmmm, this isn't good. The disk driver returned a format |
// list but none of the formats were marked as "current". |
// We handle this correctly but, in debug builds, we'll also |
// drop into MacsBug, just to let you know this is happening. |
MoreAssertQ(false); |
err = paramErr; |
} |
} else { |
// ¥¥¥ The logic here is slightly screwed up. The problem is that |
// I can't tell whether the kReturnFormatList call failed because |
// the driver just doesn't support it, or because the driver failed |
// to get the information for some other reason. If that driver |
// happens to be the ".Sony" driver, I'm going to take a wrong step |
// next. |
// |
// For example, say that there's a 1.4MB disk in the floppy drive |
// and I call kReturnFormatList and it fails with an error because |
// of a cosmic ray. I then test the driver name, find that it's |
// ".Sony", call DriveStatus, and then return noErr with a size |
// of either 400KB or 800KB. Not good. |
// |
// You might think this is an unlikely occurence, but it's exactly |
// what happens when there's no disk in the floppy drive. I've |
// special-cased that away above, but the general problem still stands. |
// |
// What are the alternatives? I could special case the error |
// result from kReturnFormatList and only run this code if I get |
// statusErr. But can you guarantee that all ".Sony" drives |
// return statusErr for an unrecognised status code? I thought not. |
// Beyond that, I can't think of any options. So this code |
// stands. It's probably never going to bite anyone, but it's |
// worth noting here, just in case. Besides, this is what |
// the equivalent routine in MoreFiles does (-: |
// |
// -- Quinn, 3 Mar 1999 |
// Step 2. If that doesn't work, then look at the driver. If it's |
// the ".Sony" driver (and this will be a really old ".Sony" driver |
// because new ones support kReturnFormatList), special case |
// the possible media types. |
err = TradGetDriverInformation(drvQEl->dQRefNum, nil, nil, driverName, nil); |
if (err == noErr) { |
if ( EqualString(driverName, "\p.Sony", false, true) ) { |
err = DriveStatus(drvQEl->dQDrive, &status); |
if (err == noErr) { |
if ( status.twoSideFmt == 0 ) { |
*sizeInBlocks = 400 * 2; |
} else { |
*sizeInBlocks = 800 * 2; |
} |
} |
} else { |
// Step 3. If it's not the ".Sony" driver, get the size out of the |
// drive queue element. |
if (drvQEl->qType == 0) { |
// Old style drive, with just 16 bits of size information |
// in dQDrvSz. |
*sizeInBlocks = drvQEl->dQDrvSz; |
} else { |
// New style drive, with 32 bits of size information spread |
// between dQDrvSz and dQDrvSz2. |
*sizeInBlocks = (((UInt32 ) drvQEl->dQDrvSz2) * 65536) + (UInt32 ) drvQEl->dQDrvSz; |
} |
} |
} |
} |
} |
return err; |
} |
extern pascal SInt16 MoreVolumeMountedOnDrive(SInt16 drive, Boolean ejectedIsMounted) |
// See comment in interface part. |
{ |
SInt16 result; |
VCBPtr thisVCB; |
MoreAssertQ(drive > 0); |
// Get the VCB queue (in low memory) and walk it. |
// We can't use UTLocateNextVCB because it will only |
// iterate volumes by name, not return a complete list. |
result = 0; |
thisVCB = (VCBPtr) GetVCBQHdr()->qHead; |
while (thisVCB != nil && result == 0) { |
if (thisVCB->vcbDrvNum == drive || |
(ejectedIsMounted && |
thisVCB->vcbDrvNum == 0 && |
thisVCB->vcbDRefNum == drive |
) |
) { |
MoreAssertQ(thisVCB->vcbDrvNum == 0 || thisVCB->vcbDRefNum == MoreGetDriveRefNum(drive)); |
result = thisVCB->vcbVRefNum; |
} else { |
thisVCB = (VCBPtr) thisVCB->qLink; |
} |
} |
return result; |
} |
extern pascal SInt16 MoreFirstDriveWithoutVolume(DriverRefNum refNum) |
// See comment in interface part. |
{ |
Boolean found; |
DrvQElPtr thisDrv; |
found = false; |
thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead; |
while (thisDrv != nil && ! found) { |
if (thisDrv->dQRefNum == refNum && MoreVolumeMountedOnDrive(thisDrv->dQDrive, false) == 0) { |
found = true; |
} else { |
thisDrv = (DrvQElPtr) thisDrv->qLink; |
} |
} |
if (found) { |
return thisDrv->dQDrive; |
} else { |
return 0; |
} |
} |
extern pascal void MoreIsDriveCDROM(SInt16 drive, MoreDisksCDROMResponse *response) |
// See comment in interface part. |
{ |
DriverRefNum refNum; |
DriverFlags drvrFlags; |
Str255 drvrName; |
DriverGestaltParam pb; |
MoreAssertQ(drive > 0); |
MoreAssertQ(response != nil); |
*response = kMoreDriveUnableToDetermineCDROM; |
refNum = MoreGetDriveRefNum(drive); |
if (refNum != 0) { |
if ( TradGetDriverInformation(refNum, nil, &drvrFlags, drvrName, nil) == noErr ) { |
// Step 1 -- if the driver supports driver gestalt, we |
// exclusively rely on its response for the kdgDeviceType |
// selector. |
if ( TradDriverGestaltIsOn(drvrFlags) ) { |
pb.ioVRefNum = drive; |
pb.ioCRefNum = refNum; |
pb.csCode = kDriverGestaltCode; |
pb.driverGestaltSelector = kdgDeviceType; |
if ( PBStatusSync((ParmBlkPtr) &pb) == noErr ) { |
if (GetDriverGestaltDevTResponse(&pb)->deviceType == kdgCDType) { |
*response = kMoreDriveIsCDROM; |
} else { |
*response = kMoreDriveIsNotCDROM; |
} |
} |
} |
// Step 2 -- if the above didn't work, we only say it's a CD-ROM |
// if the driver is ".AppleCD". |
if (*response == kMoreDriveUnableToDetermineCDROM) { |
if ( EqualString(drvrName, "\p.AppleCD", false, true) ) { |
*response = kMoreDriveIsCDROM; |
} else if ( EqualString(drvrName, "\p.AFPTranslator", false, true) ) { |
// ".AFPTranslator" does not respond to Driver Gestalt, |
// which is pretty lame IMHO. [Radar ID ] Regardless, |
// we know it's not a CD-ROM. |
*response = kMoreDriveIsNotCDROM; |
} else if ( EqualString(drvrName, "\p.Sony", false, true) ) { |
*response = kMoreDriveIsNotCDROM; |
} |
} |
} |
} |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- File Exchange Control Call Interface ----- |
extern pascal OSErr MoreCreateNewDriveQueueElement(SInt16 driveToClone, |
UInt32 firstBlock, UInt32 sizeInBlocks, |
SInt16 *newDrive) |
// See comment in interface part. |
{ |
OSErr err; |
OSErr junk; |
CntrlParam pb; |
DrvQElPtr drvQEl; |
MoreAssertQ(driveToClone > 0); |
MoreAssertQ(newDrive != nil); |
// First check that the driver supports the File Exchange |
// control call interface. |
err = noErr; |
if ( ! MoreDriveSupportFileExchange(driveToClone) ) { |
err = controlErr; |
} |
// Find the drive queue element associated with |
// driveToClone. This is an input parameter to |
// kGetADrive. |
if (err == noErr) { |
err = MoreUTFindDriveQ(driveToClone, &drvQEl); |
} |
// Make the kGetADrive call to the driver. Because |
// we pass a pointer to memory outside of the parameter |
// block (drvQEl) and the driver might be a paging device, |
// we must hold drvQEl (and make sure to unhold it later!). |
if (err == noErr) { |
err = SafeHoldMemory(&drvQEl, sizeof(drvQEl)); |
if (err == noErr) { |
pb.ioVRefNum = driveToClone; |
pb.ioCRefNum = MoreGetDriveRefNum(driveToClone); |
pb.csCode = kGetADrive; |
*((DrvQElPtr **) &pb.csParam[0]) = &drvQEl; |
err = PBControlSync((ParmBlkPtr) &pb); |
if (err == noErr) { |
*newDrive = drvQEl->dQDrive; |
} |
junk = SafeUnholdMemory(&drvQEl, sizeof(drvQEl)); |
MoreAssertQ(junk == noErr); |
} |
} |
// Now re-target the new drive to the partition on the |
// disk specified by firstBlock and sizeInBlocks. We do |
// this in the create call because some disk drivers |
// don't always inherit the partition information from |
// the drive that was cloned. |
if (err == noErr) { |
err = MoreSetDrivePartition(*newDrive, firstBlock, sizeInBlocks); |
} |
return err; |
} |
extern pascal OSErr MoreSetDrivePartition(SInt16 drive, UInt32 firstBlock, UInt32 sizeInBlocks) |
// See comment in interface part. |
{ |
OSErr err; |
CntrlParam pb; |
DrvQElPtr drvQEl; |
MoreAssertQ(drive > 0); |
// First check that the driver supports the File Exchange |
// control call interface. |
err = noErr; |
if ( ! MoreDriveSupportFileExchange(drive) ) { |
err = controlErr; |
} |
// Find the drive queue element associated with |
// drive. This is an input parameter to |
// kRegisterPartition. |
if (err == noErr) { |
err = MoreUTFindDriveQ(drive, &drvQEl); |
} |
// Make the kRegisterPartition control call. We |
// don't need to hold any memory because all the |
// parameters to this control call are entirely |
// contained within the parameter block. |
if (err == noErr) { |
pb.ioVRefNum = drive; |
pb.ioCRefNum = MoreGetDriveRefNum(drive); |
pb.csCode = kRegisterPartition; |
*((DrvQElPtr *) &pb.csParam[0]) = drvQEl; |
*((UInt32 *) &pb.csParam[2]) = firstBlock; |
*((UInt32 *) &pb.csParam[4]) = sizeInBlocks; |
err = PBControlSync((ParmBlkPtr) &pb); |
} |
// In the debug build, check that our changes stuck. |
#if MORE_DEBUG |
if (err == noErr) { |
OSErr debugErr; |
UInt32 trueFirstBlock; |
UInt32 trueSizeInBlocks; |
debugErr = MoreGetDrivePartition(drive, &trueFirstBlock, &trueSizeInBlocks); |
MoreAssertQ(debugErr == noErr && trueSizeInBlocks == sizeInBlocks && trueFirstBlock == firstBlock); |
} |
#endif |
return err; |
} |
extern pascal OSErr MoreGetDrivePartition(SInt16 drive, UInt32 *firstBlock, UInt32 *sizeInBlocks) |
// See comment in interface part. |
{ |
OSErr err; |
partInfoRec partInfo; |
MoreAssertQ(drive > 0); |
MoreAssertQ(firstBlock != nil); |
MoreAssertQ(sizeInBlocks != nil); |
err = MoreGetPartitionInfo(drive, &partInfo); |
if (err == noErr) { |
*firstBlock = partInfo.physPartitionLoc; |
err = MoreGetDriveSize(drive, sizeInBlocks); |
} |
return err; |
} |
extern pascal OSErr MoreGetPartitionInfo(SInt16 drive, partInfoRec *partInfo) |
// See comment in interface part. |
{ |
OSErr err; |
OSErr junk; |
CntrlParam pb; |
MoreAssertQ(drive > 0); |
MoreAssertQ(partInfo != nil); |
// First check that the driver supports the File Exchange |
// control call interface. |
err = noErr; |
if ( ! MoreDriveSupportFileExchange(drive) ) { |
err = controlErr; |
} |
// Make the kGetADrive call to the driver. Because |
// we pass a pointer to memory outside of the parameter |
// block (partInfo) and the driver might be a paging device, |
// we must hold partInfo (and make sure to unhold it later!). |
if (err == noErr) { |
err = SafeHoldMemory(partInfo, sizeof(*partInfo)); |
if (err == noErr) { |
pb.ioVRefNum = drive; |
pb.ioCRefNum = MoreGetDriveRefNum(drive); |
pb.csCode = kGetPartInfo; |
*((partInfoRec **) &pb.csParam[0]) = partInfo; |
err = PBStatusSync((ParmBlkPtr) &pb); |
junk = SafeUnholdMemory(partInfo, sizeof(*partInfo)); |
MoreAssertQ(junk == noErr); |
} |
} |
return err; |
} |
#if 0 |
// ¥¥¥ |
// This code temporarily disable while I figure out what's going on. |
// -- Quinn, 3 Mar 1999 |
extern pascal OSErr MoreGetPartitionVolume(DriverRefNum refNum, const partInfoRec *partInfo, SInt16 *vRefNum) |
// See comment in interface part. |
{ |
OSErr err; |
OSErr junk; |
CntrlParam pb; |
MoreAssertQ(refNum < 0); |
MoreAssertQ(partInfo != nil); |
MoreAssertQ(vRefNum != nil); |
// First check that the driver supports the File Exchange |
// control call interface. |
err = noErr; |
if ( ! MoreDriveSupportFileExchangeInternal(refNum, 0) ) { |
err = controlErr; |
} |
// Make the kGetPartitionStatus call to the driver. Because |
// we pass a pointer to memory outside of the parameter |
// block (partInfo and vRefNum) and the driver might be a paging device, |
// we must hold that memory (and make sure to unhold it later!). |
if (err == noErr) { |
err = SafeHoldMemory( (partInfoRec *) partInfo, sizeof(*partInfo)); |
if (err == noErr) { |
err = SafeHoldMemory(vRefNum, sizeof(*vRefNum)); |
if (err == noErr) { |
pb.ioVRefNum = 0; |
pb.ioCRefNum = refNum; |
pb.csCode = kGetPartitionStatus; |
*((partInfoRec **) &pb.csParam[0]) = (partInfoRec *) partInfo; |
*((SInt16 **) &pb.csParam[2]) = vRefNum; |
err = PBStatusSync((ParmBlkPtr) &pb); |
junk = SafeUnholdMemory(vRefNum, sizeof(*vRefNum)); |
MoreAssertQ(junk == noErr); |
} |
junk = SafeUnholdMemory( (partInfoRec *) partInfo, sizeof(*partInfo)); |
MoreAssertQ(junk == noErr); |
} |
} |
return err; |
} |
#endif |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14