#include "Scavenger.h"
static int RcdValErr( SGlobPtr GPtr, OSErr type, UInt32 correct, UInt32 incorrect, HFSCatalogNodeID parid );
static int RcdFThdErr( SGlobPtr GPtr, UInt32 fid );
static int RcdNoDirErr( SGlobPtr GPtr, UInt32 did );
static int RcdNameLockedErr( SGlobPtr GPtr, OSErr type, UInt32 incorrect );
static OSErr RcdMDBEmbededVolDescriptionErr( SGlobPtr GPtr, OSErr type, HFSMasterDirectoryBlock *mdb );
static OSErr RcdInvalidWrapperExtents( SGlobPtr GPtr, OSErr type );
static OSErr CheckNodesFirstOffset( SGlobPtr GPtr, BTreeControlBlock *btcb );
static Boolean ExtentInfoExists( ExtentsTable **extentsTableH, ExtentInfo *extentInfo );
static OSErr CheckWrapperExtents( SGlobPtr GPtr, HFSMasterDirectoryBlock *mdb );
static OSErr GetVolumeHeaderBlock(SVCB *vcb, HFSMasterDirectoryBlock *mdb, BlockDescriptor *block,
UInt32 *idSector, UInt32 *hfsPlusIOPosOffset);
OSErr ScavengeVolumeType( SGlobPtr GPtr, HFSMasterDirectoryBlock *mdb, UInt16 *volumeType );
OSErr SeekVolumeHeader( SGlobPtr GPtr, UInt32 startSector, UInt32 numSectors, UInt32 *vHSector );
int
CheckForClean(SGlobPtr GPtr, Boolean markClean)
{
#define kIDSector 2
OSErr err;
int result = -1;
HFSMasterDirectoryBlock *mdbp;
HFSPlusVolumeHeader *vhp;
SVCB *vcb = GPtr->calculatedVCB;
ReleaseBlockOptions rbOptions;
BlockDescriptor block;
vhp = (HFSPlusVolumeHeader *) NULL;
rbOptions = kReleaseBlock;
err = GetVolumeBlock(vcb, kIDSector, kGetBlock, &block);
if (err) return (-1);
mdbp = (HFSMasterDirectoryBlock *) block.buffer;
if (mdbp->drSigWord == kHFSPlusSigWord) {
vhp = (HFSPlusVolumeHeader *) block.buffer;
} else if (mdbp->drSigWord == kHFSSigWord) {
if (mdbp->drEmbedSigWord == kHFSPlusSigWord) {
UInt32 vhSector;
UInt32 blkSectors;
blkSectors = mdbp->drAlBlkSiz / 512;
vhSector = mdbp->drAlBlSt;
vhSector += blkSectors * mdbp->drEmbedExtent.startBlock;
vhSector += kIDSector;
(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
err = GetVolumeBlock(vcb, vhSector, kGetBlock, &block);
if (err) return (-1);
vhp = (HFSPlusVolumeHeader *) block.buffer;
mdbp = (HFSMasterDirectoryBlock *) NULL;
} else {
result = (mdbp->drAtrb & kHFSVolumeUnmountedMask) != 0;
if (markClean && (result == 0)) {
mdbp->drAtrb |= kHFSVolumeUnmountedMask;
rbOptions = kForceWriteBlock;
}
}
}
if ((vhp != NULL) && (ValidVolumeHeader(vhp) == noErr)) {
result = (vhp->attributes & kHFSVolumeUnmountedMask) != 0;
if (markClean && (result == 0)) {
vhp->attributes |= kHFSVolumeUnmountedMask;
rbOptions = kForceWriteBlock;
}
}
(void) ReleaseVolumeBlock(vcb, &block, rbOptions);
return (result);
}
OSErr IVChk( SGlobPtr GPtr )
{
#define kBitsPerSector 4096
#define kLog2SectorSize 9
UInt32 bitMapSizeInSectors;
OSErr err;
HFSMasterDirectoryBlock *alternateMDB;
HFSPlusVolumeHeader *alternateVolumeHeader;
BlockDescriptor block_VH;
BlockDescriptor block_MDB;
UInt32 numABlks;
UInt32 alternateBlockLocation;
UInt32 minABlkSz;
UInt32 totalSectors, sectorSize;
UInt32 maxNumberOfAllocationBlocks;
UInt32 realAllocationBlockSize;
UInt32 realTotalBlocks;
UInt32 hfsBlockSize;
UInt32 hfsBlockCount;
UInt32 i;
UInt32 hfsPlusIOPosOffset;
BTreeControlBlock *btcb;
SVCB *vcb = GPtr->calculatedVCB;
GPtr->TarID = AMDB_FNum; GPtr->TarBlock = 0;
alternateVolumeHeader = NULL;
block_VH.buffer = block_MDB.buffer = NULL;
err = GetDeviceSize(vcb->vcbDriveNumber, &totalSectors, §orSize);
if ( (totalSectors < 3) || (err != noErr) )
return( 123 );
alternateBlockLocation = totalSectors - 2;
again:
err = GetVolumeBlock(vcb, alternateBlockLocation, kGetBlock, &block_MDB);
ReturnIfError( err );
alternateMDB = (HFSMasterDirectoryBlock *) block_MDB.buffer;
if ( alternateMDB->drSigWord == kHFSPlusSigWord )
{
alternateVolumeHeader = (HFSPlusVolumeHeader *)alternateMDB;
GPtr->volumeType = kPureHFSPlusVolumeType;
GPtr->isHFSPlus = true;
WriteMsg( GPtr, M_CheckingHFSPlusVolume, kStatusMessage );
}
else if ( alternateMDB->drSigWord == kHFSSigWord )
{
err = ScavengeVolumeType( GPtr, alternateMDB, &GPtr->volumeType );
if ( ((GPtr->inputFlags & examineWrapperMask) == 0) && (alternateMDB->drEmbedSigWord == kHFSPlusSigWord) )
{
WriteMsg( GPtr, M_CheckingHFSPlusVolume, kStatusMessage );
GPtr->isHFSPlus = true;
}
else
{
WriteMsg( GPtr, M_CheckingHFSVolume, kStatusMessage );
GPtr->isHFSPlus = false;
}
if ( err == E_InvalidMDBdrAlBlSt )
err = RcdMDBEmbededVolDescriptionErr( GPtr, E_InvalidMDBdrAlBlSt, alternateMDB );
}
else
{
if (alternateBlockLocation == MDB_BlkN) {
err = R_BadSig;
goto ReleaseAndBail;
}
alternateBlockLocation = MDB_BlkN;
(void) ReleaseVolumeBlock(vcb, &block_MDB, kReleaseBlock);
block_MDB.buffer = NULL;
goto again;
}
if ( ((GPtr->inputFlags & examineWrapperMask) != 0) && (alternateMDB->drEmbedSigWord == kHFSPlusSigWord) )
{
err = CheckWrapperExtents( GPtr, alternateMDB );
if (err != noErr)
goto ReleaseAndBail;
}
if ( GPtr->isHFSPlus == true )
{
GPtr->numExtents = kHFSPlusExtentDensity;
vcb->vcbSignature = kHFSPlusSigWord;
if ( GPtr->volumeType == kPureHFSPlusVolumeType )
{
hfsPlusIOPosOffset = 0; HFSBlocksFromTotalSectors( totalSectors, &hfsBlockSize, (UInt16*)&hfsBlockCount );
}
else
{
totalSectors = alternateMDB->drEmbedExtent.blockCount * ( alternateMDB->drAlBlkSiz / Blk_Size );
hfsBlockSize = alternateMDB->drAlBlkSiz;
hfsBlockCount = alternateMDB->drNmAlBlks;
err = GetVolumeHeaderBlock(vcb, alternateMDB, &block_VH, &alternateBlockLocation, &hfsPlusIOPosOffset);
if (err)
goto ReleaseAndBail;
alternateVolumeHeader = (HFSPlusVolumeHeader*) block_VH.buffer;
}
err = ValidVolumeHeader( alternateVolumeHeader );
if ( err != noErr )
{
WriteError( GPtr, E_InvalidVolumeHeader, 1, 0 );
err = E_InvalidVolumeHeader; goto ReleaseAndBail;
}
vcb->vcbAlBlSt = hfsPlusIOPosOffset / 512;
vcb->vcbEmbeddedOffset = hfsPlusIOPosOffset;
maxNumberOfAllocationBlocks = 0xFFFFFFFF;
realAllocationBlockSize = alternateVolumeHeader->blockSize;
realTotalBlocks = alternateVolumeHeader->totalBlocks;
vcb->vcbNextCatalogID = alternateVolumeHeader->nextCatalogID;
vcb->vcbCreateDate = alternateVolumeHeader->createDate;
vcb->vcbAttributes = alternateVolumeHeader->attributes & kHFSCatalogNodeIDsReused;
if ( alternateVolumeHeader->attributesFile.totalBlocks == 0 )
vcb->vcbAttributesFile = NULL;
btcb = (BTreeControlBlock *) vcb->vcbExtentsFile->fcbBtree;
btcb->attributes |= kBTBigKeysMask;
}
else {
GPtr->numExtents = kHFSExtentDensity;
vcb->vcbSignature = alternateMDB->drSigWord;
totalSectors = alternateBlockLocation;
maxNumberOfAllocationBlocks = 0xFFFF;
vcb->vcbNextCatalogID = alternateMDB->drNxtCNID; vcb->vcbCreateDate = alternateMDB->drCrDate;
realAllocationBlockSize = alternateMDB->drAlBlkSiz;
realTotalBlocks = alternateMDB->drNmAlBlks;
hfsBlockSize = alternateMDB->drAlBlkSiz;
hfsBlockCount = alternateMDB->drNmAlBlks;
}
GPtr->idSector = alternateBlockLocation; GPtr->TarBlock = alternateBlockLocation;
numABlks = totalSectors;
minABlkSz = Blk_Size; for( i = 2; numABlks > maxNumberOfAllocationBlocks; i++ ) {
minABlkSz = i * Blk_Size; numABlks = alternateBlockLocation / i; }
if ((realAllocationBlockSize >= minABlkSz) && (realAllocationBlockSize <= Max_ABSiz) && ((realAllocationBlockSize % Blk_Size) == 0))
{
vcb->vcbBlockSize = realAllocationBlockSize;
numABlks = totalSectors / ( realAllocationBlockSize / Blk_Size ); }
else
{
RcdError( GPtr, E_ABlkSz );
err = E_ABlkSz; goto ReleaseAndBail;
}
bitMapSizeInSectors = ( numABlks + kBitsPerSector - 1 ) / kBitsPerSector;
vcb->vcbTotalBlocks = realTotalBlocks;
vcb->vcbFreeBlocks = 0;
if ( GPtr->isHFSPlus == false )
{
numABlks = (totalSectors - 3 - bitMapSizeInSectors) / (realAllocationBlockSize / Blk_Size);
if ( realTotalBlocks > numABlks )
{
RcdError( GPtr, E_NABlks );
err = E_NABlks; goto ReleaseAndBail;
}
if ( alternateMDB->drVBMSt <= MDB_BlkN )
{
RcdError(GPtr,E_VBMSt);
err = E_VBMSt; goto ReleaseAndBail;
}
vcb->vcbVBMSt = alternateMDB->drVBMSt;
if (alternateMDB->drAlBlSt < (alternateMDB->drVBMSt + bitMapSizeInSectors))
{
RcdError(GPtr,E_ABlkSt);
err = E_ABlkSt; goto ReleaseAndBail;
}
vcb->vcbAlBlSt = alternateMDB->drAlBlSt;
}
ReleaseAndBail:
if (block_MDB.buffer != NULL)
(void) ReleaseVolumeBlock(vcb, &block_MDB, kReleaseBlock);
if (block_VH.buffer != NULL)
(void) ReleaseVolumeBlock(vcb, &block_VH, kReleaseBlock);
return( err );
}
static OSErr
GetVolumeHeaderBlock(SVCB *vcb, HFSMasterDirectoryBlock *mdb, BlockDescriptor *block,
UInt32 *idSector, UInt32 *hfsPlusIOPosOffset)
{
OSErr err;
HFSPlusVolumeHeader * altVH;
UInt32 totalHFSPlusSectors;
totalHFSPlusSectors = (mdb->drAlBlkSiz / 512) * mdb->drEmbedExtent.blockCount;
*hfsPlusIOPosOffset = (mdb->drEmbedExtent.startBlock * mdb->drAlBlkSiz) + (mdb->drAlBlSt * 512);
*idSector = mdb->drAlBlSt + ((mdb->drAlBlkSiz / 512) * mdb->drEmbedExtent.startBlock) + totalHFSPlusSectors - 2;
err = GetVolumeBlock(vcb, *idSector, kGetBlock, block);
altVH = (HFSPlusVolumeHeader*) block->buffer;
if ( err == noErr )
err = ValidVolumeHeader(altVH);
if ( err != noErr ) {
*idSector = (mdb->drEmbedExtent.startBlock * mdb->drAlBlkSiz / 512) + mdb->drAlBlSt + 2;
err = GetVolumeBlock(vcb, *idSector, kGetBlock, block);
altVH = (HFSPlusVolumeHeader*) block->buffer;
if ( err == noErr )
err = ValidVolumeHeader(altVH);
}
return (err);
}
OSErr ScavengeVolumeType( SGlobPtr GPtr, HFSMasterDirectoryBlock *mdb, UInt16 *volumeType )
{
UInt32 vHSector;
UInt32 totalSectors;
UInt32 sectorSize;
UInt32 startSector;
UInt32 altVHSector;
UInt32 sectorsPerBlock;
UInt32 hfsPlusSectors = 0;
UInt32 numSectorsToSearch;
OSErr err;
HFSPlusVolumeHeader *volumeHeader;
HFSExtentDescriptor embededExtent;
SVCB *calculatedVCB = GPtr->calculatedVCB;
UInt16 embedSigWord = mdb->drEmbedSigWord;
BlockDescriptor block;
if (embedSigWord == 0 &&
mdb->drEmbedExtent.blockCount == 0 &&
mdb->drEmbedExtent.startBlock == 0)
{
*volumeType = kHFSVolumeType;
return noErr;
}
*volumeType = kEmbededHFSPlusVolumeType;
if ( embedSigWord == kHFSPlusSigWord )
{
vHSector = mdb->drAlBlSt + ((mdb->drAlBlkSiz / 512) * mdb->drEmbedExtent.startBlock) + 2;
err = GetVolumeBlock(calculatedVCB, vHSector, kGetBlock, &block);
volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
if ( err != noErr ) goto AssumeHFS;
err = ValidVolumeHeader( volumeHeader );
(void) ReleaseVolumeBlock(calculatedVCB, &block, kReleaseBlock);
if ( err == noErr )
return( noErr );
}
sectorsPerBlock = mdb->drAlBlkSiz / 512;
if ( embedSigWord != kHFSPlusSigWord )
{
err = GetDeviceSize( GPtr->calculatedVCB->vcbDriveNumber, &totalSectors, §orSize );
if ( err != noErr ) goto AssumeHFS;
numSectorsToSearch = mdb->drAlBlkSiz / sectorSize;
startSector = totalSectors - 4 - numSectorsToSearch;
err = SeekVolumeHeader( GPtr, startSector, numSectorsToSearch, &altVHSector );
if ( err != noErr ) goto AssumeHFS;
startSector = mdb->drAlBlSt + (4 * sectorsPerBlock); numSectorsToSearch = 10 * sectorsPerBlock;
err = SeekVolumeHeader( GPtr, startSector, numSectorsToSearch, &vHSector );
if ( err != noErr ) goto AssumeHFS;
hfsPlusSectors = altVHSector - vHSector + 1 + 2 + 1;
embededExtent.blockCount = hfsPlusSectors / sectorsPerBlock;
embededExtent.startBlock = (vHSector - 2 - mdb->drAlBlSt ) / sectorsPerBlock;
embedSigWord = kHFSPlusSigWord;
}
else
{
embedSigWord = mdb->drEmbedSigWord;
embededExtent.blockCount = mdb->drEmbedExtent.blockCount;
embededExtent.startBlock = mdb->drEmbedExtent.startBlock;
}
if ( embedSigWord == kHFSPlusSigWord )
{
startSector = (embededExtent.startBlock * mdb->drAlBlkSiz / 512) + mdb->drAlBlSt + 2;
err = SeekVolumeHeader( GPtr, startSector, mdb->drAlBlkSiz / 512, &vHSector );
if ( err != noErr ) goto AssumeHFS;
mdb->drEmbedExtent.blockCount = hfsPlusSectors / sectorsPerBlock;
mdb->drEmbedExtent.startBlock = (vHSector - 2 - mdb->drAlBlSt ) / sectorsPerBlock;
mdb->drEmbedSigWord = kHFSPlusSigWord;
mdb->drAlBlSt += vHSector - startSector; GPtr->VIStat = GPtr->VIStat | S_MDB; return( E_InvalidMDBdrAlBlSt );
}
AssumeHFS:
*volumeType = kHFSVolumeType;
return( noErr );
}
OSErr SeekVolumeHeader( SGlobPtr GPtr, UInt32 startSector, UInt32 numSectors, UInt32 *vHSector )
{
OSErr err;
HFSPlusVolumeHeader *volumeHeader;
SVCB *calculatedVCB = GPtr->calculatedVCB;
BlockDescriptor block;
for ( *vHSector = startSector ; *vHSector < startSector + numSectors ; (*vHSector)++ )
{
err = GetVolumeBlock(calculatedVCB, *vHSector, kGetBlock, &block);
volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
if ( err != noErr ) return( err );
err = ValidVolumeHeader(volumeHeader);
(void) ReleaseVolumeBlock(calculatedVCB, &block, kReleaseBlock);
if ( err == noErr )
return( noErr );
}
return( fnfErr );
}
static OSErr CheckWrapperExtents( SGlobPtr GPtr, HFSMasterDirectoryBlock *mdb )
{
OSErr err = noErr;
if ( mdb->drCTExtRec[0].startBlock >= mdb->drEmbedExtent.startBlock)
{
mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock + mdb->drXTExtRec[0].blockCount;
GPtr->VIStat = GPtr->VIStat | S_MDB; err = RcdInvalidWrapperExtents( GPtr, E_InvalidWrapperExtents );
}
return err;
}
OSErr CreateExtentsBTreeControlBlock( SGlobPtr GPtr )
{
OSErr err;
SInt32 size;
UInt32 numABlks;
BTHeaderRec header;
BTreeControlBlock * btcb;
SVCB * vcb;
BlockDescriptor block;
GPtr->TarID = kHFSExtentsFileID; GPtr->TarBlock = kHeaderNodeNum; vcb = GPtr->calculatedVCB;
btcb = GPtr->calculatedExtentsBTCB;
err = GetVolumeBlock(vcb, GPtr->idSector, kGetBlock, &block);
ReturnIfError(err);
if (GPtr->isHFSPlus)
{
HFSPlusVolumeHeader *volumeHeader;
volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
CopyMemory(volumeHeader->extentsFile.extents, GPtr->calculatedExtentsFCB->fcbExtents32, sizeof(HFSPlusExtentRecord) );
err = CheckFileExtents( GPtr, kHFSExtentsFileID, 0, (void *)GPtr->calculatedExtentsFCB->fcbExtents32, &numABlks ); if (err) goto exit;
if ( volumeHeader->extentsFile.totalBlocks != numABlks ) {
RcdError( GPtr, E_ExtPEOF );
err = E_ExtPEOF;
goto exit;
}
else
{
GPtr->calculatedExtentsFCB->fcbLogicalSize = (UInt32) volumeHeader->extentsFile.logicalSize; GPtr->calculatedExtentsFCB->fcbPhysicalSize = volumeHeader->extentsFile.totalBlocks * volumeHeader->blockSize; }
err = GetBTreeHeader(GPtr, GPtr->calculatedExtentsFCB, &header);
if (err) goto exit;
btcb->maxKeyLength = kHFSPlusExtentKeyMaximumLength; btcb->keyCompareProc = (void *)CompareExtentKeysPlus;
btcb->attributes |=kBTBigKeysMask; btcb->leafRecords = header.leafRecords;
btcb->treeDepth = header.treeDepth;
btcb->rootNode = header.rootNode;
btcb->firstLeafNode = header.firstLeafNode;
btcb->lastLeafNode = header.lastLeafNode;
btcb->nodeSize = header.nodeSize;
btcb->totalNodes = ( GPtr->calculatedExtentsFCB->fcbPhysicalSize / btcb->nodeSize );
btcb->freeNodes = btcb->totalNodes;
err = CheckNodesFirstOffset( GPtr, btcb );
if ( (err != noErr) && (btcb->nodeSize != 1024) ) {
btcb->nodeSize = 1024;
btcb->totalNodes = ( GPtr->calculatedExtentsFCB->fcbPhysicalSize / btcb->nodeSize );
btcb->freeNodes = btcb->totalNodes;
err = CheckNodesFirstOffset( GPtr, btcb );
if (err) goto exit;
GPtr->EBTStat |= S_BTH; }
}
else {
HFSMasterDirectoryBlock *alternateMDB;
alternateMDB = (HFSMasterDirectoryBlock *) block.buffer;
CopyMemory(alternateMDB->drXTExtRec, GPtr->calculatedExtentsFCB->fcbExtents16, sizeof(HFSExtentRecord) );
err = CheckFileExtents( GPtr, kHFSExtentsFileID, 0, (void *)GPtr->calculatedExtentsFCB->fcbExtents16, &numABlks );
if (err) goto exit;
if (alternateMDB->drXTFlSize != (numABlks * GPtr->calculatedVCB->vcbBlockSize)) {
RcdError(GPtr,E_ExtPEOF);
err = E_ExtPEOF;
goto exit;
}
else
{
GPtr->calculatedExtentsFCB->fcbPhysicalSize = alternateMDB->drXTFlSize; GPtr->calculatedExtentsFCB->fcbLogicalSize = GPtr->calculatedExtentsFCB->fcbPhysicalSize;
}
err = GetBTreeHeader(GPtr, GPtr->calculatedExtentsFCB, &header);
if (err) goto exit;
btcb->maxKeyLength = kHFSExtentKeyMaximumLength; btcb->keyCompareProc = (void *)CompareExtentKeys;
btcb->leafRecords = header.leafRecords;
btcb->treeDepth = header.treeDepth;
btcb->rootNode = header.rootNode;
btcb->firstLeafNode = header.firstLeafNode;
btcb->lastLeafNode = header.lastLeafNode;
btcb->nodeSize = header.nodeSize;
btcb->totalNodes = (GPtr->calculatedExtentsFCB->fcbPhysicalSize / btcb->nodeSize );
btcb->freeNodes = btcb->totalNodes;
err = CheckNodesFirstOffset( GPtr, btcb );
if (err) goto exit;
}
if ( header.btreeType != kHFSBTreeType )
{
GPtr->EBTStat |= S_ReservedBTH; }
btcb->refCon = (UInt32) AllocateClearMemory( sizeof(BTreeExtensionsRec) ); if ( btcb->refCon == (UInt32) nil ) {
err = R_NoMem;
goto exit;
}
size = (btcb->totalNodes + 7) / 8; ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr = AllocateClearMemory(size); if ( ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr == nil )
{
err = R_NoMem;
goto exit;
}
((BTreeExtensionsRec*)btcb->refCon)->BTCBMSize = size; ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount = header.freeNodes;exit:
(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
return (err);
}
#define GetRecordOffset(btreePtr,node,index) (*(short *) ((UInt8 *)(node) + (btreePtr)->nodeSize - ((index) << 1) - kOffsetSize))
static OSErr CheckNodesFirstOffset( SGlobPtr GPtr, BTreeControlBlock *btcb )
{
NodeRec nodeRec;
UInt16 offset;
OSErr err;
(void) SetFileBlockSize(btcb->fcbPtr, btcb->nodeSize);
err = GetNode( btcb, kHeaderNodeNum, &nodeRec );
if ( err == noErr )
{
offset = GetRecordOffset( btcb, (NodeDescPtr)nodeRec.buffer, 0 );
if ( (offset < sizeof (BTNodeDescriptor)) || (offset & 1) || (offset >= btcb->nodeSize) ) {
err = fsBTInvalidNodeErr;
}
}
if ( err != noErr )
RcdError( GPtr, E_InvalidNodeSize );
(void) ReleaseNode(btcb, &nodeRec);
return( err );
}
OSErr ExtBTChk( SGlobPtr GPtr )
{
OSErr err;
GPtr->TarID = kHFSExtentsFileID; GPtr->TarBlock = GPtr->idSector;
err = BTCheck(GPtr, kCalculatedExtentRefNum, NULL);
ReturnIfError( err );
err = BTMapChk( GPtr, kCalculatedExtentRefNum );
ReturnIfError( err );
err = CmpBTH( GPtr, kCalculatedExtentRefNum );
ReturnIfError( err );
err = CmpBTM( GPtr, kCalculatedExtentRefNum );
return( err );
}
OSErr ExtFlChk( SGlobPtr GPtr )
{
UInt32 attributes;
void *p;
OSErr result;
SVCB * vcb;
BlockDescriptor block;
vcb = GPtr->calculatedVCB;
result = GetVolumeBlock(vcb, GPtr->idSector, kGetBlock, &block);
ReturnIfError( result ); p = (void *) block.buffer;
attributes = GPtr->isHFSPlus == true ? ((HFSPlusVolumeHeader*)p)->attributes : ((HFSMasterDirectoryBlock*)p)->drAtrb;
if ( attributes & kHFSVolumeSparedBlocksMask ) {
HFSPlusExtentRecord zeroXdr; UInt32 numBadBlocks;
ClearMemory ( zeroXdr, sizeof( HFSPlusExtentRecord ) );
result = CheckFileExtents( GPtr, kHFSBadBlockFileID, 0, (void *)zeroXdr, &numBadBlocks ); }
(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
return (result);
}
OSErr CreateCatalogBTreeControlBlock( SGlobPtr GPtr )
{
OSErr err;
SInt32 size;
UInt32 numABlks;
BTHeaderRec header;
BTreeControlBlock * btcb;
SVCB * vcb;
BlockDescriptor block;
GPtr->TarID = kHFSCatalogFileID;
GPtr->TarBlock = kHeaderNodeNum;
vcb = GPtr->calculatedVCB;
btcb = GPtr->calculatedCatalogBTCB;
err = GetVolumeBlock(vcb, GPtr->idSector, kGetBlock, &block);
ReturnIfError(err);
if (GPtr->isHFSPlus)
{
HFSPlusVolumeHeader * volumeHeader;
volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
CopyMemory(volumeHeader->catalogFile.extents, GPtr->calculatedCatalogFCB->fcbExtents32, sizeof(HFSPlusExtentRecord) );
err = CheckFileExtents( GPtr, kHFSCatalogFileID, 0, (void *)GPtr->calculatedCatalogFCB->fcbExtents32, &numABlks );
if (err) goto exit;
if ( volumeHeader->catalogFile.totalBlocks != numABlks )
{
RcdError( GPtr, E_CatPEOF );
err = E_CatPEOF;
goto exit;
}
else
{
GPtr->calculatedCatalogFCB->fcbLogicalSize = (UInt32) volumeHeader->catalogFile.logicalSize;
GPtr->calculatedCatalogFCB->fcbPhysicalSize = volumeHeader->catalogFile.totalBlocks * volumeHeader->blockSize;
}
err = GetBTreeHeader(GPtr, GPtr->calculatedCatalogFCB, &header);
if (err) goto exit;
btcb->maxKeyLength = kHFSPlusCatalogKeyMaximumLength; btcb->keyCompareProc = (void *)CompareExtendedCatalogKeys;
btcb->leafRecords = header.leafRecords;
btcb->nodeSize = header.nodeSize;
btcb->totalNodes = ( GPtr->calculatedCatalogFCB->fcbPhysicalSize / btcb->nodeSize );
btcb->freeNodes = btcb->totalNodes; btcb->attributes |=(kBTBigKeysMask + kBTVariableIndexKeysMask);
btcb->treeDepth = header.treeDepth;
btcb->rootNode = header.rootNode;
btcb->firstLeafNode = header.firstLeafNode;
btcb->lastLeafNode = header.lastLeafNode;
err = CheckNodesFirstOffset( GPtr, btcb );
if ( (err != noErr) && (btcb->nodeSize != 4096) ) {
btcb->nodeSize = 4096;
btcb->totalNodes = ( GPtr->calculatedCatalogFCB->fcbPhysicalSize / btcb->nodeSize );
btcb->freeNodes = btcb->totalNodes;
err = CheckNodesFirstOffset( GPtr, btcb );
if (err) goto exit;
GPtr->CBTStat |= S_BTH; }
}
else {
HFSMasterDirectoryBlock *alternateMDB;
alternateMDB = (HFSMasterDirectoryBlock *) block.buffer;
CopyMemory( alternateMDB->drCTExtRec, GPtr->calculatedCatalogFCB->fcbExtents16, sizeof(HFSExtentRecord) );
err = CheckFileExtents( GPtr, kHFSCatalogFileID, 0, (void *)GPtr->calculatedCatalogFCB->fcbExtents16, &numABlks );
if (err) goto exit;
if (alternateMDB->drCTFlSize != (numABlks * vcb->vcbBlockSize)) {
RcdError( GPtr, E_CatPEOF );
err = E_CatPEOF;
goto exit;
}
else
{
GPtr->calculatedCatalogFCB->fcbPhysicalSize = alternateMDB->drCTFlSize; GPtr->calculatedCatalogFCB->fcbLogicalSize = GPtr->calculatedCatalogFCB->fcbPhysicalSize;
}
err = GetBTreeHeader(GPtr, GPtr->calculatedCatalogFCB, &header);
if (err) goto exit;
btcb->maxKeyLength = kHFSCatalogKeyMaximumLength; btcb->keyCompareProc = (void *) CompareCatalogKeys;
btcb->leafRecords = header.leafRecords;
btcb->nodeSize = header.nodeSize;
btcb->totalNodes = (GPtr->calculatedCatalogFCB->fcbPhysicalSize / btcb->nodeSize );
btcb->freeNodes = btcb->totalNodes;
btcb->treeDepth = header.treeDepth;
btcb->rootNode = header.rootNode;
btcb->firstLeafNode = header.firstLeafNode;
btcb->lastLeafNode = header.lastLeafNode;
err = CheckNodesFirstOffset( GPtr, btcb );
if (err) goto exit;
}
#if 0
printf(" Catalog B-tree is %qd bytes\n", (UInt64)btcb->totalNodes * (UInt64) btcb->nodeSize);
#endif
if ( header.btreeType != kHFSBTreeType )
{
GPtr->CBTStat |= S_ReservedBTH; }
btcb->refCon = (UInt32) AllocateClearMemory( sizeof(BTreeExtensionsRec) ); if ( btcb->refCon == (UInt32)nil ) {
err = R_NoMem;
goto exit;
}
size = (btcb->totalNodes + 7) / 8; ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr = AllocateClearMemory(size); if ( ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr == nil )
{
err = R_NoMem;
goto exit;
}
((BTreeExtensionsRec*)btcb->refCon)->BTCBMSize = size; ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount = header.freeNodes;
exit:
(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
return (err);
}
OSErr CreateExtendedAllocationsFCB( SGlobPtr GPtr )
{
OSErr err = 0;
UInt32 numABlks;
SVCB * vcb;
BlockDescriptor block;
GPtr->TarID = kHFSAllocationFileID;
GPtr->TarBlock = GPtr->idSector;
vcb = GPtr->calculatedVCB;
block.buffer = NULL;
if ( GPtr->isHFSPlus )
{
SFCB * fcb;
HFSPlusVolumeHeader *volumeHeader;
err = GetVolumeBlock(vcb, GPtr->idSector, kGetBlock, &block);
ReturnIfError(err);
volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
fcb = GPtr->calculatedAllocationsFCB;
CopyMemory( volumeHeader->allocationFile.extents, fcb->fcbExtents32, sizeof(HFSPlusExtentRecord) );
err = CheckFileExtents( GPtr, kHFSAllocationFileID, 0, (void *)fcb->fcbExtents32, &numABlks );
if (err) goto exit;
(void) SetFileBlockSize (fcb, 512);
if ( volumeHeader->allocationFile.totalBlocks != numABlks )
{
RcdError( GPtr, E_CatPEOF );
err = E_CatPEOF;
goto exit;
}
else
{
fcb->fcbLogicalSize = (UInt32) volumeHeader->allocationFile.logicalSize;
fcb->fcbPhysicalSize = volumeHeader->allocationFile.totalBlocks * volumeHeader->blockSize;
}
fcb = GPtr->calculatedStartupFCB;
CopyMemory( volumeHeader->startupFile.extents, fcb->fcbExtents32, sizeof(HFSPlusExtentRecord) );
err = CheckFileExtents( GPtr, kHFSStartupFileID, 0, (void *)fcb->fcbExtents32, &numABlks );
if (err) goto exit;
fcb->fcbLogicalSize = (UInt32) volumeHeader->startupFile.logicalSize;
fcb->fcbPhysicalSize = volumeHeader->startupFile.totalBlocks * volumeHeader->blockSize;
}
exit:
if (block.buffer)
(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
return (err);
}
OSErr CatHChk( SGlobPtr GPtr )
{
SInt16 i;
OSErr result;
UInt16 recSize;
SInt16 selCode;
UInt32 hint;
UInt32 dirCnt;
UInt32 filCnt;
SInt16 rtdirCnt;
SInt16 rtfilCnt;
SVCB *calculatedVCB;
SDPR *dprP;
SDPR *dprP1;
CatalogKey foundKey;
Boolean validKeyFound;
CatalogKey key;
CatalogRecord record;
CatalogRecord record2;
HFSPlusCatalogFolder *largeCatalogFolderP;
HFSPlusCatalogFile *largeCatalogFileP;
HFSCatalogFile *smallCatalogFileP;
HFSCatalogFolder *smallCatalogFolderP;
CatalogName catalogName;
UInt32 valence;
CatalogRecord threadRecord;
HFSCatalogNodeID parID;
Boolean isHFSPlus = GPtr->isHFSPlus;
calculatedVCB = GPtr->calculatedVCB;
GPtr->TarID = kHFSCatalogFileID;
GPtr->TarBlock = 0;
{
BuildCatalogKey( 1, (const CatalogName *)nil, isHFSPlus, &key );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &threadRecord, &recSize, &hint );
GPtr->TarBlock = hint;
if ( result != btNotFound )
{
RcdError( GPtr, E_CatRec );
return( E_CatRec );
}
}
GPtr->DirLevel = 1;
dprP = &(*GPtr->DirPTPtr)[0];
dprP->directoryID = 1;
dirCnt = filCnt = rtdirCnt = rtfilCnt = 0;
result = noErr;
selCode = 0x8001;
while ( (GPtr->DirLevel > 0) && (result == noErr) )
{
dprP = &(*GPtr->DirPTPtr)[GPtr->DirLevel -1];
validKeyFound = true;
record.recordType = 0;
result = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recSize, &hint );
GPtr->TarBlock = hint;
if ( result != noErr )
{
if ( result == btNotFound )
{
result = noErr;
validKeyFound = false;
}
else
{
result = IntError( GPtr, result );
return( result );
}
}
selCode = 1;
GPtr->itemsProcessed++;
parID = isHFSPlus == true ? foundKey.hfsPlus.parentID : foundKey.hfs.parentID;
if ( (validKeyFound == true) && (parID == dprP->directoryID) )
{
dprP->offspringIndex++;
if ( record.recordType == kHFSPlusFolderRecord )
{
result = CheckForStop( GPtr ); ReturnIfError( result );
largeCatalogFolderP = (HFSPlusCatalogFolder *) &record;
GPtr->TarID = largeCatalogFolderP->folderID; GPtr->CNType = record.recordType; CopyCatalogName( (const CatalogName *) &foundKey.hfsPlus.nodeName, &GPtr->CName, isHFSPlus );
if ( dprP->directoryID > 1 )
{
GPtr->DirLevel++; dirCnt++;
}
if ( dprP->directoryID == kHFSRootFolderID ) rtdirCnt++;
if ( GPtr->DirLevel > CMMaxDepth )
{
RcdError(GPtr,E_CatDepth); return noErr; }
dprP = &(*GPtr->DirPTPtr)[GPtr->DirLevel -1];
dprP->directoryID = largeCatalogFolderP->folderID;
dprP->offspringIndex = 1;
dprP->directoryHint = hint;
dprP->parentDirID = foundKey.hfsPlus.parentID;
CopyCatalogName( (const CatalogName *) &foundKey.hfsPlus.nodeName, &dprP->directoryName, isHFSPlus );
for ( i = 1; i < GPtr->DirLevel; i++ )
{
dprP1 = &(*GPtr->DirPTPtr)[i -1];
if (dprP->directoryID == dprP1->directoryID)
{
RcdError( GPtr,E_DirLoop ); return( E_DirLoop );
}
}
BuildCatalogKey( dprP->directoryID, (const CatalogName *) nil, isHFSPlus, &key );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &threadRecord, &recSize, &hint );
if ( result != noErr ) {
char idStr[16];
struct MissingThread *mtp;
sprintf(idStr, "%d", dprP->directoryID);
PrintError(GPtr, E_NoThd, 1, idStr);
if ( !isHFSPlus )
return (E_NoThd);
for (mtp = GPtr->missingThreadList; mtp != NULL; mtp = mtp->link) {
if (mtp->threadID == dprP->directoryID) {
mtp->thread.recordType = kHFSPlusFolderThreadRecord;
mtp->thread.parentID = dprP->parentDirID;
CopyCatalogName(&dprP->directoryName, (CatalogName *)&mtp->thread.nodeName, isHFSPlus);
result = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &mtp->nextKey,
kNoHint, &foundKey, &threadRecord, &recSize, &hint);
if (result)
return (E_NoThd);
selCode = 0;
break;
}
}
if (selCode != 0) {
goto resumeAtParent;
}
}
dprP->threadHint = hint;
GPtr->TarBlock = hint;
}
else if ( record.recordType == kHFSPlusFileRecord )
{
largeCatalogFileP = (HFSPlusCatalogFile *) &record;
GPtr->TarID = largeCatalogFileP->fileID; GPtr->CNType = record.recordType; CopyCatalogName( (const CatalogName *) &foundKey.hfsPlus.nodeName, &GPtr->CName, isHFSPlus );
filCnt++;
if (dprP->directoryID == kHFSRootFolderID)
rtfilCnt++;
}
else if ( record.recordType == kHFSFolderRecord )
{
result = CheckForStop( GPtr ); ReturnIfError( result );
smallCatalogFolderP = (HFSCatalogFolder *) &record;
GPtr->TarID = smallCatalogFolderP->folderID;
GPtr->CNType = record.recordType;
CopyCatalogName( (const CatalogName *) &key.hfs.nodeName, &GPtr->CName, isHFSPlus );
if (dprP->directoryID > 1)
{
GPtr->DirLevel++;
dirCnt++;
}
if (dprP->directoryID == kHFSRootFolderID)
rtdirCnt++;
if (GPtr->DirLevel > CMMaxDepth)
{
RcdError(GPtr,E_CatDepth);
return noErr;
}
dprP = &(*GPtr->DirPTPtr)[GPtr->DirLevel -1];
dprP->directoryID = smallCatalogFolderP->folderID;
dprP->offspringIndex = 1;
dprP->directoryHint = hint;
dprP->parentDirID = foundKey.hfs.parentID;
CopyCatalogName( (const CatalogName *) &foundKey.hfs.nodeName, &dprP->directoryName, isHFSPlus );
for (i = 1; i < GPtr->DirLevel; i++)
{
dprP1 = &(*GPtr->DirPTPtr)[i -1];
if (dprP->directoryID == dprP1->directoryID)
{
RcdError( GPtr,E_DirLoop );
return( E_DirLoop );
}
}
BuildCatalogKey( dprP->directoryID, (const CatalogName *)0, isHFSPlus, &key );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &threadRecord, &recSize, &hint );
if (result != noErr )
{
result = IntError(GPtr,result);
return(result);
}
dprP->threadHint = hint;
GPtr->TarBlock = hint;
}
else if ( record.recordType == kHFSFileRecord )
{
smallCatalogFileP = (HFSCatalogFile *) &record;
GPtr->TarID = smallCatalogFileP->fileID;
GPtr->CNType = record.recordType;
CopyCatalogName( (const CatalogName *) &foundKey.hfs.nodeName, &GPtr->CName, isHFSPlus );
filCnt++;
if (dprP->directoryID == kHFSRootFolderID)
rtfilCnt++;
}
else
{
M_DebugStr("\p Unknown-Bad record type");
return( 123 );
}
}
else if ( (record.recordType == kHFSFileThreadRecord) || (record.recordType == kHFSPlusFileThreadRecord) )
{
GPtr->TarID = parID; GPtr->CNType = record.recordType; GPtr->CName.ustr.length = 0; }
else
{
resumeAtParent:
GPtr->TarID = dprP->directoryID;
GPtr->CNType = record.recordType;
CopyCatalogName( (const CatalogName *) &dprP->directoryName, &GPtr->CName, isHFSPlus );
CopyCatalogName( (const CatalogName *) &dprP->directoryName, &catalogName, isHFSPlus );
BuildCatalogKey( dprP->parentDirID, (const CatalogName *)&catalogName, isHFSPlus, &key );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, dprP->directoryHint, &foundKey, &record2, &recSize, &hint );
if ( result != noErr )
{
result = IntError(GPtr,result);
return(result);
}
GPtr->TarBlock = hint;
valence = isHFSPlus == true ? record2.hfsPlusFolder.valence : (UInt32)record2.hfsFolder.valence;
if ( valence != dprP->offspringIndex -1 )
if ( result = RcdValErr( GPtr, E_DirVal, dprP->offspringIndex -1, valence, dprP->parentDirID ) )
return( result );
GPtr->DirLevel--;
if(GPtr->DirLevel > 0)
{
dprP = &(*GPtr->DirPTPtr)[GPtr->DirLevel -1];
GPtr->TarID = dprP->directoryID;
GPtr->CNType = record.recordType;
CopyCatalogName( (const CatalogName *) &dprP->directoryName, &GPtr->CName, isHFSPlus );
}
}
}
if (!isHFSPlus && (rtdirCnt != calculatedVCB->vcbNmRtDirs))
if ( result = RcdValErr(GPtr,E_RtDirCnt,rtdirCnt,calculatedVCB->vcbNmRtDirs,0) )
return( result );
if (!isHFSPlus && (rtfilCnt != calculatedVCB->vcbNmFls))
if ( result = RcdValErr(GPtr,E_RtFilCnt,rtfilCnt,calculatedVCB->vcbNmFls,0) )
return( result );
if (dirCnt != calculatedVCB->vcbFolderCount)
if ( result = RcdValErr(GPtr,E_DirCnt,dirCnt,calculatedVCB->vcbFolderCount,0) )
return( result );
if (filCnt != calculatedVCB->vcbFileCount)
if ( result = RcdValErr(GPtr,E_FilCnt,filCnt,calculatedVCB->vcbFileCount,0) )
return( result );
return( noErr );
}
OSErr CreateAttributesBTreeControlBlock( SGlobPtr GPtr )
{
OSErr err = 0;
SInt32 size;
UInt32 numABlks;
BTHeaderRec header;
BTreeControlBlock * btcb;
SVCB * vcb;
BlockDescriptor block;
GPtr->TarID = kHFSAttributesFileID;
GPtr->TarBlock = kHeaderNodeNum;
block.buffer = NULL;
btcb = GPtr->calculatedAttributesBTCB;
vcb = GPtr->calculatedVCB;
if (GPtr->isHFSPlus)
{
HFSPlusVolumeHeader *volumeHeader;
err = GetVolumeBlock(vcb, GPtr->idSector, kGetBlock, &block);
ReturnIfError(err);
volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
CopyMemory( volumeHeader->attributesFile.extents, GPtr->calculatedAttributesFCB->fcbExtents32, sizeof(HFSPlusExtentRecord) );
err = CheckFileExtents( GPtr, kHFSAttributesFileID, 0, (void *)GPtr->calculatedAttributesFCB->fcbExtents32, &numABlks );
if (err) goto exit;
if ( volumeHeader->attributesFile.totalBlocks != numABlks ) {
RcdError( GPtr, E_CatPEOF );
err = E_CatPEOF;
goto exit;
}
else
{
GPtr->calculatedAttributesFCB->fcbLogicalSize = (UInt64) volumeHeader->attributesFile.logicalSize; GPtr->calculatedAttributesFCB->fcbPhysicalSize = volumeHeader->attributesFile.totalBlocks * volumeHeader->blockSize; }
if (numABlks == 0)
{
btcb->maxKeyLength = 0;
btcb->keyCompareProc = 0;
btcb->leafRecords = 0;
btcb->nodeSize = 0;
btcb->totalNodes = 0;
btcb->freeNodes = 0;
btcb->attributes = 0;
btcb->treeDepth = 0;
btcb->rootNode = 0;
btcb->firstLeafNode = 0;
btcb->lastLeafNode = 0;
GPtr->calculatedVCB->vcbAttributesFile = NULL;
}
else
{
err = GetBTreeHeader(GPtr, GPtr->calculatedAttributesFCB, &header);
if (err) goto exit;
btcb->maxKeyLength = kAttributeKeyMaximumLength; btcb->keyCompareProc = (void *)CompareAttributeKeys;
btcb->leafRecords = header.leafRecords;
btcb->nodeSize = header.nodeSize;
btcb->totalNodes = ( GPtr->calculatedAttributesFCB->fcbPhysicalSize / btcb->nodeSize );
btcb->freeNodes = btcb->totalNodes; btcb->attributes |=(kBTBigKeysMask + kBTVariableIndexKeysMask);
btcb->treeDepth = header.treeDepth;
btcb->rootNode = header.rootNode;
btcb->firstLeafNode = header.firstLeafNode;
btcb->lastLeafNode = header.lastLeafNode;
err = CheckNodesFirstOffset( GPtr, btcb );
if (err) goto exit;
}
}
else
{
btcb->maxKeyLength = 0;
btcb->keyCompareProc = 0;
btcb->leafRecords = 0;
btcb->nodeSize = 0;
btcb->totalNodes = 0;
btcb->freeNodes = 0;
btcb->attributes = 0;
btcb->treeDepth = 0;
btcb->rootNode = 0;
btcb->firstLeafNode = 0;
btcb->lastLeafNode = 0;
GPtr->calculatedVCB->vcbAttributesFile = NULL;
}
btcb->refCon = (UInt32) AllocateClearMemory( sizeof(BTreeExtensionsRec) ); if ( btcb->refCon == (UInt32)nil ) {
err = R_NoMem;
goto exit;
}
if (btcb->totalNodes == 0)
{
((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr = nil;
((BTreeExtensionsRec*)btcb->refCon)->BTCBMSize = 0;
((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount = 0;
}
else
{
if ( btcb->refCon == (UInt32)nil ) {
err = R_NoMem;
goto exit;
}
size = (btcb->totalNodes + 7) / 8; ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr = AllocateClearMemory(size); if ( ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr == nil )
{
err = R_NoMem;
goto exit;
}
((BTreeExtensionsRec*)btcb->refCon)->BTCBMSize = size; ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount = header.freeNodes; }
exit:
if (block.buffer)
(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
return (err);
}
OSErr AttrBTChk( SGlobPtr GPtr )
{
OSErr err;
if (GPtr->calculatedVCB->vcbAttributesFile == NULL)
return noErr;
WriteMsg( GPtr, M_AttrBTChk, kStatusMessage );
GPtr->TarID = kHFSAttributesFileID; GPtr->TarBlock = GPtr->idSector;
err = BTCheck( GPtr, kCalculatedAttributesRefNum, NULL);
ReturnIfError( err );
err = BTMapChk( GPtr, kCalculatedAttributesRefNum );
ReturnIfError( err );
err = CmpBTH( GPtr, kCalculatedAttributesRefNum );
ReturnIfError( err );
err = CmpBTM( GPtr, kCalculatedAttributesRefNum );
return( err );
}
static int RcdFThdErr( SGlobPtr GPtr, UInt32 fid ) {
RepairOrderPtr p;
RcdError( GPtr, E_NoFile );
p = AllocMinorRepairOrder( GPtr, 0 ); if ( p==NULL ) return( R_NoMem );
p->type = E_NoFile; p->parid = fid; GPtr->CatStat |= S_FThd;
return( noErr ); }
static int RcdNoDirErr( SGlobPtr GPtr, UInt32 did ) {
RepairOrderPtr p;
RcdError( GPtr, E_NoDir );
p = AllocMinorRepairOrder( GPtr, 0 ); if ( p==NULL ) return ( R_NoMem );
p->type = E_NoDir; p->parid = did; GPtr->CatStat |= S_NoDir;
return( noErr ); }
static int RcdValErr( SGlobPtr GPtr, OSErr type, UInt32 correct, UInt32 incorrect, HFSCatalogNodeID parid )
{
RepairOrderPtr p;
SInt16 n;
char goodStr[32], badStr[32];
PrintError(GPtr, type, 0);
sprintf(goodStr, "%d", correct);
sprintf(badStr, "%d", incorrect);
PrintError(GPtr, E_BadValue, 2, goodStr, badStr);
if (type == E_DirVal)
n = CatalogNameSize( &GPtr->CName, GPtr->isHFSPlus);
else
n = 0;
p = AllocMinorRepairOrder( GPtr,n );
if (p==NULL)
return (R_NoMem);
p->type = type;
p->correct = correct;
p->incorrect = incorrect;
p->parid = parid;
if ( n != 0 )
CopyCatalogName( (const CatalogName *) &GPtr->CName, (CatalogName*)&p->name, GPtr->isHFSPlus );
GPtr->CatStat |= S_Valence;
return( noErr );
}
static OSErr RcdMDBEmbededVolDescriptionErr( SGlobPtr GPtr, OSErr type, HFSMasterDirectoryBlock *mdb )
{
RepairOrderPtr p; EmbededVolDescription *desc;
RcdError( GPtr, type );
p = AllocMinorRepairOrder( GPtr, sizeof(EmbededVolDescription) ); if ( p == nil ) return( R_NoMem );
p->type = type; desc = (EmbededVolDescription *) &(p->name);
desc->drAlBlSt = mdb->drAlBlSt;
desc->drEmbedSigWord = mdb->drEmbedSigWord;
desc->drEmbedExtent.startBlock = mdb->drEmbedExtent.startBlock;
desc->drEmbedExtent.blockCount = mdb->drEmbedExtent.blockCount;
GPtr->VIStat |= S_InvalidWrapperExtents;
return( noErr ); }
static OSErr RcdInvalidWrapperExtents( SGlobPtr GPtr, OSErr type )
{
RepairOrderPtr p;
RcdError( GPtr, type );
p = AllocMinorRepairOrder( GPtr, 0 ); if ( p == nil ) return( R_NoMem );
p->type = type;
GPtr->VIStat |= S_BadMDBdrAlBlSt;
return( noErr ); }
#if(0) // We just check and fix them in SRepair.c
static OSErr RcdOrphanedExtentErr ( SGlobPtr GPtr, SInt16 type, void *theKey )
{
RepairOrderPtr p;
SInt16 n;
RcdError( GPtr,type );
if ( GPtr->isHFSPlus )
n = sizeof( HFSPlusExtentKey );
else
n = sizeof( HFSExtentKey );
p = AllocMinorRepairOrder( GPtr, n );
if ( p == NULL )
return( R_NoMem );
CopyMemory( theKey, p->name, n );
p->type = type;
GPtr->EBTStat |= S_OrphanedExtent;
return( noErr );
}
#endif
OSErr VInfoChk( SGlobPtr GPtr )
{
OSErr result;
UInt32 hint;
UInt32 maxClump;
SVCB *vcb;
CatalogRecord record;
CatalogKey foundKey;
UInt16 recSize;
Boolean isHFSPlus = GPtr->isHFSPlus;
BlockDescriptor altBlock;
BlockDescriptor priBlock;
vcb = GPtr->calculatedVCB;
altBlock.buffer = priBlock.buffer = NULL;
result = GetBTreeRecord( GPtr->calculatedCatalogFCB, 0x8001, &foundKey, &record, &recSize, &hint );
GPtr->TarID = kHFSCatalogFileID;
GPtr->TarBlock = hint;
if ( result != noErr )
{
result = IntError( GPtr, result );
return( result );
}
result = GetVolumeBlock(vcb, GPtr->idSector, kGetBlock, &altBlock);
ReturnIfError(result);
if ( isHFSPlus )
{
HFSPlusVolumeHeader *volumeHeader;
HFSPlusVolumeHeader *alternateVolumeHeader;
GPtr->TarID = AMDB_FNum; GPtr->TarBlock = GPtr->idSector;
alternateVolumeHeader = (HFSPlusVolumeHeader *) altBlock.buffer;
GPtr->TarID = MDB_FNum;
GPtr->TarBlock = MDB_BlkN;
if ( GPtr->idSector == (vcb->vcbEmbeddedOffset/512)+2) {
volumeHeader = alternateVolumeHeader;
} else {
result = GetVolumeBlock(vcb, (vcb->vcbEmbeddedOffset/512)+2, kGetBlock, &priBlock);
if (result) goto exit;
volumeHeader = (HFSPlusVolumeHeader *) priBlock.buffer;
}
maxClump = (vcb->vcbTotalBlocks / 4) * vcb->vcbBlockSize;
vcb->vcbCreateDate = alternateVolumeHeader->createDate; vcb->vcbModifyDate = volumeHeader->modifyDate; vcb->vcbCheckedDate = volumeHeader->checkedDate;
if ( ((UInt16)volumeHeader->attributes & VAtrb_Msk) == 0 )
vcb->vcbAttributes = (UInt16)volumeHeader->attributes;
else
vcb->vcbAttributes = VAtrb_DFlt;
if ( volumeHeader->nextAllocation < vcb->vcbTotalBlocks )
vcb->vcbNextAllocation = volumeHeader->nextAllocation;
else
vcb->vcbNextAllocation = 0;
if ( (volumeHeader->rsrcClumpSize > 0) && (volumeHeader->rsrcClumpSize <= kMaxClumpSize) && ((volumeHeader->rsrcClumpSize % vcb->vcbBlockSize) == 0) )
vcb->vcbRsrcClumpSize = volumeHeader->rsrcClumpSize;
else if ( (alternateVolumeHeader->rsrcClumpSize > 0) && (alternateVolumeHeader->rsrcClumpSize <= kMaxClumpSize) && ((alternateVolumeHeader->rsrcClumpSize % vcb->vcbBlockSize) == 0) )
vcb->vcbRsrcClumpSize = alternateVolumeHeader->rsrcClumpSize;
else
vcb->vcbRsrcClumpSize = 4 * vcb->vcbBlockSize;
if ( vcb->vcbRsrcClumpSize > kMaxClumpSize )
vcb->vcbRsrcClumpSize = vcb->vcbBlockSize;
if ( (volumeHeader->dataClumpSize > 0) && (volumeHeader->dataClumpSize <= kMaxClumpSize) && ((volumeHeader->dataClumpSize % vcb->vcbBlockSize) == 0) )
vcb->vcbDataClumpSize = volumeHeader->dataClumpSize;
else if ( (alternateVolumeHeader->dataClumpSize > 0) && (alternateVolumeHeader->dataClumpSize <= kMaxClumpSize) && ((alternateVolumeHeader->dataClumpSize % vcb->vcbBlockSize) == 0) )
vcb->vcbDataClumpSize = alternateVolumeHeader->dataClumpSize;
else
vcb->vcbDataClumpSize = 4 * vcb->vcbBlockSize;
if ( vcb->vcbDataClumpSize > kMaxClumpSize )
vcb->vcbDataClumpSize = vcb->vcbBlockSize;
if ( (volumeHeader->nextCatalogID > vcb->vcbNextCatalogID) && (volumeHeader->nextCatalogID <= (vcb->vcbNextCatalogID + 4096)) )
vcb->vcbNextCatalogID = volumeHeader->nextCatalogID;
result = ChkCName( GPtr, (const CatalogName*) &foundKey.hfsPlus.nodeName, isHFSPlus );
vcb->vcbBackupDate = volumeHeader->backupDate;
vcb->vcbWriteCount = volumeHeader->writeCount;
if ( ((volumeHeader->extentsFile.clumpSize % vcb->vcbBlockSize) == 0) && (volumeHeader->extentsFile.clumpSize <= maxClump) )
vcb->vcbExtentsFile->fcbClumpSize = volumeHeader->extentsFile.clumpSize;
else if ( ((alternateVolumeHeader->extentsFile.clumpSize % vcb->vcbBlockSize) == 0) && (alternateVolumeHeader->extentsFile.clumpSize <= maxClump) )
vcb->vcbExtentsFile->fcbClumpSize = alternateVolumeHeader->extentsFile.clumpSize;
else
vcb->vcbExtentsFile->fcbClumpSize = (alternateVolumeHeader->extentsFile.extents[0].blockCount * vcb->vcbBlockSize);
if ( ((volumeHeader->catalogFile.clumpSize % vcb->vcbBlockSize) == 0) && (volumeHeader->catalogFile.clumpSize <= maxClump) )
vcb->vcbCatalogFile->fcbClumpSize = volumeHeader->catalogFile.clumpSize;
else if ( ((alternateVolumeHeader->catalogFile.clumpSize % vcb->vcbBlockSize) == 0) && (alternateVolumeHeader->catalogFile.clumpSize <= maxClump) )
vcb->vcbCatalogFile->fcbClumpSize = alternateVolumeHeader->catalogFile.clumpSize;
else
vcb->vcbCatalogFile->fcbClumpSize = (alternateVolumeHeader->catalogFile.extents[0].blockCount * vcb->vcbBlockSize);
if ( ((volumeHeader->allocationFile.clumpSize % vcb->vcbBlockSize) == 0) && (volumeHeader->allocationFile.clumpSize <= maxClump) )
vcb->vcbAllocationFile->fcbClumpSize = volumeHeader->allocationFile.clumpSize;
else if ( ((alternateVolumeHeader->allocationFile.clumpSize % vcb->vcbBlockSize) == 0) && (alternateVolumeHeader->allocationFile.clumpSize <= maxClump) )
vcb->vcbAllocationFile->fcbClumpSize = alternateVolumeHeader->allocationFile.clumpSize;
else
vcb->vcbAllocationFile->fcbClumpSize = (alternateVolumeHeader->allocationFile.extents[0].blockCount * vcb->vcbBlockSize);
CopyMemory( volumeHeader->finderInfo, vcb->vcbFinderInfo, sizeof(vcb->vcbFinderInfo) );
result = CompareVolumeHeader( GPtr, volumeHeader );
}
else {
HFSMasterDirectoryBlock *mdbP;
HFSMasterDirectoryBlock *alternateMDB;
GPtr->TarID = AMDB_FNum;
GPtr->TarBlock = GPtr->idSector;
alternateMDB = (HFSMasterDirectoryBlock *) altBlock.buffer;
GPtr->TarID = MDB_FNum;
GPtr->TarBlock = MDB_BlkN;
if (GPtr->idSector == MDB_BlkN) {
mdbP = alternateMDB;
} else {
result = GetVolumeBlock(vcb, MDB_BlkN, kGetBlock, &priBlock);
if (result) goto exit;
mdbP = (HFSMasterDirectoryBlock *) priBlock.buffer;
}
maxClump = (vcb->vcbTotalBlocks / 4) * vcb->vcbBlockSize;
vcb->vcbCreateDate = alternateMDB->drCrDate;
vcb->vcbModifyDate = mdbP->drLsMod;
if ( (mdbP->drAtrb & VAtrb_Msk) == 0 )
vcb->vcbAttributes = mdbP->drAtrb;
else
vcb->vcbAttributes = VAtrb_DFlt;
if ( mdbP->drAllocPtr < vcb->vcbTotalBlocks )
vcb->vcbNextAllocation = mdbP->drAllocPtr;
else
vcb->vcbNextAllocation = 0;
if ( (mdbP->drClpSiz > 0) && (mdbP->drClpSiz <= maxClump) && ((mdbP->drClpSiz % vcb->vcbBlockSize) == 0) )
vcb->vcbDataClumpSize = mdbP->drClpSiz;
else if ( (alternateMDB->drClpSiz > 0) && (alternateMDB->drClpSiz <= maxClump) && ((alternateMDB->drClpSiz % vcb->vcbBlockSize) == 0) )
vcb->vcbDataClumpSize = alternateMDB->drClpSiz;
else
vcb->vcbDataClumpSize = 4 * vcb->vcbBlockSize;
if ( vcb->vcbDataClumpSize > kMaxClumpSize )
vcb->vcbDataClumpSize = vcb->vcbBlockSize;
if ( (mdbP->drNxtCNID > vcb->vcbNextCatalogID) && (mdbP->drNxtCNID <= (vcb->vcbNextCatalogID + 4096)) )
vcb->vcbNextCatalogID = mdbP->drNxtCNID;
result = ChkCName( GPtr, (const CatalogName*) &vcb->vcbVN, isHFSPlus );
if ( result == noErr )
if ( CmpBlock( mdbP->drVN, vcb->vcbVN, vcb->vcbVN[0] + 1 ) == 0 )
CopyMemory( mdbP->drVN, vcb->vcbVN, kHFSMaxVolumeNameChars + 1 );
vcb->vcbBackupDate = mdbP->drVolBkUp;
vcb->vcbVSeqNum = mdbP->drVSeqNum;
vcb->vcbWriteCount = mdbP->drWrCnt;
if ( ((mdbP->drXTClpSiz % vcb->vcbBlockSize) == 0) && (mdbP->drXTClpSiz <= maxClump) )
vcb->vcbExtentsFile->fcbClumpSize = mdbP->drXTClpSiz;
else if ( ((alternateMDB->drXTClpSiz % vcb->vcbBlockSize) == 0) && (alternateMDB->drXTClpSiz <= maxClump) )
vcb->vcbExtentsFile->fcbClumpSize = alternateMDB->drXTClpSiz;
else
vcb->vcbExtentsFile->fcbClumpSize = (alternateMDB->drXTExtRec[0].blockCount * vcb->vcbBlockSize);
if ( ((mdbP->drCTClpSiz % vcb->vcbBlockSize) == 0) && (mdbP->drCTClpSiz <= maxClump) )
vcb->vcbCatalogFile->fcbClumpSize = mdbP->drCTClpSiz;
else if ( ((alternateMDB->drCTClpSiz % vcb->vcbBlockSize) == 0) && (alternateMDB->drCTClpSiz <= maxClump) )
vcb->vcbCatalogFile->fcbClumpSize = alternateMDB->drCTClpSiz;
else
vcb->vcbCatalogFile->fcbClumpSize = (alternateMDB->drCTExtRec[0].blockCount * vcb->vcbBlockSize);
CopyMemory(mdbP->drFndrInfo, vcb->vcbFinderInfo, sizeof(mdbP->drFndrInfo));
result = CmpMDB( GPtr, mdbP);
}
exit:
if (priBlock.buffer)
(void) ReleaseVolumeBlock(vcb, &priBlock, kReleaseBlock);
if (altBlock.buffer)
(void) ReleaseVolumeBlock(vcb, &altBlock, kReleaseBlock);
return (result);
}
OSErr VLockedChk( SGlobPtr GPtr )
{
UInt32 hint;
CatalogKey foundKey;
CatalogRecord record;
UInt16 recSize;
OSErr result;
UInt16 frFlags;
Boolean isHFSPlus = GPtr->isHFSPlus;
SVCB *calculatedVCB = GPtr->calculatedVCB;
GPtr->TarID = kHFSCatalogFileID;
GPtr->TarBlock = 0;
result = GetBTreeRecord( GPtr->calculatedCatalogFCB, 0x8001, &foundKey, &record, &recSize, &hint );
if ( result)
{
RcdError( GPtr, E_EntryNotFound );
return( E_EntryNotFound );
}
if ( isHFSPlus == false )
{
CopyMemory( foundKey.hfs.nodeName, calculatedVCB->vcbVN, sizeof(calculatedVCB->vcbVN) );
}
else if ( GPtr->volumeType != kPureHFSPlusVolumeType )
{
HFSMasterDirectoryBlock *mdbP;
BlockDescriptor block;
result = GetVolumeBlock(calculatedVCB, MDB_BlkN, kGetBlock, &block);
ReturnIfError(result);
mdbP = (HFSMasterDirectoryBlock *) block.buffer;
CopyMemory( mdbP->drVN, calculatedVCB->vcbVN, sizeof(mdbP->drVN) );
(void) ReleaseVolumeBlock(calculatedVCB, &block, kReleaseBlock);
}
else {
CopyMemory( "\x0dPure HFS Plus", calculatedVCB->vcbVN, sizeof(Str27) );
}
GPtr->TarBlock = hint;
if ( isHFSPlus )
CopyCatalogName( (const CatalogName *)&foundKey.hfsPlus.nodeName, &GPtr->CName, isHFSPlus );
else
CopyCatalogName( (const CatalogName *)&foundKey.hfs.nodeName, &GPtr->CName, isHFSPlus );
if ( (record.recordType == kHFSPlusFolderRecord) || (record.recordType == kHFSFolderRecord) )
{
frFlags = record.recordType == kHFSPlusFolderRecord ? record.hfsPlusFolder.userInfo.frFlags : record.hfsFolder.userInfo.frFlags;
if ( frFlags & fNameLocked ) RcdNameLockedErr( GPtr, E_LockedDirName, frFlags );
}
return( noErr );
}
static int RcdNameLockedErr( SGlobPtr GPtr, SInt16 type, UInt32 incorrect )
{
RepairOrderPtr p;
int n;
RcdError( GPtr, type );
n = CatalogNameSize( &GPtr->CName, GPtr->isHFSPlus );
p = AllocMinorRepairOrder( GPtr, n );
if ( p==NULL )
return ( R_NoMem );
CopyCatalogName( (const CatalogName *) &GPtr->CName, (CatalogName*)&p->name, GPtr->isHFSPlus );
p->type = type;
p->correct = incorrect & ~fNameLocked;
p->incorrect = incorrect;
p->maskBit = (UInt16)fNameLocked;
p->parid = 1;
GPtr->CatStat |= S_LockedDirName;
return( noErr );
}
OSErr CheckFileExtents( SGlobPtr GPtr, UInt32 fileNumber, UInt8 forkType, const void *extents, UInt32 *blocksUsed )
{
UInt32 blockCount;
UInt32 extentBlockCount;
UInt32 extentStartBlock;
UInt32 hint;
HFSPlusExtentKey key;
HFSPlusExtentKey extentKey;
HFSPlusExtentRecord extentRecord;
UInt16 recSize;
OSErr err;
SInt16 i;
Boolean firstRecord;
Boolean isHFSPlus;
isHFSPlus = GPtr->isHFSPlus;
firstRecord = true;
err = noErr;
blockCount = 0;
while ( (extents != nil) && (err == noErr) )
{
err = ChkExtRec( GPtr, extents ); if ( err != noErr ) break;
for ( i=0 ; i<GPtr->numExtents ; i++ ) {
if ( isHFSPlus == true )
{
extentBlockCount = ((HFSPlusExtentDescriptor *)extents)[i].blockCount;
extentStartBlock = ((HFSPlusExtentDescriptor *)extents)[i].startBlock;
}
else
{
extentBlockCount = ((HFSExtentDescriptor *)extents)[i].blockCount;
extentStartBlock = ((HFSExtentDescriptor *)extents)[i].startBlock;
}
if ( extentBlockCount == 0 )
break;
err = CaptureBitmapBits(extentStartBlock, extentBlockCount);
if (err == E_OvlExt)
err = AddExtentToOverlapList(GPtr, fileNumber, extentStartBlock, extentBlockCount, forkType);
blockCount += extentBlockCount;
}
if ( fileNumber == kHFSExtentsFileID ) break;
if ( firstRecord == true )
{
firstRecord = false;
BuildExtentKey( isHFSPlus, forkType, fileNumber, blockCount, (void *)&key );
err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, &key, kNoHint, (void *) &extentKey, (void *) &extentRecord, &recSize, &hint );
if ( err == btNotFound )
{
err = noErr; extents = nil;
break;
}
else if ( err != noErr )
{
err = IntError( GPtr, err ); return( err );
}
}
else
{
err = GetBTreeRecord( GPtr->calculatedExtentsFCB, 1, &extentKey, extentRecord, &recSize, &hint );
if ( err == btNotFound )
{
err = noErr; extents = nil;
break;
}
else if ( err != noErr )
{
err = IntError( GPtr, err );
return( err );
}
if ( isHFSPlus )
{
if ( (extentKey.fileID != fileNumber) || (extentKey.forkType != forkType) )
break;
}
else
{
if ( (((HFSExtentKey *) &extentKey)->fileID != fileNumber) || (((HFSExtentKey *) &extentKey)->forkType != forkType) )
break;
}
}
extents = (void *) &extentRecord;
}
*blocksUsed = blockCount;
return( err );
}
void BuildExtentKey( Boolean isHFSPlus, UInt8 forkType, HFSCatalogNodeID fileNumber, UInt32 blockNumber, void * key )
{
if ( isHFSPlus )
{
HFSPlusExtentKey *hfsPlusKey = (HFSPlusExtentKey*) key;
hfsPlusKey->keyLength = kHFSPlusExtentKeyMaximumLength;
hfsPlusKey->forkType = forkType;
hfsPlusKey->pad = 0;
hfsPlusKey->fileID = fileNumber;
hfsPlusKey->startBlock = blockNumber;
}
else
{
HFSExtentKey *hfsKey = (HFSExtentKey*) key;
hfsKey->keyLength = kHFSExtentKeyMaximumLength;
hfsKey->forkType = forkType;
hfsKey->fileID = fileNumber;
hfsKey->startBlock = (UInt16) blockNumber;
}
}
OSErr AddExtentToOverlapList( SGlobPtr GPtr, HFSCatalogNodeID fileNumber, UInt32 extentStartBlock, UInt32 extentBlockCount, UInt8 forkType )
{
UInt32 newHandleSize;
ExtentInfo extentInfo;
ExtentsTable **extentsTableH;
char fileno[32];
sprintf(fileno, "%ud", fileNumber);
PrintError(GPtr, E_OvlExt, 1, fileno);
GPtr->VIStat |= S_OverlappingExtents;
extentInfo.fileNumber = fileNumber;
extentInfo.startBlock = extentStartBlock;
extentInfo.blockCount = extentBlockCount;
extentInfo.forkType = forkType;
if ( GPtr->overlappedExtents == nil )
{
GPtr->overlappedExtents = (ExtentsTable **) NewHandleClear( sizeof(ExtentsTable) );
extentsTableH = GPtr->overlappedExtents;
}
else
{
extentsTableH = GPtr->overlappedExtents;
if ( ExtentInfoExists( extentsTableH, &extentInfo ) == true )
return( noErr );
newHandleSize = ( sizeof(ExtentInfo) ) + ( GetHandleSize( (Handle)extentsTableH ) );
SetHandleSize( (Handle)extentsTableH, newHandleSize );
}
CopyMemory( &extentInfo, &((**extentsTableH).extentInfo[(**extentsTableH).count]), sizeof(ExtentInfo) );
(**extentsTableH).count++;
return( noErr );
}
static Boolean ExtentInfoExists( ExtentsTable **extentsTableH, ExtentInfo *extentInfo )
{
UInt32 i;
ExtentInfo *aryExtentInfo;
for ( i = 0 ; i < (**extentsTableH).count ; i++ )
{
aryExtentInfo = &((**extentsTableH).extentInfo[i]);
if ( extentInfo->fileNumber == aryExtentInfo->fileNumber )
{
if ( (extentInfo->startBlock == aryExtentInfo->startBlock) &&
(extentInfo->blockCount == aryExtentInfo->blockCount) &&
(extentInfo->forkType == aryExtentInfo->forkType) )
{
return( true );
}
}
}
return( false );
}