#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 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);
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 FixOverlappingExtents( 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 )
{
SetDFAStage( kRepairStage ); }
}
SetDFAStage( kRepairStage );
err = MRepair( GPtr );
return( err );
}
int MRepair( SGlobPtr GPtr )
{
OSErr err;
SVCB *calculatedVCB = GPtr->calculatedVCB;
Boolean isHFSPlus = GPtr->isHFSPlus;
#if 1 // turn on catalog B-Tree rebuild magic
if ( GPtr->CBTStat & S_RebuildBTree )
{
err = RebuildCatalogBTree( GPtr );
return( err );
}
#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_OverlappingExtents) != 0 )
{
err = FixOverlappingExtents( GPtr ); ReturnIfError( err );
GPtr->VIStat &= ~S_OverlappingExtents;
GPtr->VIStat |= S_VBM; InvalidateCalculatedVolumeBitMap( GPtr ); }
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->CBTStat & S_Orphan) != 0 )
{
err = FixOrphanedFiles ( GPtr ); ReturnIfError( err );
GPtr->CBTStat |= S_BTH; }
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 );
if ( (GPtr->VIStat & S_VBM) != 0 )
{
err = UpdateVolumeBitMap( GPtr, false ); ReturnIfError( err );
InvalidateCalculatedVolumeBitMap( GPtr ); }
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( ((GPtr->VIStat & S_MDB) != 0) || IsVCBDirty(calculatedVCB) ) {
MarkVCBDirty(calculatedVCB); calculatedVCB->vcbAttributes |= kHFSVolumeUnmountedMask;
err = FlushAlternateVolumeControlBlock( calculatedVCB, isHFSPlus ); 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;
SInt32 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;
GPtr->VIStat |= S_VBM;
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;
UInt64 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;
UInt64 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 );
}
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 );
}
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 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);
}