#include "Scavenger.h"
enum {
clearBlocks,
addBitmapBit,
deleteExtents
};
void SetOffset (void *buffer, UInt16 btNodeSize, SInt16 recOffset, SInt16 vecOffset);
#define SetOffset(buffer,nodesize,offset,record) (*(SInt16 *) ((Byte *) (buffer) + (nodesize) + (-2 * (record))) = (offset))
static OSErr UpdateBTreeHeader( SFCB * fcbPtr );
static OSErr FixBTreeHeaderReservedFields( SGlobPtr GPtr, short refNum );
static OSErr UpdBTM( SGlobPtr GPtr, short refNum);
static OSErr UpdateVolumeBitMap( SGlobPtr GPtr, Boolean preAllocateOverlappedExtents );
static OSErr DoMinorOrders( SGlobPtr GPtr );
static OSErr UpdVal( SGlobPtr GPtr, RepairOrderPtr rP );
static int DelFThd( SGlobPtr GPtr, UInt32 fid );
static OSErr FixDirThread( SGlobPtr GPtr, UInt32 did );
static OSErr FixOrphanedFiles ( SGlobPtr GPtr );
static OSErr RepairReservedBTreeFields ( SGlobPtr GPtr );
static OSErr RebuildBTree( SGlobPtr GPtr, SInt16 refNum );
static OSErr FixFinderFlags( SGlobPtr GPtr, RepairOrderPtr p );
static OSErr FixLinkCount( SGlobPtr GPtr, RepairOrderPtr p );
static OSErr FixBSDInfo( SGlobPtr GPtr, RepairOrderPtr p );
static OSErr DeleteUnlinkedFile( SGlobPtr GPtr, RepairOrderPtr p );
static OSErr FixOrphanedExtent( SGlobPtr GPtr );
static OSErr FixFileSize(SGlobPtr GPtr, RepairOrderPtr p);
static OSErr CreateAndOpenRepairBtree( SGlobPtr GPtr, SInt16 refNum, SInt16 *newBTreeRefNum );
static OSErr CreateMapNodes( UInt32 firstMapNode, UInt32 numberOfNewMapNodes, UInt16 nodeSize );
extern OSErr FindExtentRecord( const SVCB *vcb, UInt8 forkType, UInt32 fileID, UInt32 startBlock, Boolean allowPrevious, HFSPlusExtentKey *foundKey, HFSPlusExtentRecord foundData, UInt32 *foundHint);
extern OSErr DeleteExtentRecord( const SVCB *vcb, UInt8 forkType, UInt32 fileID, UInt32 startBlock );
OSErr GetFCBExtentRecord( const SVCB *vcb, const SFCB *fcb, HFSPlusExtentRecord extents );
static OSErr DeleteFilesOverflowExtents( SGlobPtr GPtr, SFCB *fcb );
static OSErr MoveExtent( SGlobPtr GPtr, ExtentInfo *extentInfo ); static Boolean ReplaceStartBlock( SGlobPtr GPtr, ExtentRecord *extentRecord, UInt32 originalStartBlock, UInt32 newStartBlock );
static OSErr ScanForCatalogRecord( SGlobPtr GPtr, HFSCatalogNodeID fileID, SInt16 recordType, CatalogKey **foundCatalogKeyH, CatalogRecord **foundCatalogRecordH, UInt16 *recordSize );
static OSErr ScanForExtentRecord( SGlobPtr GPtr, UInt32 startBlock, HFSCatalogNodeID fileID, UInt8 forkType, ExtentKey **foundExtentKeyH, ExtentRecord **foundExtentRecordH, UInt16 *recordSize );
static OSErr FixOverlappingExtents( SGlobPtr GPtr );
static void InsertIdentifier( SGlobPtr GPtr, FileIdentifier *fileIdentifier );
static Boolean IdentifierExists( FileIdentifierTable **fileIdentifierTable, HFSCatalogNodeID fileID );
static OSErr CreateFileIdentifiers( SGlobPtr GPtr );
OSErr CopyDiskBlocks( SGlobPtr GPtr, UInt32 startAllocationBlock, UInt32 blockCount, UInt32 newStartAllocationBlock );
static OSErr FixBloatedThreadRecords( SGlob *GPtr );
static OSErr FixMissingThreadRecords( SGlob *GPtr );
static OSErr FixEmbededVolDescription( SGlobPtr GPtr, RepairOrderPtr p );
static OSErr FixWrapperExtents( SGlobPtr GPtr, RepairOrderPtr p );
OSErr RepairVolume( SGlobPtr GPtr )
{
OSErr err;
OSErr unmountResult;
Boolean volumeMounted;
SetDFAStage( kAboutToRepairStage ); err = CheckForStop( GPtr ); ReturnIfError( err );
volumeMounted = ( GPtr->volumeFeatures & volumeIsMountedMask );
unmountResult = fBsyErr;
if ( volumeMounted ) {
unmountResult = fBsyErr;
err = GetVolumeFeatures( GPtr );
if ( unmountResult != noErr )
{
#if 0
if ( GPtr->volumeFeatures & supportsTrashVolumeCacheFeatureMask )
{
ParamBlockRec pb;
ClearMemory( &pb, sizeof(ParamBlockRec) );
pb.volumeParam.ioVRefNum = GPtr->realVCB->vcbVRefNum;
PBHTrashVolumeCachesSync( &pb ); }
#endif
SetDFAStage( kRepairStage );
#if 0
err = FlushVol( NULL, GPtr->realVCB->vcbVRefNum );
MarkFileSystemBusy();
if ( (GPtr->volumeFeatures & supportsTrashVolumeCacheFeatureMask) == 0 )
{
if ( (GPtr->volumeFeatures & supportsHFSPlusVolsFeatureMask) != 0 )
{
TrashAllFSCaches( GPtr->realVCB );
}
else {
TrashVolumeDiskCache( GPtr->realVCB );
}
}
#endif
}
}
SetDFAStage( kRepairStage );
err = MRepair( GPtr );
#if 0
if ( volumeMounted ) {
if ( unmountResult != noErr ) {
OSErr updateErr = UpdateInMemoryStructures( GPtr );
if ( err == noErr )
err = updateErr;
}
{
ParamBlockRec theParam;
theParam.ioParam.ioCompletion = nil;
theParam.ioParam.ioNamePtr = nil;
theParam.ioParam.ioVRefNum = GPtr->DrvNum;
}
}
#endif
return( err );
}
int MRepair( SGlobPtr GPtr )
{
OSErr err;
SVCB *calculatedVCB = GPtr->calculatedVCB;
Boolean isHFSPlus = GPtr->isHFSPlus;
#if 0
RebuildBtrees:
if ( rebuildErr == noErr )
{
if ( GPtr->EBTStat & S_RebuildBTree )
{
rebuildErr = RebuildBTree( GPtr, kCalculatedExtentRefNum );
GPtr->EBTStat &= ~S_RebuildBTree;
}
if ( GPtr->CBTStat & S_RebuildBTree )
{
rebuildErr = RebuildBTree( GPtr, kCalculatedCatalogRefNum );
GPtr->CBTStat &= ~S_RebuildBTree;
}
if ( GPtr->ABTStat & S_RebuildBTree )
{
rebuildErr = RebuildBTree( GPtr, kCalculatedAttributesRefNum );
GPtr->ABTStat &= ~S_RebuildBTree;
}
}
#endif
err = DoMinorOrders( GPtr );
ReturnIfError( err );
err = CheckForStop( GPtr ); ReturnIfError( err );
GPtr->CatStat &= ~(S_FileAllocation | S_Permissions | S_UnlinkedFile | S_LinkCount);
if (GPtr->CatStat & S_MissingThread) {
err = FixMissingThreadRecords(GPtr);
ReturnIfError(err);
GPtr->CatStat &= ~S_MissingThread;
GPtr->CBTStat |= S_BTH;
}
if ( GPtr->VeryMinorErrorsStat & S_BloatedThreadRecordFound )
{
(void) FixBloatedThreadRecords( GPtr );
GPtr->VeryMinorErrorsStat &= ~S_BloatedThreadRecordFound;
}
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->VIStat & S_MDB) != 0 ) {
MarkVCBDirty( calculatedVCB );
calculatedVCB->vcbAttributes |= kHFSVolumeUnmountedMask;
err = FlushAlternateVolumeControlBlock( calculatedVCB, isHFSPlus ); ReturnIfError( err );
}
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->VIStat & S_OverlappingExtents) != 0 )
{
err = FixOverlappingExtents( GPtr ); ReturnIfError( err );
GPtr->VIStat &= ~S_OverlappingExtents;
GPtr->VIStat |= S_VBM; InvalidateCalculatedVolumeBitMap( GPtr ); }
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->VIStat & S_VBM) != 0 )
{
err = UpdateVolumeBitMap( GPtr, false ); ReturnIfError( err );
InvalidateCalculatedVolumeBitMap( GPtr ); }
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->CBTStat & S_Orphan) != 0 ) {
err = FixOrphanedFiles ( GPtr );
ReturnIfError( err );
}
if ( (GPtr->EBTStat & S_OrphanedExtent) != 0 ) {
err = FixOrphanedExtent( GPtr );
GPtr->EBTStat &= ~S_OrphanedExtent;
ReturnIfError( err );
}
err = CheckForStop( GPtr ); ReturnIfError( err );
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->EBTStat & S_BTH) || (GPtr->EBTStat & S_ReservedBTH) )
{
err = UpdateBTreeHeader( GPtr->calculatedExtentsFCB );
if ( (err == noErr) && (GPtr->EBTStat & S_ReservedBTH) )
{
err = FixBTreeHeaderReservedFields( GPtr, kCalculatedExtentRefNum );
}
ReturnIfError( err );
}
if ( (GPtr->EBTStat & S_BTM) != 0 )
{
err = UpdBTM( GPtr, kCalculatedExtentRefNum ); ReturnIfError( err );
}
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->CBTStat & S_BTH) || (GPtr->CBTStat & S_ReservedBTH) )
{
err = UpdateBTreeHeader( GPtr->calculatedCatalogFCB );
if ( (err == noErr) && (GPtr->CBTStat & S_ReservedBTH) )
{
err = FixBTreeHeaderReservedFields( GPtr, kCalculatedCatalogRefNum );
}
ReturnIfError( err );
}
if ( GPtr->CBTStat & S_BTM )
{
err = UpdBTM( GPtr, kCalculatedCatalogRefNum ); ReturnIfError( err );
}
if ( (GPtr->CBTStat & S_ReservedNotZero) != 0 )
{
err = RepairReservedBTreeFields( GPtr ); ReturnIfError( err );
}
err = CheckForStop( GPtr ); ReturnIfError( err );
return( err ); }
static OSErr UpdateBTreeHeader( SFCB * fcbPtr )
{
OSErr err;
M_BTreeHeaderDirty( ((BTreeControlBlockPtr) fcbPtr->fcbBtree) );
err = BTFlushPath( fcbPtr );
return( err );
}
static OSErr FixBTreeHeaderReservedFields( SGlobPtr GPtr, short refNum )
{
OSErr err;
BTHeaderRec header;
err = GetBTreeHeader(GPtr, ResolveFCB(refNum), &header);
ReturnIfError( err );
if ( (header.clumpSize % GPtr->calculatedVCB->vcbBlockSize) != 0 )
header.clumpSize = GPtr->calculatedVCB->vcbBlockSize;
header.reserved1 = 0;
header.btreeType = kHFSBTreeType; header.reserved2 = 0;
ClearMemory( header.reserved3, sizeof(header.reserved3) );
return( err );
}
static OSErr UpdBTM( SGlobPtr GPtr, short refNum )
{
OSErr err;
UInt16 recSize;
SInt16 mapSize;
SInt16 size;
SInt16 recIndx;
Ptr p;
Ptr btmP;
Ptr sbtmP;
UInt32 nodeNum;
NodeRec node;
UInt32 fLink;
BTreeControlBlock *calculatedBTCB = GetBTreeControlBlock( refNum );
mapSize = ((BTreeExtensionsRec*)calculatedBTCB->refCon)->BTCBMSize;
if ( mapSize > 0 )
{
nodeNum = 0;
recIndx = 2;
sbtmP = ((BTreeExtensionsRec*)calculatedBTCB->refCon)->BTCBMPtr;
do
{
GPtr->TarBlock = nodeNum;
err = GetNode( calculatedBTCB, nodeNum, &node );
ReturnIfError( err );
recSize = GetRecordSize( calculatedBTCB, (BTNodeDescriptor *)node.buffer, recIndx );
btmP = (Ptr)GetRecordAddress( calculatedBTCB, (BTNodeDescriptor *)node.buffer, recIndx );
fLink = ((NodeDescPtr)node.buffer)->fLink;
size = ( recSize > mapSize ) ? mapSize : recSize;
CopyMemory( sbtmP, btmP, size );
err = UpdateNode( calculatedBTCB, &node );
mapSize -= size; if ( mapSize == 0 ) break; if ( fLink == 0 ) {
RcdError( GPtr, E_ShortBTM );
(void) ReleaseNode(calculatedBTCB, &node);
return( E_ShortBTM );
}
nodeNum = fLink;
sbtmP += size;
recIndx = 0;
} while ( mapSize > 0 );
for ( p = btmP + size ; p < btmP + recSize ; p++ )
*p = 0;
err = UpdateNode( calculatedBTCB, &node ); }
return( noErr ); }
static OSErr UpdateVolumeBitMap( SGlobPtr GPtr, Boolean preAllocateOverlappedExtents )
{
GPtr->TarID = VBM_FNum;
return ( CheckVolumeBitMap(GPtr, true) );
}
static OSErr DoMinorOrders( SGlobPtr GPtr ) {
RepairOrderPtr p;
OSErr err = noErr;
while( (p = GPtr->MinorRepairsP) && (err == noErr) ) {
GPtr->MinorRepairsP = p->link;
switch( p->type ) {
case E_RtDirCnt: case E_RtFilCnt: case E_DirCnt:
case E_FilCnt:
case E_DirVal:
err = UpdVal( GPtr, p ); break;
case E_LockedDirName:
err = FixFinderFlags( GPtr, p );
break;
case E_UnlinkedFile:
err = DeleteUnlinkedFile( GPtr, p );
break;
case E_InvalidLinkCount:
err = FixLinkCount( GPtr, p );
break;
case E_InvalidPermissions:
case E_InvalidUID:
err = FixBSDInfo( GPtr, p );
break;
case E_NoFile: err = DelFThd( GPtr, p->parid ); break;
case E_EntryNotFound:
GPtr->EBTStat |= S_OrphanedExtent;
break;
case E_NoDir: err = FixDirThread( GPtr, p->parid ); break;
case E_InvalidMDBdrAlBlSt:
err = FixEmbededVolDescription( GPtr, p );
break;
case E_InvalidWrapperExtents:
err = FixWrapperExtents(GPtr, p);
break;
case E_PEOF:
case E_LEOF:
err = FixFileSize(GPtr, p);
break;
default: err = IntError( GPtr, R_IntErr ); break;
}
DisposeMemory( p ); }
return( err ); }
static int DelFThd( SGlobPtr GPtr, UInt32 fid ) {
CatalogRecord record;
CatalogKey foundKey;
CatalogKey key;
UInt32 hint; OSErr result; UInt16 recSize;
ExtentRecord zeroExtents;
BuildCatalogKey( fid, (const CatalogName*) nil, GPtr->isHFSPlus, &key );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint );
if ( result ) return ( IntError( GPtr, result ) );
if ( (record.recordType != kHFSFileThreadRecord) && (record.recordType != kHFSPlusFileThreadRecord) ) return ( IntError( GPtr, R_IntErr ) );
ClearMemory( (Ptr)&zeroExtents, sizeof(ExtentRecord) );
result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &key, hint, &zeroExtents, recSize, &hint );
if ( result ) return ( IntError( GPtr, result ) );
result = DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &key );
if ( result ) return ( IntError( GPtr, result ) );
GPtr->CBTStat |= S_BTH + S_BTM;
return( noErr ); }
static OSErr FixDirThread( SGlobPtr GPtr, UInt32 did ) {
UInt8 *dataPtr;
UInt32 hint; OSErr result; UInt16 recSize;
CatalogName catalogName; CatalogName *keyName; register short index; UInt32 curLeafNode; CatalogRecord record;
CatalogKey foundKey;
CatalogKey key;
CatalogKey *keyP;
SInt16 recordType;
UInt32 folderID;
NodeRec node;
NodeDescPtr nodeDescP;
UInt32 newParDirID = 0; Boolean isHFSPlus = GPtr->isHFSPlus;
BTreeControlBlock *calculatedBTCB = GetBTreeControlBlock( kCalculatedCatalogRefNum );
BuildCatalogKey( did, (const CatalogName*) nil, isHFSPlus, &key );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint );
if ( result )
return( IntError( GPtr, result ) );
if ( (record.recordType != kHFSFolderThreadRecord) && (record.recordType != kHFSPlusFolderThreadRecord) ) return ( IntError( GPtr, R_IntErr ) );
curLeafNode = calculatedBTCB->freeNodes;
while ( curLeafNode )
{
result = GetNode( calculatedBTCB, curLeafNode, &node );
if ( result != noErr ) return( IntError( GPtr, result ) );
nodeDescP = node.buffer;
for ( index = 0 ; index < nodeDescP->numRecords ; index++ )
{
GetRecordByIndex( calculatedBTCB, (NodeDescPtr)nodeDescP, index, (BTreeKey **)&keyP, &dataPtr, &recSize );
recordType = ((CatalogRecord *)dataPtr)->recordType;
folderID = recordType == kHFSPlusFolderRecord ? ((HFSPlusCatalogFolder *)dataPtr)->folderID : ((HFSCatalogFolder *)dataPtr)->folderID;
if ( (folderID == did) && ( recordType == kHFSPlusFolderRecord || recordType == kHFSFolderRecord ) )
{
newParDirID = recordType == kHFSPlusFolderRecord ? keyP->hfsPlus.parentID : keyP->hfs.parentID;
keyName = recordType == kHFSPlusFolderRecord ? (CatalogName *)&keyP->hfsPlus.nodeName : (CatalogName *)&keyP->hfs.nodeName;
CopyCatalogName( keyName, &catalogName, isHFSPlus );
break;
}
}
if ( newParDirID ) {
(void) ReleaseNode(calculatedBTCB, &node);
break;
}
curLeafNode = nodeDescP->fLink;
(void) ReleaseNode(calculatedBTCB, &node);
}
if ( newParDirID == 0 )
{
return ( IntError( GPtr, R_IntErr ) ); }
else
{
(void) SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint );
if ( isHFSPlus )
{
HFSPlusCatalogThread *largeCatalogThreadP = (HFSPlusCatalogThread *) &record;
largeCatalogThreadP->parentID = newParDirID;
CopyCatalogName( &catalogName, (CatalogName *) &largeCatalogThreadP->nodeName, isHFSPlus );
}
else
{
HFSCatalogThread *smallCatalogThreadP = (HFSCatalogThread *) &record;
smallCatalogThreadP->parentID = newParDirID;
CopyCatalogName( &catalogName, (CatalogName *)&smallCatalogThreadP->nodeName, isHFSPlus );
}
result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recSize, &hint );
}
return( noErr ); }
static OSErr UpdVal( SGlobPtr GPtr, RepairOrderPtr p ) {
OSErr result; UInt32 hint; UInt16 recSize;
CatalogRecord record;
CatalogKey foundKey;
CatalogKey key;
SVCB *calculatedVCB = GPtr->calculatedVCB;
switch( p->type )
{
case E_RtDirCnt: if ( (UInt16)p->incorrect != calculatedVCB->vcbNmRtDirs )
return ( IntError( GPtr, R_IntErr ) );
calculatedVCB->vcbNmRtDirs = (UInt16)p->correct;
GPtr->VIStat |= S_MDB;
break;
case E_RtFilCnt:
if ( (UInt16)p->incorrect != calculatedVCB->vcbNmFls )
return ( IntError( GPtr, R_IntErr ) );
calculatedVCB->vcbNmFls = (UInt16)p->correct;
GPtr->VIStat |= S_MDB;
break;
case E_DirCnt:
if ( (UInt32)p->incorrect != calculatedVCB->vcbFolderCount )
return ( IntError( GPtr, R_IntErr ) );
calculatedVCB->vcbFolderCount = (UInt32)p->correct;
GPtr->VIStat |= S_MDB;
break;
case E_FilCnt:
if ( (UInt32)p->incorrect != calculatedVCB->vcbFileCount )
return ( IntError( GPtr, R_IntErr ) );
calculatedVCB->vcbFileCount = (UInt32)p->correct;
GPtr->VIStat |= S_MDB;
break;
case E_DirVal:
BuildCatalogKey( p->parid, (CatalogName *)&p->name, GPtr->isHFSPlus, &key );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
&foundKey, &record, &recSize, &hint );
if ( result )
return ( IntError( GPtr, result ) );
if ( record.recordType == kHFSPlusFolderRecord )
{
if ( (UInt32)p->incorrect != record.hfsPlusFolder.valence)
return ( IntError( GPtr, R_IntErr ) );
record.hfsPlusFolder.valence = (UInt32)p->correct;
}
else
{
if ( (UInt16)p->incorrect != record.hfsFolder.valence )
return ( IntError( GPtr, R_IntErr ) );
record.hfsFolder.valence = (UInt16)p->correct;
}
result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &key, hint,\
&record, recSize, &hint );
if ( result )
return ( IntError( GPtr, result ) );
break;
}
return( noErr ); }
static OSErr FixFinderFlags( SGlobPtr GPtr, RepairOrderPtr p ) {
CatalogRecord record;
CatalogKey foundKey;
CatalogKey key;
UInt32 hint; OSErr result; UInt16 recSize;
BuildCatalogKey( p->parid, (CatalogName *)&p->name, GPtr->isHFSPlus, &key );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint );
if ( result )
return ( IntError( GPtr, result ) );
if ( record.recordType == kHFSPlusFolderRecord )
{
HFSPlusCatalogFolder *largeCatalogFolderP = (HFSPlusCatalogFolder *) &record;
if ( (UInt16) p->incorrect != largeCatalogFolderP->userInfo.frFlags )
{
if ( p->correct < p->incorrect )
largeCatalogFolderP->userInfo.frFlags &= ~((UInt16)p->maskBit);
else
largeCatalogFolderP->userInfo.frFlags |= (UInt16)p->maskBit;
}
else
{
largeCatalogFolderP->userInfo.frFlags = (UInt16)p->correct;
}
}
else
{
HFSCatalogFolder *smallCatalogFolderP = (HFSCatalogFolder *) &record;
if ( p->incorrect != smallCatalogFolderP->userInfo.frFlags ) {
if ( p->correct < p->incorrect )
smallCatalogFolderP->userInfo.frFlags &= ~((UInt16)p->maskBit);
else
smallCatalogFolderP->userInfo.frFlags |= (UInt16)p->maskBit;
}
else
{
smallCatalogFolderP->userInfo.frFlags = (UInt16)p->correct;
}
}
result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recSize, &hint ); if ( result )
return( IntError( GPtr, result ) );
return( noErr ); }
static OSErr
FixLinkCount(SGlobPtr GPtr, RepairOrderPtr p)
{
SFCB *fcb;
CatalogRecord rec;
HFSPlusCatalogKey * keyp;
FSBufferDescriptor btRecord;
BTreeIterator btIterator;
size_t len;
OSErr result;
UInt16 recSize;
if (!GPtr->isHFSPlus)
return (0);
fcb = GPtr->calculatedCatalogFCB;
ClearMemory(&btIterator, sizeof(btIterator));
btIterator.hint.nodeNum = p->hint;
keyp = (HFSPlusCatalogKey*)&btIterator.key;
keyp->parentID = p->parid;
(void) utf_decodestr(&p->name[1], p->name[0], keyp->nodeName.unicode, &len);
keyp->nodeName.length = len / 2;
keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + len;
btRecord.bufferAddress = &rec;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof(rec);
result = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey,
&btRecord, &recSize, &btIterator);
if (result)
return (IntError(GPtr, result));
if (rec.recordType != kHFSPlusFileRecord)
return (noErr);
if ((UInt32)p->correct != rec.hfsPlusFile.bsdInfo.special.linkCount) {
if (GPtr->logLevel >= kDebugLog)
printf("\t%s: fixing link count from %d to %d\n",
&p->name[1], rec.hfsPlusFile.bsdInfo.special.linkCount, (int)p->correct);
rec.hfsPlusFile.bsdInfo.special.linkCount = (UInt32)p->correct;
result = BTReplaceRecord(fcb, &btIterator, &btRecord, recSize);
if (result)
return (IntError(GPtr, result));
}
return (noErr);
}
static OSErr
FixBSDInfo(SGlobPtr GPtr, RepairOrderPtr p)
{
SFCB *fcb;
CatalogRecord rec;
FSBufferDescriptor btRecord;
BTreeIterator btIterator;
OSErr result;
UInt16 recSize;
size_t namelen;
unsigned char filename[256];
if (!GPtr->isHFSPlus)
return (0);
fcb = GPtr->calculatedCatalogFCB;
ClearMemory(&btIterator, sizeof(btIterator));
btIterator.hint.nodeNum = p->hint;
BuildCatalogKey(p->parid, (CatalogName *)&p->name, true,
(CatalogKey*)&btIterator.key);
btRecord.bufferAddress = &rec;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof(rec);
result = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey,
&btRecord, &recSize, &btIterator);
if (result)
return (IntError(GPtr, result));
if (rec.recordType != kHFSPlusFileRecord &&
rec.recordType != kHFSPlusFolderRecord)
return (noErr);
utf_encodestr(((HFSUniStr255 *)&p->name)->unicode,
((HFSUniStr255 *)&p->name)->length << 1, filename, &namelen);
filename[namelen] = '\0';
if (p->type == E_InvalidPermissions &&
((UInt16)p->incorrect == rec.hfsPlusFile.bsdInfo.fileMode)) {
if (GPtr->logLevel >= kDebugLog)
printf("\t\"%s\": fixing mode from %07o to %07o\n",
filename, (int)p->incorrect, (int)p->correct);
rec.hfsPlusFile.bsdInfo.fileMode = (UInt16)p->correct;
result = BTReplaceRecord(fcb, &btIterator, &btRecord, recSize);
}
if (p->type == E_InvalidUID) {
if ((UInt32)p->incorrect == rec.hfsPlusFile.bsdInfo.ownerID) {
if (GPtr->logLevel >= kDebugLog) {
printf("\t\"%s\": replacing UID %d with %d\n",
filename, (int)p->incorrect, (int)p->correct);
}
rec.hfsPlusFile.bsdInfo.ownerID = (UInt32)p->correct;
}
if ((UInt32)p->incorrect == rec.hfsPlusFile.bsdInfo.groupID)
rec.hfsPlusFile.bsdInfo.groupID = (UInt32)p->correct;
result = BTReplaceRecord(fcb, &btIterator, &btRecord, recSize);
}
if (result)
return (IntError(GPtr, result));
else
return (noErr);
}
static OSErr
DeleteUnlinkedFile(SGlobPtr GPtr, RepairOrderPtr p)
{
CatalogName name;
CatalogName *cNameP;
size_t len;
if (!GPtr->isHFSPlus)
return (0);
if (p->name[0] > 0) {
(void) utf_decodestr(&p->name[1], p->name[0], name.ustr.unicode, &len);
name.ustr.length = len / 2;
cNameP = &name;
} else {
cNameP = NULL;
}
(void) DeleteCatalogNode(GPtr->calculatedVCB, p->parid, cNameP, p->hint);
GPtr->VIStat |= S_MDB;
return (noErr);
}
static OSErr
FixFileSize(SGlobPtr GPtr, RepairOrderPtr p)
{
SFCB *fcb;
CatalogRecord rec;
HFSPlusCatalogKey * keyp;
FSBufferDescriptor btRecord;
BTreeIterator btIterator;
size_t len;
Boolean replace;
OSErr result;
UInt16 recSize;
UInt64 bytes;
if (!GPtr->isHFSPlus)
return (0);
fcb = GPtr->calculatedCatalogFCB;
replace = false;
ClearMemory(&btIterator, sizeof(btIterator));
btIterator.hint.nodeNum = p->hint;
keyp = (HFSPlusCatalogKey*)&btIterator.key;
keyp->parentID = p->parid;
(void) utf_decodestr(&p->name[1], p->name[0], keyp->nodeName.unicode, &len);
keyp->nodeName.length = len / 2;
keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + len;
btRecord.bufferAddress = &rec;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof(rec);
result = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey,
&btRecord, &recSize, &btIterator);
if (result)
return (IntError(GPtr, result));
if (rec.recordType != kHFSPlusFileRecord)
return (noErr);
if (p->type == E_PEOF) {
bytes = p->correct * (UInt64)GPtr->calculatedVCB->vcbBlockSize;
if ((p->forkType == kRsrcFork) &&
((UInt32)p->incorrect == rec.hfsPlusFile.resourceFork.totalBlocks)) {
rec.hfsPlusFile.resourceFork.totalBlocks = (UInt32)p->correct;
replace = true;
if (rec.hfsPlusFile.resourceFork.logicalSize > bytes) {
rec.hfsPlusFile.resourceFork.logicalSize = bytes;
}
} else if ((p->forkType == kDataFork) &&
((UInt32)p->incorrect == rec.hfsPlusFile.dataFork.totalBlocks)) {
rec.hfsPlusFile.dataFork.totalBlocks = (UInt32)p->correct;
replace = true;
if (rec.hfsPlusFile.dataFork.logicalSize > bytes) {
rec.hfsPlusFile.dataFork.logicalSize = bytes;
}
}
} else {
if ((p->forkType == kRsrcFork) &&
(p->incorrect == rec.hfsPlusFile.resourceFork.logicalSize)) {
rec.hfsPlusFile.resourceFork.logicalSize = p->correct;
replace = true;
} else if ((p->forkType == kDataFork) &&
(p->incorrect == rec.hfsPlusFile.dataFork.logicalSize)) {
rec.hfsPlusFile.dataFork.logicalSize = p->correct;
replace = true;
}
}
if (replace) {
result = BTReplaceRecord(fcb, &btIterator, &btRecord, recSize);
if (result)
return (IntError(GPtr, result));
}
return (noErr);
}
static OSErr FixEmbededVolDescription( SGlobPtr GPtr, RepairOrderPtr p )
{
OSErr err;
UInt32 totalSectors;
UInt32 sectorSize;
HFSMasterDirectoryBlock *mdb;
EmbededVolDescription *desc;
SVCB *vcb = GPtr->calculatedVCB;
BlockDescriptor block;
desc = (EmbededVolDescription *) &(p->name);
err = GetDeviceSize( vcb->vcbDriveNumber, &totalSectors, §orSize );
ReturnIfError( err );
err = GetVolumeBlock(vcb, totalSectors - 2, kGetBlock, &block);
ReturnIfError( err );
mdb = (HFSMasterDirectoryBlock *) block.buffer;
mdb->drAlBlSt = desc->drAlBlSt;
mdb->drEmbedSigWord = desc->drEmbedSigWord;
mdb->drEmbedExtent.startBlock = desc->drEmbedExtent.startBlock;
mdb->drEmbedExtent.blockCount = desc->drEmbedExtent.blockCount;
err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
ReturnIfError( err );
err = GetVolumeBlock(vcb, 2, kGetBlock, &block);
ReturnIfError( err );
mdb = (HFSMasterDirectoryBlock *) block.buffer;
mdb->drAlBlSt = desc->drAlBlSt;
mdb->drEmbedSigWord = desc->drEmbedSigWord;
mdb->drEmbedExtent.startBlock = desc->drEmbedExtent.startBlock;
mdb->drEmbedExtent.blockCount = desc->drEmbedExtent.blockCount;
err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
return( err );
}
static OSErr FixWrapperExtents( SGlobPtr GPtr, RepairOrderPtr p )
{
#pragma unused (p)
OSErr err;
UInt32 totalSectors;
UInt32 sectorSize;
HFSMasterDirectoryBlock *mdb;
SVCB *vcb = GPtr->calculatedVCB;
BlockDescriptor block;
err = GetDeviceSize( vcb->vcbDriveNumber, &totalSectors, §orSize );
ReturnIfError( err );
err = GetVolumeBlock(vcb, totalSectors - 2, kGetBlock, &block);
ReturnIfError( err );
mdb = (HFSMasterDirectoryBlock *) block.buffer;
mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock +
mdb->drXTExtRec[0].blockCount;
err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
ReturnIfError( err );
err = GetVolumeBlock(vcb, 2, kGetBlock, &block);
ReturnIfError( err );
mdb = (HFSMasterDirectoryBlock *) block.buffer;
mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock +
mdb->drXTExtRec[0].blockCount;
err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
return( err );
}
static OSErr FixOrphanedExtent( SGlobPtr GPtr )
{
#if 0
OSErr err;
UInt32 hint;
UInt32 recordSize;
UInt32 maxRecords;
UInt32 numberOfFilesInList;
ExtentKey *extentKeyPtr;
ExtentRecord *extentDataPtr;
ExtentRecord extents;
ExtentRecord zeroExtents;
CatalogKey foundExtentKey;
CatalogRecord catalogData;
CatalogRecord threadData;
HFSCatalogNodeID fileID;
BTScanState scanState;
HFSCatalogNodeID lastFileID = -1;
UInt32 recordsFound = 0;
Boolean mustRebuildBTree = false;
Boolean isHFSPlus = GPtr->isHFSPlus;
SVCB *calculatedVCB = GPtr->calculatedVCB;
UInt32 **dataHandle = GPtr->validFilesList;
SFCB * fcb = GPtr->calculatedExtentsFCB;
err = BTScanInitialize( fcb, 0, 0, 0, gFSBufferPtr, gFSBufferSize, &scanState );
if ( err != noErr ) return( badMDBErr );
ClearMemory( (Ptr)&zeroExtents, sizeof(ExtentRecord) );
if ( isHFSPlus )
{
maxRecords = fcb->fcbLogicalSize / sizeof(HFSPlusExtentRecord);
}
else
{
maxRecords = fcb->fcbLogicalSize / sizeof(HFSExtentRecord);
numberOfFilesInList = GetHandleSize((Handle) dataHandle) / sizeof(UInt32);
qsort( *dataHandle, numberOfFilesInList, sizeof (UInt32), cmpLongs ); }
while ( recordsFound < maxRecords )
{
err = BTScanNextRecord( &scanState, false, (void **) &extentKeyPtr, (void **) &extentDataPtr, &recordSize );
if ( err != noErr )
{
if ( err == btNotFound )
err = noErr;
break;
}
++recordsFound;
fileID = (isHFSPlus == true) ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
if ( (fileID > kHFSBadBlockFileID) && (lastFileID != fileID) ) {
lastFileID = fileID;
if ( isHFSPlus )
{
err = LocateCatalogThread( calculatedVCB, fileID, &threadData, (UInt16*)&recordSize, &hint );
if ( err == noErr ) {
err = LocateCatalogNode( calculatedVCB, threadData.hfsPlusThread.parentID, (const CatalogName *) &(threadData.hfsPlusThread.nodeName), kNoHint, &foundExtentKey, &catalogData, &hint );
}
else if ( err == cmNotFound )
{
err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyPtr, kNoHint, &foundExtentKey, &extents, (UInt16*)&recordSize, &hint );
if ( err == noErr )
{ err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey, hint, &zeroExtents, recordSize, &hint );
err = DeleteBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey ); }
}
if ( err != noErr )
mustRebuildBTree = true; }
else
{
if ( ! bsearch( &fileID, *dataHandle, numberOfFilesInList, sizeof(UInt32), cmpLongs ) )
{
err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyPtr, kNoHint, &foundExtentKey, &extents, (UInt16*)&recordSize, &hint );
if ( err == noErr )
{ err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey, hint, &zeroExtents, recordSize, &hint );
err = DeleteBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey ); }
if ( err != noErr )
mustRebuildBTree = true; }
}
}
}
if ( mustRebuildBTree == true )
{
GPtr->EBTStat |= S_RebuildBTree;
err = errRebuildBtree;
}
return( err );
#else
return (0);
#endif
}
static OSErr FixOrphanedFiles ( SGlobPtr GPtr )
{
CatalogKey key;
CatalogKey foundKey;
CatalogKey tempKey;
CatalogRecord record;
CatalogRecord threadRecord;
CatalogRecord record2;
HFSCatalogNodeID parentID;
HFSCatalogNodeID cNodeID = 0;
BTreeIterator savedIterator;
UInt32 hint;
UInt32 hint2;
UInt32 threadHint;
OSErr err;
UInt16 recordSize;
SInt16 recordType;
SInt16 threadRecordType = 0;
SInt16 selCode = 0x8001;
Boolean isHFSPlus = GPtr->isHFSPlus;
BTreeControlBlock *btcb = GetBTreeControlBlock( kCalculatedCatalogRefNum );
CopyMemory( &btcb->lastIterator, &savedIterator, sizeof(BTreeIterator) );
do
{
CopyMemory( &savedIterator, &btcb->lastIterator, sizeof(BTreeIterator) );
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
if ( err != noErr ) break;
CopyMemory( &btcb->lastIterator, &savedIterator, sizeof(BTreeIterator) );
selCode = 1; recordType = record.recordType;
switch( recordType )
{
case kHFSFileRecord:
if ( ( record.hfsFile.flags & kHFSThreadExistsMask ) == 0 )
break;
case kHFSFolderRecord:
case kHFSPlusFolderRecord:
case kHFSPlusFileRecord:
(void) CheckForStop( GPtr );
parentID = isHFSPlus == true ? foundKey.hfsPlus.parentID : foundKey.hfs.parentID;
threadHint = hint;
switch( recordType )
{
case kHFSFolderRecord: cNodeID = record.hfsFolder.folderID; break;
case kHFSFileRecord: cNodeID = record.hfsFile.fileID; break;
case kHFSPlusFolderRecord: cNodeID = record.hfsPlusFolder.folderID; break;
case kHFSPlusFileRecord: cNodeID = record.hfsPlusFile.fileID; break;
}
BuildCatalogKey( cNodeID, nil, isHFSPlus, &key );
err = SearchBTreeRecord ( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &threadRecord, &recordSize, &hint2 );
if ( err != noErr )
{
if ( err == btNotFound )
{
switch( recordType )
{
case kHFSFolderRecord: threadRecordType = kHFSFolderThreadRecord; break;
case kHFSFileRecord: threadRecordType = kHFSFileThreadRecord; break;
case kHFSPlusFolderRecord: threadRecordType = kHFSPlusFolderThreadRecord; break;
case kHFSPlusFileRecord: threadRecordType = kHFSPlusFileThreadRecord; break;
}
if ( isHFSPlus )
{
HFSPlusCatalogThread threadData;
UInt16 recSize;
ClearMemory( (Ptr)&threadData, sizeof(HFSPlusCatalogThread) );
threadData.recordType = threadRecordType;
threadData.parentID = parentID;
CopyCatalogName( (CatalogName *)&foundKey.hfsPlus.nodeName,
(CatalogName *)&threadData.nodeName, isHFSPlus );
recSize = 10 + (foundKey.hfsPlus.nodeName.length * 2);
err = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &key,
&threadData, recSize, &threadHint );
}
else
{
HFSCatalogThread threadData;
ClearMemory( (Ptr)&threadData, sizeof(HFSCatalogThread) );
threadData.recordType = threadRecordType;
threadData.parentID = parentID;
CopyCatalogName( (CatalogName *)&foundKey.hfs.nodeName, (CatalogName *)&threadData.nodeName, isHFSPlus );
err = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &key, &threadData, sizeof(HFSCatalogThread), &threadHint );
}
}
else
{
break;
}
}
break;
case kHFSFolderThreadRecord:
case kHFSFileThreadRecord:
case kHFSPlusFolderThreadRecord:
case kHFSPlusFileThreadRecord:
if ( isHFSPlus )
BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, isHFSPlus, &key );
else
BuildCatalogKey( record.hfsThread.parentID, (const CatalogName *)&record.hfsThread.nodeName, isHFSPlus, &key );
err = SearchBTreeRecord ( GPtr->calculatedCatalogFCB, &key, kNoHint, &tempKey, &record2, &recordSize, &hint2 );
if ( err != noErr )
{
err = DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey );
}
break;
default:
M_DebugStr("\p Unknown record type");
break;
}
} while ( err == noErr );
if ( err == btNotFound )
err = noErr;
return( err );
}
static OSErr RepairReservedBTreeFields ( SGlobPtr GPtr )
{
CatalogRecord record;
CatalogKey foundKey;
UInt16 recordSize;
SInt16 selCode;
UInt32 hint;
UInt32 *reserved;
OSErr err;
selCode = 0x8001;
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
if ( err != noErr ) goto EXIT;
selCode = 1;
do
{
switch( record.recordType )
{
case kHFSPlusFolderRecord:
if ( record.hfsPlusFolder.flags != 0 )
{
record.hfsPlusFolder.flags = 0;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
case kHFSPlusFileRecord:
if ( ( record.hfsPlusFile.flags & (UInt16) ~(0X83) ) != 0 )
{
record.hfsPlusFile.flags &= 0X83;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
case kHFSFolderRecord:
if ( record.hfsFolder.flags != 0 )
{
record.hfsFolder.flags = 0;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
case kHFSFolderThreadRecord:
case kHFSFileThreadRecord:
reserved = (UInt32*) &(record.hfsThread.reserved);
if ( reserved[0] || reserved[1] )
{
reserved[0] = 0;
reserved[1] = 0;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
case kHFSFileRecord:
if ( ( ( record.hfsFile.flags & (UInt8) ~(0X83) ) != 0 )
|| ( record.hfsFile.dataStartBlock != 0 )
|| ( record.hfsFile.rsrcStartBlock != 0 )
|| ( record.hfsFile.reserved != 0 ) )
{
record.hfsFile.flags &= 0X83;
record.hfsFile.dataStartBlock = 0;
record.hfsFile.rsrcStartBlock = 0;
record.hfsFile.reserved = 0;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
default:
break;
}
if ( err != noErr ) goto EXIT;
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
} while ( err == noErr );
if ( err == btNotFound )
err = noErr;
EXIT:
return( err );
}
static OSErr RebuildBTree( SGlobPtr GPtr, SInt16 refNum )
{
#if 0
CatalogRecord record;
CatalogKey foundKey;
UInt16 recordSize;
UInt16 i;
SInt16 newBTreeRefNum;
UInt32 returnedHint;
UInt32 hint;
UInt32 blocksUsed;
OSErr err;
Boolean hasOverflowExtents;
SInt16 selCode = 0x8001; Boolean isHFSPlus = GPtr->isHFSPlus;
SFCB * fcbPtr;
SFCB * repairFCB = GPtr->calculatedRepairFCB;
SVCB *vcb = GPtr->calculatedVCB;
fcbPtr = ResolveFCB( refNum );
vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
MarkVCBDirty( vcb );
err = FlushVolumeControlBlock( vcb );
ReturnIfError( err );
err = CreateAndOpenRepairBtree( GPtr, refNum, &newBTreeRefNum );
ReturnIfError( err );
err = GetBTreeRecord( refNum, selCode, &foundKey, &record, &recordSize, &hint );
if ( err == noErr )
{
selCode = 1;
do
{
if ( ++i > 10 ) { (void) CheckForStop( GPtr ); i = 0; }
err = InsertBTreeRecord( newBTreeRefNum, &foundKey, &record, recordSize, &returnedHint );
ExitOnError( err );
err = GetBTreeRecord( refNum, selCode, &foundKey, &record, &recordSize, &hint );
} while ( err == noErr );
}
if ( GPtr->RepLevel >= repairLevelWillCauseDataLoss )
{
if ( err == btNotFound )
{
selCode = 0x7FFF;
err = GetBTreeRecord( refNum, selCode, &foundKey, &record, &recordSize, &hint );
ExitOnError( err );
selCode = -1;
do
{
if ( ++i > 10 ) { (void) CheckForStop( GPtr ); i = 0; }
err = InsertBTreeRecord( newBTreeRefNum, &foundKey, &record, recordSize, &returnedHint );
if ( err == noErr )
err = GetBTreeRecord( refNum, selCode, &foundKey, &record, &recordSize, &hint );
} while ( err == noErr );
}
if ( err == btExists ) err = noErr;
}
if ( (err != noErr) && (err != btNotFound) ) ExitOnError( err );
(void) ProcessFileExtents( GPtr, fcbPtr, 0, deleteExtents, (fcbPtr->fcbFileID == kHFSExtentsFileID), &hasOverflowExtents, &blocksUsed );
(void) DeleteFilesOverflowExtents( GPtr, fcbPtr );
if ( hasOverflowExtents == true )
{
M_BTreeHeaderDirty( ((BTreeControlBlockPtr) GPtr->calculatedExtentsFCB->fcbBTCBPtr) );
(void) BTFlushPath( GPtr->calculatedExtentsFCB ); }
GPtr->calculatedRepairBTCB->fileRefNum = refNum;
CopyMemory( GPtr->calculatedRepairBTCB, fcbPtr->fcbBtree, sizeof( BTreeControlBlock ) );
repairFCB->fcbFileID = fcbPtr->fcbFileID;
repairFCB->fcbBtree = fcbPtr->fcbBtree;
CopyMemory( repairFCB, fcbPtr, sizeof( SFCB ) );
err = UpdateBTreeHeader( GPtr, refNum ); ExitOnError( err );
MarkVCBDirty( GPtr->calculatedVCB );
err = FlushAlternateVolumeControlBlock( GPtr->calculatedVCB, isHFSPlus ); ExitOnError( err );
ErrorExit:
if ( err )
(void) BTClosePath( repairFCB );
return( err );
#else
return (0);
#endif
}
static OSErr CreateAndOpenRepairBtree( SGlobPtr GPtr, SInt16 refNum, SInt16 *newBTreeRefNum )
{
OSErr err = 0;
#if 0
UInt16 nodeSize;
SInt32 clumpSize;
UInt32 mapNodes;
UInt32 actualSectorsAdded;
UInt32 blocksUsed;
Boolean readFromDisk;
Boolean hasOverflowExtents;
BTreeControlBlock *corruptedBTCB;
void* buffer;
SFCB *corruptedFCB;
SFCB *fcb;
UInt16 recordCount = 0;
SVCB *vcb = GPtr->calculatedVCB;
BTreeControlBlock *btcb = GPtr->calculatedRepairBTCB; HFSCatalogNodeID fileID = vcb->vcbNextCatalogID + 1;
ClearMemory( (Ptr) GPtr->calculatedRepairFCB, sizeof(SFCB) );
ClearMemory( (Ptr) GPtr->calculatedRepairBTCB, sizeof(BTreeControlBlock) );
if ( refNum == kCalculatedExtentRefNum )
{
WriteMsg( GPtr, M_RebuildingExtentsBTree, kStatusMessage );
corruptedBTCB = GPtr->calculatedExtentsBTCB;
corruptedFCB = GPtr->calculatedExtentsFCB;
clumpSize = vcb->vcbExtentsFile->fcbClumpSize;
GPtr->EBTStat |= S_BTH;
GPtr->TarID = kHFSExtentsFileID;
}
else if ( refNum == kCalculatedCatalogRefNum )
{
WriteMsg( GPtr, M_RebuildingCatalogBTree, kStatusMessage );
corruptedBTCB = GPtr->calculatedCatalogBTCB;
corruptedFCB = GPtr->calculatedCatalogFCB;
clumpSize = vcb->vcbCatalogFile->fcbClumpSize;
GPtr->CBTStat |= S_BTH;
GPtr->TarID = kHFSCatalogFileID;
recordCount = 0;
}
else if ( refNum == kCalculatedAttributesRefNum )
{
WriteMsg( GPtr, M_RebuildingAttributesBTree, kStatusMessage );
corruptedBTCB = GPtr->calculatedAttributesBTCB;
corruptedFCB = GPtr->calculatedAttributesFCB;
clumpSize = vcb->vcbAttributesFile->fcbClumpSize;
GPtr->TarID = kHFSAttributesFileID;
}
else
{
return( notBTree );
}
GPtr->VIStat |= ( S_VBM + S_MDB ); InvalidateCalculatedVolumeBitMap( GPtr );
fcb = GPtr->calculatedRepairFCB;
SetupFCB( vcb, kCalculatedRepairRefNum, fileID, clumpSize );
fcb->fcbBtree = nil;
fcb->fcbLogicalSize = 0;
fcb->fcbPhysicalSize = 0;
err = ExtendFileC ( vcb, fcb, (corruptedFCB->fcbLogicalSize) >> kSectorShift, 0, &actualSectorsAdded );
ReturnIfError( err );
fcb->fcbLogicalSize = fcb->fcbPhysicalSize; *newBTreeRefNum = kCalculatedRepairRefNum;
err = ProcessFileExtents( GPtr, fcb, 0, clearBlocks, (refNum == kCalculatedExtentRefNum), &hasOverflowExtents, &blocksUsed );
if ( err || hasOverflowExtents )
{
if ( hasOverflowExtents )
{
err = E_DiskFull;
GPtr->TarBlock = blocksUsed;
RcdError( GPtr, err );
}
(void) ProcessFileExtents( GPtr, fcb, 0, deleteExtents, (refNum == kCalculatedExtentRefNum), &hasOverflowExtents, &blocksUsed );
return( err );
}
err = ZeroFileBlocks( vcb, fcb, 0, (fcb->fcbLogicalSize) >> kSectorShift);
nodeSize = corruptedBTCB->nodeSize;
err = GetCacheBlock( kCalculatedRepairRefNum, 0, nodeSize, gbDefault, (void**)&buffer, &readFromDisk );
ReturnIfError( err );
InitBTreeHeader( fcb->fcbLogicalSize, fcb->fcbClumpSize, nodeSize, recordCount, corruptedBTCB->maxKeyLength, corruptedBTCB->attributes, &mapNodes, (void*)buffer );
err = ReleaseCacheBlock( buffer, rbWriteMask );
ReturnIfError( err );
if ( mapNodes > 0 ) {
err = CreateMapNodes( recordCount+1, mapNodes, nodeSize ); ReturnIfError( err );
}
err = BTOpenPath(GPtr->calculatedRepairFCB,
corruptedBTCB->keyCompareProc,
GetBlockProc,
ReleaseBlockProc,
SetEndOfForkProc,
SetBlockSizeProc);
if ( err != noErr )
{
M_DebugStr("\pCould not Open B-tree");
(void) BTClosePath( fcb );
return( err );
}
CopyMemory( fcb->fcbBtree , btcb, sizeof(BTreeControlBlock) );
fcb->fcbBtree = (Ptr) btcb;
DisposeMemory( fcb->fcbBtree );
#endif
return( err );
}
OSErr ProcessFileExtents( SGlobPtr GPtr, SFCB *fcb, UInt8 forkType, UInt16 flags, Boolean isExtentsBTree, Boolean *hasOverflowExtents, UInt32 *blocksUsed )
{
OSErr err = noErr;
#if 0
UInt32 extentBlockCount;
UInt32 extentStartBlock;
UInt32 hint;
SInt16 i;
HFSPlusExtentKey key;
HFSPlusExtentRecord extents;
Boolean done = false;
SVCB *vcb = GPtr->calculatedVCB;
UInt32 fileNumber = fcb->fcbFileID;
UInt32 blockCount = 0;
OSErr err = noErr;
*hasOverflowExtents = false;
extentBlockCount = 0;
err = GetFCBExtentRecord( vcb, fcb, extents );
while ( (done == false) && (err == noErr) )
{
for ( i=0 ; i<GPtr->numExtents ; i++ ) {
extentBlockCount = extents[i].blockCount;
extentStartBlock = extents[i].startBlock;
if ( extentBlockCount == 0 )
break;
if ( flags == addBitmapBit )
{
err = AllocExt( GPtr, extentStartBlock, extentBlockCount );
if ( err != noErr )
{
M_DebugStr("\p Problem Allocating Extents");
break;
}
}
blockCount += extentBlockCount;
}
if ( (err != noErr) || isExtentsBTree ) break;
err = FindExtentRecord( vcb, forkType, fileNumber, blockCount, false, &key, extents, &hint );
if ( err == noErr )
{
*hasOverflowExtents = true;
}
else if ( err == btNotFound )
{
err = noErr; done = true;
break;
}
else if ( err != noErr )
{
err = IntError( GPtr, err );
break;
}
if ( flags == deleteExtents )
{
err = DeleteExtentRecord( vcb, forkType, fileNumber, blockCount );
if ( err != noErr ) break;
vcb->vcbFreeBlocks += extentBlockCount;
MarkVCBDirty( vcb );
}
}
*blocksUsed = blockCount;
#endif
return( err );
}
static OSErr DeleteFilesOverflowExtents( SGlobPtr GPtr, SFCB *fcb )
{
#if 0
BTScanState scanState;
ExtentKey *extentKeyPtr;
ExtentRecord *extentDataPtr;
ExtentRecord zeroExtents;
UInt32 maxRecords;
UInt32 recordSize;
UInt32 hint;
OSErr err;
ExtentRecord extents;
ExtentKey foundExtentKey;
UInt32 recordsFound = 0;
SVCB *vcb = GPtr->calculatedVCB;
Boolean isHFSPlus = GPtr->isHFSPlus;
ClearMemory( (Ptr)&zeroExtents, sizeof(ExtentRecord) );
maxRecords = (fcb->fcbLogicalSize) / (isHFSPlus ? sizeof(HFSPlusExtentRecord) : sizeof(HFSExtentRecord));
err = BTScanInitialize( GPtr->calculatedExtentsFCB, 0, 0, 0, gFSBufferPtr, gFSBufferSize, &scanState );
if ( err != noErr ) return( badMDBErr );
while ( recordsFound < maxRecords )
{
err = BTScanNextRecord( &scanState, false, (void **) &extentKeyPtr, (void **) &extentDataPtr, &recordSize );
if ( err != noErr ) break;
++recordsFound;
if ( isHFSPlus && (fcb->fcbFileID == extentKeyPtr->hfsPlus.fileID) )
{
err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyPtr, kNoHint, &foundExtentKey, &extents, (UInt16*)&recordSize, &hint );
err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey, hint, &zeroExtents, recordSize, &hint );
}
else if ( !isHFSPlus && (fcb->fcbFileID == extentKeyPtr->hfs.fileID) )
{
err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyPtr, kNoHint, &foundExtentKey, &extents, (UInt16*)&recordSize, &hint );
err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey, hint, &zeroExtents, recordSize, &hint );
}
}
if ( err == btNotFound )
err = noErr;
return( err );
#else
return (0);
#endif
}
static OSErr CreateMapNodes( UInt32 firstMapNode, UInt32 numberOfNewMapNodes, UInt16 nodeSize )
{
void *buffer;
UInt32 mapRecordBytes;
UInt32 i;
UInt32 mapNodeNum = firstMapNode;
OSErr err = noErr;
SFCB* fcb = ResolveFCB(kCalculatedRepairRefNum);
BlockDescriptor block;
SetFileBlockSize(fcb, nodeSize);
for ( i = 0 ; i < numberOfNewMapNodes ; i++ )
{
err = GetFileBlock(ResolveFCB(kCalculatedRepairRefNum), mapNodeNum, kGetBlock, &block);
ReturnIfError( err );
buffer = (void *) block.buffer;
ClearMemory( buffer, nodeSize );
((NodeDescPtr)buffer)->numRecords = 1;
((NodeDescPtr)buffer)->kind = kBTMapNode;
mapRecordBytes = nodeSize - sizeof(BTNodeDescriptor) - 2*sizeof(SInt16) - 2;
SetOffset( buffer, nodeSize, sizeof(BTNodeDescriptor), 1); SetOffset( buffer, nodeSize, sizeof(BTNodeDescriptor) + mapRecordBytes, 2);
if ( (i+1) < numberOfNewMapNodes )
((BTNodeDescriptor*)buffer)->fLink = mapNodeNum+1; else
((BTNodeDescriptor*)buffer)->fLink = 0;
err = ReleaseFileBlock (fcb, &block, kForceWriteBlock);
ReturnIfError( err );
++mapNodeNum;
}
return( err );
}
int cmpLongs ( const void *a, const void *b )
{
return( *(long*)a - *(long*)b );
}
static OSErr FixOverlappingExtents( SGlobPtr GPtr )
{
OSErr err = noErr;
#if 0
UInt32 i;
UInt32 numInitialExtents;
ExtentInfo *extentInfo;
ExtentsTable **extentsTableH = GPtr->overlappedExtents;
if ( extentsTableH == nil )
return( -1 );
numInitialExtents = (**extentsTableH).count;
InvalidateCalculatedVolumeBitMap( GPtr );
err = UpdateVolumeBitMap( GPtr, true );
for ( i = 0 ; i < (**extentsTableH).count; i++ )
{
if ( (**extentsTableH).extentInfo[i].fileNumber < kHFSFirstUserCatalogNodeID )
{
char fileNum[32];
GPtr->TarBlock = (**extentsTableH).extentInfo[i].fileNumber;
sprintf(fileNum, "%ld", GPtr->TarBlock);
PrintError(GPtr, E_InternalFileOverlap, 1, fileNum);
return( E_InternalFileOverlap );
}
}
for ( i = 0 ; (i < numInitialExtents) && (err == noErr) ; i++ )
{
extentInfo = &((**extentsTableH).extentInfo[i]);
err = MoveExtent( GPtr, extentInfo );
}
InvalidateCalculatedVolumeBitMap( GPtr );
if ( err == noErr )
err = CreateFileIdentifiers( GPtr );
#endif
return( err );
}
static OSErr MoveExtent( SGlobPtr GPtr, ExtentInfo *extentInfo ) {
OSErr err = 0;
#if 0
Ptr byteP;
UInt16 bitPos;
unsigned char mask;
SInt32 bufferNumber;
UInt32 startBuffer;
UInt32 startBlock;
UInt16 recordSize;
UInt32 hint;
UInt32 i;
UInt32 bitsInBuffer;
UInt32 blockCount = 0;
VolumeBitMapHeader *volumeBitMap = GPtr->volumeBitMapPtr;
Boolean isHFSPlus = GPtr->isHFSPlus;
GPtr->TarID = VBM_FNum; startBlock = startBuffer = 0;
bitsInBuffer = volumeBitMap->bufferSize * 8;
for ( bufferNumber=0 ; bufferNumber < volumeBitMap->numberOfBuffers ; bufferNumber++ )
{
BitMapRec *bufferInfo = &(volumeBitMap->bitMapRecord[bufferNumber]);
if ( (bufferInfo->processed) && (bufferInfo->count == 0) )
{
ClearMemory ( volumeBitMap->buffer, volumeBitMap->bufferSize ); }
else if ( (bufferInfo->processed) && (bufferInfo->count == volumeBitMap->bufferSize * 8) )
{
memset( volumeBitMap->buffer, 0xFF, volumeBitMap->bufferSize );
}
else
{
err = CreateVolumeBitMapBuffer( GPtr, bufferNumber, false ); ReturnIfError( err );
}
if ( bufferNumber == volumeBitMap->numberOfBuffers-1 )
bitsInBuffer = (volumeBitMap->bitMapSizeInBytes - (volumeBitMap->bufferSize * bufferNumber)) * 8;
byteP = volumeBitMap->buffer;
for ( i = 0; i < bitsInBuffer; i++ )
{
bitPos = ( i % 8 );
mask = ( 0x80 >> bitPos );
if ( (*byteP & mask) != 0 )
{
startBlock = -1;
}
else
{
if ( startBlock == -1 )
{
startBuffer = bufferNumber;
startBlock = i;
blockCount = 1;
}
else
{
blockCount++;
}
if ( blockCount >= extentInfo->blockCount )
break;
}
if ( bitPos == 7 ) byteP++;
}
if ( blockCount >= extentInfo->blockCount )
break;
}
if ( blockCount < extentInfo->blockCount )
{
err = E_DiskFull;
GPtr->TarBlock = kHFSAllocationFileID;
RcdError( GPtr, err );
return( err );
}
else
{
ExtentKey *extentKeyP;
ExtentRecord *extentRecordP;
UInt32 newStartBlock = startBlock + ( startBuffer * volumeBitMap->bufferSize * 8 );
err = ScanForExtentRecord( GPtr, extentInfo->startBlock, extentInfo->fileNumber, extentInfo->forkType, &extentKeyP, &extentRecordP, &recordSize );
if ( err == noErr )
{
(void) ReplaceStartBlock( GPtr, extentRecordP, extentInfo->startBlock, newStartBlock );
err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyP, hint, extentRecordP, recordSize, &hint );
ReturnIfError( err );
}
else {
CatalogRecord catalogRecord;
CatalogKey catalogKey;
CatalogKey foundCatalogKey;
if ( err != btNotFound )
return( err );
BuildCatalogKey( extentInfo->fileNumber, (const CatalogName*) nil, isHFSPlus, &catalogKey );
err = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &catalogKey, kNoHint, &foundCatalogKey, &catalogRecord, &recordSize, &hint );
if ( err == noErr )
{
extentInfo->hasThread = true;
if ( (catalogRecord.recordType != kHFSFileThreadRecord) && (catalogRecord.recordType != kHFSPlusFileThreadRecord) ) return ( IntError( GPtr, R_IntErr ) );
if ( isHFSPlus == true )
{
BuildCatalogKey( catalogRecord.hfsPlusThread.parentID, (const CatalogName*) &(catalogRecord.hfsPlusThread.nodeName), isHFSPlus, &catalogKey );
err = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &catalogKey, kNoHint, &foundCatalogKey, &catalogRecord, &recordSize, &hint );
ReturnIfError( err );
if ( extentInfo->forkType == 0x00 )
(void) ReplaceStartBlock( GPtr, (ExtentRecord *) &(catalogRecord.hfsPlusFile.dataFork.extents), extentInfo->startBlock, newStartBlock );
else
(void) ReplaceStartBlock( GPtr, (ExtentRecord *) &(catalogRecord.hfsPlusFile.resourceFork.extents), extentInfo->startBlock, newStartBlock );
}
else
{
BuildCatalogKey( catalogRecord.hfsThread.parentID, (const CatalogName*) &(catalogRecord.hfsThread.nodeName), isHFSPlus, &catalogKey );
err = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &catalogKey, kNoHint, &foundCatalogKey, &catalogRecord, &recordSize, &hint );
ReturnIfError( err );
if ( extentInfo->forkType == 0x00 )
(void) ReplaceStartBlock( GPtr, (ExtentRecord *) &(catalogRecord.hfsFile.dataExtents), extentInfo->startBlock, newStartBlock );
else
(void) ReplaceStartBlock( GPtr, (ExtentRecord *) &(catalogRecord.hfsFile.rsrcExtents), extentInfo->startBlock, newStartBlock );
}
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundCatalogKey, hint, &catalogRecord, recordSize, &hint );
ReturnIfError( err );
}
else {
CatalogRecord *foundCatalogRecordP;
CatalogKey *foundCatalogKeyP;
if ( isHFSPlus == true )
return ( IntError( GPtr, R_IntErr ) );
err = ScanForCatalogRecord( GPtr, extentInfo->fileNumber, kHFSFileRecord, &foundCatalogKeyP, &foundCatalogRecordP, &recordSize );
ReturnIfError( err );
if ( extentInfo->forkType == 0x00 )
(void) ReplaceStartBlock( GPtr, (ExtentRecord *) &(foundCatalogRecordP->hfsFile.dataExtents), extentInfo->startBlock, newStartBlock );
else
(void) ReplaceStartBlock( GPtr, (ExtentRecord *) &(foundCatalogRecordP->hfsFile.rsrcExtents), extentInfo->startBlock, newStartBlock );
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, foundCatalogKeyP, kNoHint, foundCatalogRecordP, recordSize, &hint );
ReturnIfError( err );
}
err = CopyDiskBlocks( GPtr, extentInfo->startBlock, extentInfo->blockCount, newStartBlock );
ReturnIfError( err );
}
}
#endif
return( err );
}
static Boolean ReplaceStartBlock( SGlobPtr GPtr, ExtentRecord *extentRecord, UInt32 originalStartBlock, UInt32 newStartBlock )
{
SInt16 i;
Boolean isHFSPlus = GPtr->isHFSPlus;
for ( i = 0 ; i < GPtr->numExtents ; i++ )
{
if ( isHFSPlus == true )
{
if ( extentRecord->hfsPlus[i].startBlock == originalStartBlock )
{
extentRecord->hfsPlus[i].startBlock = newStartBlock;
return( true );
}
}
else
{
if ( extentRecord->hfs[i].startBlock == originalStartBlock )
{
extentRecord->hfs[i].startBlock = newStartBlock;
return( true );
}
}
}
return( false );
}
#if 0
OSErr CopyDiskBlocks( SGlobPtr GPtr, UInt32 startAllocationBlock, UInt32 blockCount, UInt32 newStartAllocationBlock )
{
OSErr err = noErr;
UInt32 diskBlock;
SInt32 i;
UInt32 sectorsInBuffer;
UInt32 numberOfBuffersToWrite;
SVCB *vcb;
UInt32 sectorsPerBlock;
UInt32 ioReqCount;
UInt32 actBytes;
int drive;
gFSBufferInUse = true;
ClearMemory( gFSBufferPtr, gFSBufferSize );
vcb = GPtr->calculatedVCB;
sectorsPerBlock = vcb->vcbBlockSize / Blk_Size;
drive = vcb->vcbDriveNumber;
ioReqCount = gFSBufferSize;
sectorsInBuffer = gFSBufferSize / Blk_Size;
numberOfBuffersToWrite = ( blockCount * sectorsPerBlock + sectorsInBuffer -1 ) / ( sectorsInBuffer );
for ( i = 0 ; i < numberOfBuffersToWrite ; i++ )
{
if ( i == numberOfBuffersToWrite - 1 ) ioReqCount = ( (blockCount * sectorsPerBlock) % sectorsInBuffer) * Blk_Size;
diskBlock = sectorsPerBlock * startAllocationBlock + vcb->vcbAlBlSt + ( i * sectorsInBuffer );
err = DeviceRead(vcb->vcbDriverReadRef, drive, gFSBufferPtr, diskBlock << Log2BlkLo, ioReqCount, &actBytes);
ReturnIfError( err );
diskBlock = sectorsPerBlock * newStartAllocationBlock + vcb->vcbAlBlSt + ( i * sectorsInBuffer );
err = DeviceWrite(vcb->vcbDriverWriteRef, drive, gFSBufferPtr, diskBlock << Log2BlkLo, ioReqCount, &actBytes);
ReturnIfError( err );
}
gFSBufferInUse = false;
return( err );
}
#endif
static OSErr
ScanForCatalogRecord( SGlobPtr GPtr, HFSCatalogNodeID fileID, SInt16 recordType,
CatalogKey **foundCatalogKeyH, CatalogRecord **foundCatalogRecordH, UInt16 *recordSize )
{
OSErr err;
UInt32 maxRecords;
UInt32 recordsFound;
SFCB * fcb;
CatalogRecord catalogRecord;
UInt16 operation;
BTreeIterator btreeIterator;
FSBufferDescriptor btRecord;
UInt16 btRecordSize;
if ( GPtr->isHFSPlus == true ) return( -1 );
fcb = GPtr->calculatedCatalogFCB;
operation = kBTreeFirstRecord;
recordsFound = 0;
maxRecords = (fcb->fcbLogicalSize) / sizeof(HFSCatalogFolder);
btRecord.bufferAddress = &catalogRecord;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof(catalogRecord);
while ( recordsFound < maxRecords )
{
err = BTIterateRecord(fcb, operation, &btreeIterator, &btRecord, &btRecordSize);
ReturnIfError( err );
++recordsFound;
if (operation == kBTreeFirstRecord)
operation = kBTreeNextRecord;
if ( (catalogRecord.recordType == kHFSFileRecord) && (catalogRecord.hfsFile.fileID == fileID) )
{
*recordSize = btRecordSize;
return( noErr );
}
}
return( btNotFound );
}
static OSErr ScanForExtentRecord( SGlobPtr GPtr, UInt32 startBlock, HFSCatalogNodeID fileID, UInt8 forkType,
ExtentKey **foundExtentKeyH, ExtentRecord **foundExtentRecordH, UInt16 *recordSize )
{
OSErr err;
UInt32 maxRecords;
UInt32 i;
UInt32 recordsFound = 0;
Boolean isHFSPlus = GPtr->isHFSPlus;
SFCB *fcb = GPtr->calculatedExtentsFCB;
HFSPlusExtentKey *extentKeyPtr;
HFSPlusExtentRecord extentRecord;
UInt16 operation;
BTreeIterator btreeIterator;
FSBufferDescriptor btRecord;
UInt16 btRecordSize;
operation = kBTreeFirstRecord;
maxRecords = (fcb->fcbLogicalSize) / (isHFSPlus ? sizeof(HFSPlusExtentRecord) : sizeof(HFSExtentRecord));
extentKeyPtr = (HFSPlusExtentKey*) &btreeIterator.key;
btRecord.bufferAddress = &extentRecord;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof(extentRecord);
while ( recordsFound < maxRecords )
{
err = BTIterateRecord(fcb, operation, &btreeIterator, &btRecord, &btRecordSize);
ReturnIfError( err );
++recordsFound;
if (operation == kBTreeFirstRecord)
operation = kBTreeNextRecord;
if ( isHFSPlus )
{
if ( (extentKeyPtr->fileID != fileID) ||
(extentKeyPtr->forkType != forkType) )
continue;
}
else
{
if ( (((HFSExtentKey*) extentKeyPtr)->fileID != fileID) ||
(((HFSExtentKey*) extentKeyPtr)->forkType != forkType) )
continue;
ConvertToHFSPlusExtent(*(HFSExtentRecord*) &extentRecord, extentRecord);
}
for ( i=0 ; i < GPtr->numExtents ; i++ )
{
if ( extentRecord[i].startBlock == startBlock )
{
*recordSize = btRecordSize;
return( noErr );
}
}
}
return( btNotFound );
}
static OSErr CreateFileIdentifiers( SGlobPtr GPtr )
{
UInt32 i;
ExtentInfo *extentInfo;
FileIdentifier fileIdentifier;
CatalogRecord *foundCatalogRecordP;
CatalogKey *foundCatalogKeyP;
UInt16 recordSize;
OSErr err;
Boolean isHFSPlus = GPtr->isHFSPlus;
ExtentsTable **extentsTableH = GPtr->overlappedExtents;
ClearMemory( &fileIdentifier, sizeof(FileIdentifier) );
for ( i = 0 ; i < (**extentsTableH).count; i++ )
{
extentInfo = &((**extentsTableH).extentInfo[i]);
if ( IdentifierExists( GPtr->fileIdentifierTable, extentInfo->fileNumber ) == false )
{
if ( isHFSPlus || extentInfo->hasThread )
{
fileIdentifier.hasThread = true;
}
else {
err = ScanForCatalogRecord( GPtr, extentInfo->fileNumber, kHFSFileRecord, &foundCatalogKeyP, &foundCatalogRecordP, &recordSize );
ReturnIfError( err );
fileIdentifier.hasThread = false;
fileIdentifier.parID = foundCatalogKeyP->hfs.parentID;
CopyMemory( foundCatalogKeyP->hfs.nodeName, fileIdentifier.name, foundCatalogKeyP->hfs.nodeName[0]+1 );
}
fileIdentifier.fileID = extentInfo->fileNumber;
InsertIdentifier( GPtr, &fileIdentifier );
}
}
return( noErr );
}
static void InsertIdentifier( SGlobPtr GPtr, FileIdentifier *fileIdentifier )
{
UInt32 newHandleSize;
FileIdentifierTable **fileIdentifierTable;
if ( GPtr->fileIdentifierTable == nil )
{
GPtr->fileIdentifierTable = (FileIdentifierTable **) NewHandleClear( sizeof(FileIdentifierTable) );
fileIdentifierTable = GPtr->fileIdentifierTable;
}
else
{
fileIdentifierTable = GPtr->fileIdentifierTable;
newHandleSize = ( sizeof(FileIdentifier) ) + ( GetHandleSize( (Handle)fileIdentifierTable ) );
SetHandleSize( (Handle)fileIdentifierTable, newHandleSize );
}
CopyMemory( fileIdentifier, &((**fileIdentifierTable).fileIdentifier[(**fileIdentifierTable).count]), sizeof(FileIdentifier) );
(**fileIdentifierTable).count++;
}
static Boolean IdentifierExists( FileIdentifierTable **fileIdentifierTable, HFSCatalogNodeID fileID )
{
UInt32 i;
if ( fileIdentifierTable == nil )
return( false );
for ( i = 0 ; i < (**fileIdentifierTable).count ; i++ )
{
if ( (**fileIdentifierTable).fileIdentifier[i].fileID == fileID )
return( true );
}
return( false );
}
static OSErr FixBloatedThreadRecords( SGlob *GPtr )
{
CatalogRecord record;
CatalogKey foundKey;
UInt32 hint;
UInt16 recordSize;
SInt16 i = 0;
OSErr err;
SInt16 selCode = 0x8001;
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
ReturnIfError( err );
selCode = 1;
do
{
if ( ++i > 10 ) { (void) CheckForStop( GPtr ); i = 0; }
if ( (recordSize == sizeof(HFSPlusCatalogThread)) && ((record.recordType == kHFSPlusFolderThreadRecord) || (record.recordType == kHFSPlusFileThreadRecord)) )
{
recordSize -= ( sizeof(record.hfsPlusThread.nodeName.unicode) - (record.hfsPlusThread.nodeName.length * sizeof(UniChar)) );
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
ReturnIfError( err );
}
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
} while ( err == noErr );
if ( err == btNotFound )
err = noErr;
return( err );
}
static OSErr
FixMissingThreadRecords( SGlob *GPtr )
{
struct MissingThread *mtp;
FSBufferDescriptor btRecord;
BTreeIterator iterator;
OSStatus result;
UInt16 dataSize;
for (mtp = GPtr->missingThreadList; mtp != NULL; mtp = mtp->link) {
if (mtp->threadID == 0 || mtp->thread.parentID == 0)
continue;
dataSize = 10 + (mtp->thread.nodeName.length * 2);
btRecord.bufferAddress = (void *)&mtp->thread;
btRecord.itemSize = dataSize;
btRecord.itemCount = 1;
iterator.hint.nodeNum = 0;
BuildCatalogKey(mtp->threadID, NULL, true, (CatalogKey*)&iterator.key);
result = BTInsertRecord(GPtr->calculatedCatalogFCB, &iterator, &btRecord, dataSize);
if (result)
return (IntError(GPtr, R_IntErr));
mtp->threadID = 0;
}
return (0);
}