#include "Scavenger.h"
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include "../cache.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 GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize);
static OSErr RepairAttributesCheckABT(SGlobPtr GPtr, Boolean isHFSPlus);
static OSErr RepairAttributesCheckCBT(SGlobPtr GPtr, Boolean isHFSPlus);
static OSErr RepairAttributes( 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);
static OSErr VolumeObjectFixVHBorMDB( Boolean * fixedIt );
static OSErr VolumeObjectRestoreWrapper( void );
static OSErr FixBloatedThreadRecords( SGlob *GPtr );
static OSErr FixMissingThreadRecords( SGlob *GPtr );
static OSErr FixEmbededVolDescription( SGlobPtr GPtr, RepairOrderPtr p );
static OSErr FixWrapperExtents( SGlobPtr GPtr, RepairOrderPtr p );
static OSErr FixIllegalNames( SGlobPtr GPtr, RepairOrderPtr roPtr );
static HFSCatalogNodeID GetObjectID( CatalogRecord * theRecPtr );
static OSErr FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID );
static OSErr FixOverlappingExtents(SGlobPtr GPtr);
static int CompareExtentBlockCount(const void *first, const void *second);
static OSErr MoveExtent(SGlobPtr GPtr, ExtentInfo *extentInfo);
static OSErr CreateCorruptFileSymlink(SGlobPtr GPtr, UInt32 parentID, ExtentInfo *extentInfo);
static OSErr SearchExtentInVH(SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 *foundExtentIndex, Boolean *noMoreExtents);
static OSErr UpdateExtentInVH (SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 foundExtentIndex);
static OSErr SearchExtentInCatalogBT(SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 *foundExtentIndex, Boolean *noMoreExtents);
static OSErr UpdateExtentInCatalogBT (SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 foundExtentIndex);
static OSErr SearchExtentInExtentBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusExtentKey *extentKey, HFSPlusExtentRecord *extentRecord, UInt16 *recordSize, UInt32 *foundExtentIndex);
static OSErr FindExtentInExtentRec (Boolean isHFSPlus, UInt32 startBlock, UInt32 blockCount, const HFSPlusExtentRecord extentData, UInt32 *foundExtentIndex, Boolean *noMoreExtents);
static OSErr CopyDiskBlocks(SGlobPtr GPtr, const UInt32 startAllocationBlock, const UInt32 blockCount, const UInt32 newStartAllocationBlock );
static OSErr WriteDiskBlocks(SGlobPtr GPtr, UInt32 startBlock, UInt32 blockCount, u_char *buffer, int buflen);
static OSErr CreateFileByName(SGlobPtr GPtr, UInt32 parentID, UInt16 fileType, u_char *fileName, unsigned int filenameLen, u_char *data, unsigned int dataLen);
static UInt32 CreateDirByName(SGlob *GPtr , const u_char *dirName, const UInt32 parentID);
static int BuildFolderRec( u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus, CatalogRecord * theRecPtr );
static int BuildThreadRec( CatalogKey * theKeyPtr, CatalogRecord * theRecPtr, Boolean isHFSPlus, Boolean isDirectory );
static int BuildFileRec(UInt16 fileType, UInt16 fileMode, UInt32 fileID, Boolean isHFSPlus, CatalogRecord *catRecord);
OSErr RepairVolume( SGlobPtr GPtr )
{
OSErr err;
SetDFAStage( kAboutToRepairStage ); err = CheckForStop( GPtr ); ReturnIfError( err );
SetDFAStage( kRepairStage );
err = MRepair( GPtr );
return( err );
}
int MRepair( SGlobPtr GPtr )
{
OSErr err;
SVCB *calculatedVCB = GPtr->calculatedVCB;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus( );
if ( GPtr->CBTStat & S_RebuildBTree )
{
err = RebuildCatalogBTree( GPtr );
return( err );
}
err = DoMinorOrders( GPtr );
ReturnIfError( err );
err = CheckForStop( GPtr ); ReturnIfError( err );
GPtr->CatStat &= ~(S_FileAllocation | S_Permissions | S_UnlinkedFile | S_LinkCount | S_IllName);
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 );
}
if ( (GPtr->ABTStat & S_AttributeCount) ||
(GPtr->ABTStat & S_SecurityCount))
{
err = RepairAttributes( GPtr );
ReturnIfError( err );
}
if ( (GPtr->ABTStat & S_BTH) )
{
err = UpdateBTreeHeader( GPtr->calculatedAttributesFCB ); ReturnIfError( err );
}
if ( GPtr->ABTStat & S_BTM )
{
err = UpdBTM( GPtr, kCalculatedAttributesRefNum ); 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 ) {
Boolean fixedIt = false;
err = VolumeObjectFixVHBorMDB( &fixedIt );
ReturnIfError( err );
if ( fixedIt ) {
GPtr->VIStat &= ~S_MDB;
MarkVCBClean( calculatedVCB );
}
}
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->VIStat & S_WMDB) != 0 ) {
err = VolumeObjectRestoreWrapper();
ReturnIfError( err );
}
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 );
if ( GPtr->minorRepairErrors )
err = R_RFail;
return( err ); }
static OSErr VolumeObjectFixVHBorMDB( Boolean* fixedItPtr )
{
OSErr err;
OSErr err2;
VolumeObjectPtr myVOPtr;
BlockDescriptor myPrimary;
BlockDescriptor myAlternate;
myVOPtr = GetVolumeObjectPtr( );
myPrimary.buffer = NULL;
myAlternate.buffer = NULL;
err = noErr;
if ( VolumeObjectIsHFS() ) {
if ( (myVOPtr->flags & kVO_PriMDBOK) != 0 &&
(myVOPtr->flags & kVO_AltMDBOK) != 0 )
goto ExitThisRoutine;
}
else {
if ( (myVOPtr->flags & kVO_PriVHBOK) != 0 &&
(myVOPtr->flags & kVO_AltVHBOK) != 0 )
goto ExitThisRoutine;
}
err = GetVolumeObjectPrimaryBlock( &myPrimary );
if ( !(err == noErr || err == badMDBErr || err == noMacDskErr) )
goto ExitThisRoutine;
if ( VolumeObjectIsHFS( ) ) {
if ( (myVOPtr->flags & kVO_PriMDBOK) == 0 )
err = badMDBErr;
}
else if ( (myVOPtr->flags & kVO_PriVHBOK) == 0 ) {
err = badMDBErr;
}
err2 = GetVolumeObjectAlternateBlock( &myAlternate );
if ( !(err2 == noErr || err2 == badMDBErr || err2 == noMacDskErr) )
goto ExitThisRoutine;
if ( VolumeObjectIsHFS( ) ) {
if ( (myVOPtr->flags & kVO_AltMDBOK) == 0 )
err2 = badMDBErr;
}
else if ( (myVOPtr->flags & kVO_AltVHBOK) == 0 ) {
err2 = badMDBErr;
}
if ( err == noErr ) {
CopyMemory( myPrimary.buffer, myAlternate.buffer, Blk_Size );
(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kForceWriteBlock );
myAlternate.buffer = NULL;
*fixedItPtr = true;
if ( VolumeObjectIsHFS( ) )
myVOPtr->flags |= kVO_AltMDBOK;
else
myVOPtr->flags |= kVO_AltVHBOK;
}
else if ( err2 == noErr ) {
CopyMemory( myAlternate.buffer, myPrimary.buffer, Blk_Size );
(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kForceWriteBlock );
myPrimary.buffer = NULL;
*fixedItPtr = true;
if ( VolumeObjectIsHFS( ) )
myVOPtr->flags |= kVO_PriMDBOK;
else
myVOPtr->flags |= kVO_PriVHBOK;
err = noErr;
}
else
err = noMacDskErr;
ExitThisRoutine:
if ( myPrimary.buffer != NULL )
(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock );
if ( myAlternate.buffer != NULL )
(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock );
return( err );
}
static OSErr VolumeObjectRestoreWrapper( void )
{
OSErr err;
OSErr err2;
VolumeObjectPtr myVOPtr;
BlockDescriptor myPrimary;
BlockDescriptor myAlternate;
myVOPtr = GetVolumeObjectPtr( );
myPrimary.buffer = NULL;
myAlternate.buffer = NULL;
err = GetVolumeObjectPrimaryMDB( &myPrimary );
if ( !(err == noErr || err == badMDBErr || err == noMacDskErr) )
goto ExitThisRoutine;
err2 = GetVolumeObjectAlternateMDB( &myAlternate );
if ( !(err2 == noErr || err2 == badMDBErr || err2 == noMacDskErr) )
goto ExitThisRoutine;
if ( err == noErr && (myVOPtr->flags & kVO_PriMDBOK) != 0 ) {
CopyMemory( myPrimary.buffer, myAlternate.buffer, Blk_Size );
(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kForceWriteBlock );
myAlternate.buffer = NULL;
myVOPtr->flags |= kVO_AltMDBOK;
}
else if ( err2 == noErr && (myVOPtr->flags & kVO_AltMDBOK) != 0 ) {
CopyMemory( myAlternate.buffer, myPrimary.buffer, Blk_Size );
(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kForceWriteBlock );
myPrimary.buffer = NULL;
myVOPtr->flags |= kVO_PriMDBOK;
err = noErr;
}
else
err = noMacDskErr;
ExitThisRoutine:
if ( myPrimary.buffer != NULL )
(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock );
if ( myAlternate.buffer != NULL )
(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock );
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;
#if 0
if (-->TBD<--)
header.keyCompareType = kHFSBinaryCompare;
#endif
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_IllegalName:
err = FixIllegalNames( 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;
Boolean isHFSPlus;
ExtentRecord zeroExtents;
isHFSPlus = VolumeObjectIsHFSPlus( );
BuildCatalogKey( fid, (const CatalogName*) nil, 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;
BTreeControlBlock *calculatedBTCB = GetBTreeControlBlock( kCalculatedCatalogRefNum );
isHFSPlus = VolumeObjectIsHFSPlus( );
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; Boolean isHFSPlus;
UInt32 hint; UInt16 recSize;
CatalogRecord record;
CatalogKey foundKey;
CatalogKey key;
SVCB *calculatedVCB = GPtr->calculatedVCB;
isHFSPlus = VolumeObjectIsHFSPlus( );
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, 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;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus( );
BuildCatalogKey( p->parid, (CatalogName *)&p->name, 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 != SWAP_BE16(largeCatalogFolderP->userInfo.frFlags) )
{
if ( p->correct < p->incorrect )
largeCatalogFolderP->userInfo.frFlags &= SWAP_BE16(~((UInt16)p->maskBit));
else
largeCatalogFolderP->userInfo.frFlags |= SWAP_BE16((UInt16)p->maskBit);
}
else
{
largeCatalogFolderP->userInfo.frFlags = SWAP_BE16((UInt16)p->correct);
}
}
else
{
HFSCatalogFolder *smallCatalogFolderP = (HFSCatalogFolder *) &record;
if ( p->incorrect != SWAP_BE16(smallCatalogFolderP->userInfo.frFlags) ) {
if ( p->correct < p->incorrect )
smallCatalogFolderP->userInfo.frFlags &= SWAP_BE16(~((UInt16)p->maskBit));
else
smallCatalogFolderP->userInfo.frFlags |= SWAP_BE16((UInt16)p->maskBit);
}
else
{
smallCatalogFolderP->userInfo.frFlags = SWAP_BE16((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;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus( );
if (!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
FixIllegalNames( SGlobPtr GPtr, RepairOrderPtr roPtr )
{
OSErr result;
Boolean isHFSPlus;
Boolean isDirectory;
UInt16 recSize;
SFCB * fcbPtr;
CatalogName * oldNamePtr;
CatalogName * newNamePtr;
UInt32 hint;
CatalogRecord record;
CatalogKey key;
CatalogKey newKey;
isHFSPlus = VolumeObjectIsHFSPlus( );
fcbPtr = GPtr->calculatedCatalogFCB;
oldNamePtr = (CatalogName *) &roPtr->name;
if ( isHFSPlus )
{
int myLength;
u_int16_t * myPtr = (u_int16_t *) oldNamePtr;
myLength = *myPtr; myPtr += (1 + myLength); newNamePtr = (CatalogName *) myPtr;
}
else
{
int myLength;
u_char * myPtr = (u_char *) oldNamePtr;
myLength = *myPtr; myPtr += (1 + myLength); newNamePtr = (CatalogName *) myPtr;
}
BuildCatalogKey( roPtr->parid, newNamePtr, isHFSPlus, &newKey );
result = SearchBTreeRecord( fcbPtr, &newKey, kNoHint, NULL, &record, &recSize, NULL );
if ( result == noErr ) {
if ( GPtr->logLevel >= kDebugLog ) {
printf( "\treplacement name already exists \n" );
printf( "\tduplicate name is 0x" );
PrintName( newNamePtr->ustr.length, (UInt8 *) &newNamePtr->ustr.unicode, true );
}
goto ErrorExit;
}
BuildCatalogKey( roPtr->parid, oldNamePtr, isHFSPlus, &key );
result = SearchBTreeRecord( fcbPtr, &key, kNoHint, NULL, &record, &recSize, &hint );
if ( result != noErr ) {
goto ErrorExit;
}
result = DeleteBTreeRecord( fcbPtr, &key );
if ( result != noErr ) {
goto ErrorExit;
}
result = InsertBTreeRecord( fcbPtr, &newKey, &record, recSize, &hint );
if ( result != noErr ) {
goto ErrorExit;
}
isDirectory = false;
switch( record.recordType )
{
case kHFSFolderRecord:
case kHFSPlusFolderRecord:
isDirectory = true; break;
}
BuildCatalogKey( GetObjectID( &record ), NULL, isHFSPlus, &key );
result = SearchBTreeRecord( fcbPtr, &key, kNoHint, NULL, &record, &recSize, &hint );
if ( result != noErr ) {
goto ErrorExit;
}
result = DeleteBTreeRecord( fcbPtr, &key );
if ( result != noErr ) {
goto ErrorExit;
}
recSize = BuildThreadRec( &newKey, &record, isHFSPlus, isDirectory );
result = InsertBTreeRecord( fcbPtr, &key, &record, recSize, &hint );
if ( result != noErr ) {
goto ErrorExit;
}
return( noErr );
ErrorExit:
GPtr->minorRepairErrors = true;
if ( GPtr->logLevel >= kDebugLog )
printf( "\t%s - repair failed for type 0x%02X %d \n", __FUNCTION__, roPtr->type, roPtr->type );
return( noErr );
}
static OSErr
FixBSDInfo(SGlobPtr GPtr, RepairOrderPtr p)
{
SFCB *fcb;
CatalogRecord rec;
FSBufferDescriptor btRecord;
BTreeIterator btIterator;
Boolean isHFSPlus;
OSErr result;
UInt16 recSize;
size_t namelen;
unsigned char filename[256];
isHFSPlus = VolumeObjectIsHFSPlus( );
if (!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;
Boolean isHFSPlus;
size_t len;
isHFSPlus = VolumeObjectIsHFSPlus( );
if (!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 isHFSPlus;
Boolean replace;
OSErr result;
UInt16 recSize;
UInt64 bytes;
isHFSPlus = VolumeObjectIsHFSPlus( );
if (!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;
HFSMasterDirectoryBlock *mdb;
EmbededVolDescription *desc;
SVCB *vcb = GPtr->calculatedVCB;
BlockDescriptor block;
desc = (EmbededVolDescription *) &(p->name);
block.buffer = NULL;
err = GetVolumeObjectAlternateMDB( &block );
if ( err != noErr )
goto ExitThisRoutine;
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 );
block.buffer = NULL;
if ( err != noErr )
goto ExitThisRoutine;
err = GetVolumeObjectPrimaryMDB( &block );
if ( err != noErr )
goto ExitThisRoutine;
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 );
block.buffer = NULL;
ExitThisRoutine:
if ( block.buffer != NULL )
err = ReleaseVolumeBlock( vcb, &block, kReleaseBlock );
return( err );
}
static OSErr FixWrapperExtents( SGlobPtr GPtr, RepairOrderPtr p )
{
#pragma unused (p)
OSErr err;
HFSMasterDirectoryBlock *mdb;
SVCB *vcb = GPtr->calculatedVCB;
BlockDescriptor block;
block.buffer = NULL;
err = GetVolumeObjectAlternateMDB( &block );
if ( err != noErr )
goto ExitThisRoutine;
mdb = (HFSMasterDirectoryBlock *) block.buffer;
mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock +
mdb->drXTExtRec[0].blockCount;
err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
block.buffer = NULL;
if ( err != noErr )
goto ExitThisRoutine;
err = GetVolumeObjectPrimaryMDB( &block );
if ( err != noErr )
goto ExitThisRoutine;
mdb = (HFSMasterDirectoryBlock *) block.buffer;
mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock +
mdb->drXTExtRec[0].blockCount;
err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
block.buffer = NULL;
ExitThisRoutine:
if ( block.buffer != NULL )
(void) ReleaseVolumeBlock( vcb, &block, kReleaseBlock );
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;
SVCB *calculatedVCB = GPtr->calculatedVCB;
UInt32 **dataHandle = GPtr->validFilesList;
SFCB * fcb = GPtr->calculatedExtentsFCB;
isHFSPlus = VolumeObjectIsHFSPlus( );
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 selCode = 0x8001;
Boolean isHFSPlus;
BTreeControlBlock *btcb = GetBTreeControlBlock( kCalculatedCatalogRefNum );
isHFSPlus = VolumeObjectIsHFSPlus( );
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,
&tempKey, &threadRecord, &recordSize, &hint2 );
if (err == noErr) {
if (isHFSPlus) {
if (parentID != threadRecord.hfsPlusThread.parentID) {
(void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
err = btNotFound;
if (GPtr->logLevel >= kDebugLog) {
printf ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__, cNodeID, parentID, threadRecord.hfsPlusThread.parentID);
}
}
if (!((foundKey.hfsPlus.nodeName.length == threadRecord.hfsPlusThread.nodeName.length)
&& (!bcmp(foundKey.hfsPlus.nodeName.unicode,
threadRecord.hfsPlusThread.nodeName.unicode,
foundKey.hfsPlus.nodeName.length * 2)))) {
(void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
err = btNotFound;
if (GPtr->logLevel >= kDebugLog) {
printf ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID);
}
}
} else {
if (parentID != threadRecord.hfsThread.parentID) {
(void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
err = btNotFound;
if (GPtr->logLevel >= kDebugLog) {
printf ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__, cNodeID, parentID, threadRecord.hfsThread.parentID);
}
}
if (!((foundKey.hfs.nodeName[0] == threadRecord.hfsThread.nodeName[0])
&& (!bcmp(&foundKey.hfs.nodeName[1],
&threadRecord.hfsThread.nodeName[1],
foundKey.hfs.nodeName[0])))) {
(void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
err = btNotFound;
if (GPtr->logLevel >= kDebugLog) {
printf ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID);
}
}
}
}
if ( err == btNotFound )
{
Boolean isDirectory;
isDirectory = false;
switch( recordType )
{
case kHFSFolderRecord:
case kHFSPlusFolderRecord:
isDirectory = true;
break;
}
recordSize = BuildThreadRec( &foundKey, &threadRecord, isHFSPlus,
isDirectory );
err = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &key,
&threadRecord, recordSize, &threadHint );
if (GPtr->logLevel >= kDebugLog) {
printf ("\t%s: Created thread record for id=%u (err=%u)\n", __FUNCTION__, cNodeID, err);
}
}
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) {
if (isHFSPlus) {
if (foundKey.hfsPlus.parentID != record2.hfsPlusFile.fileID) {
err = btNotFound;
if (GPtr->logLevel >= kDebugLog) {
printf ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfsPlus.parentID, record2.hfsPlusFile.fileID, record.hfsPlusThread.parentID);
}
}
} else {
if (foundKey.hfs.parentID != record2.hfsFile.fileID) {
err = btNotFound;
if (GPtr->logLevel >= kDebugLog) {
if (recordType == kHFSFolderThreadRecord) {
printf ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFolder.folderID, record.hfsThread.parentID);
} else {
printf ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFile.fileID, record.hfsThread.parentID);
}
}
}
}
}
if ( err != noErr )
{
err = DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey );
if (GPtr->logLevel >= kDebugLog) {
if (isHFSPlus) {
printf ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfsPlus.parentID, err);
} else {
printf ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfs.parentID, err);
}
}
}
break;
default:
if (GPtr->logLevel >= kDebugLog) {
printf ("\t%s: Unknown record type.\n", __FUNCTION__);
}
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 GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize)
{
OSErr err = noErr;
CatalogKey catThreadKey;
CatalogName catalogName;
UInt32 hint;
BuildCatalogKey(fileID, NULL, isHFSPlus, &catThreadKey);
err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catThreadKey, kNoHint, catKey, catRecord, recordSize, &hint);
if (err) {
#if DEBUG_XATTR
printf ("%s: No matching catalog thread record found\n", __FUNCTION__);
#endif
goto out;
}
#if DEBUG_XATTR
printf ("%s(%s,%d):1 recordType=%x, flags=%x\n", __FUNCTION__, __FILE__, __LINE__,
catRecord->hfsPlusFile.recordType,
catRecord->hfsPlusFile.flags);
#endif
if ((catRecord->hfsPlusFile.recordType == kHFSPlusFolderRecord) ||
(catRecord->hfsPlusFile.recordType == kHFSPlusFileRecord)) {
err = fsBTRecordNotFoundErr;
goto out;
}
CopyCatalogName((CatalogName *)&(catRecord->hfsPlusThread.nodeName), &catalogName, isHFSPlus);
BuildCatalogKey(catRecord->hfsPlusThread.parentID, &catalogName, isHFSPlus, catKey);
err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, catKey, kNoHint, catKey, catRecord, recordSize, &hint);
if (err) {
#if DEBUG_XATTR
printf ("%s: No matching catalog record found\n", __FUNCTION__);
#endif
goto out;
}
#if DEBUG_XATTR
printf ("%s(%s,%d):2 recordType=%x, flags=%x\n", __FUNCTION__, __FILE__, __LINE__,
catRecord->hfsPlusFile.recordType,
catRecord->hfsPlusFile.flags);
#endif
out:
return err;
}
static OSErr RepairAttributesCheckABT(SGlobPtr GPtr, Boolean isHFSPlus)
{
OSErr err = noErr;
UInt16 selCode;
UInt32 hint;
HFSPlusAttrRecord attrRecord;
HFSPlusAttrKey attrKey;
UInt16 attrRecordSize;
CatalogRecord catRecord;
CatalogKey catKey;
UInt16 catRecordSize;
lastAttrID lastID;
Boolean didRecordChange = false;
#if DEBUG_XATTR
char attrName[XATTR_MAXNAMELEN];
size_t len;
#endif
lastID.fileID = 0;
lastID.hasSecurity = false;
selCode = 0x8001;
err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &attrRecordSize, &hint);
if (err != noErr) goto out;
selCode = 1;
do {
#if DEBUG_XATTR
(void) utf_encodestr(attrKey.attrName, attrKey.attrNameLen * 2, attrName, &len);
attrName[len] = '\0';
printf ("%s(%s,%d): Found attrName=%s for fileID=%d\n", __FUNCTION__, __FILE__, __LINE__, attrName, attrKey.fileID);
#endif
if (attrKey.fileID != lastID.fileID) {
if (didRecordChange == true) {
err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, catRecordSize, &hint);
if (err) {
#if DEBUG_XATTR
printf ("%s: Error in replacing Catalog Record\n", __FUNCTION__);
#endif
goto out;
}
}
didRecordChange = false;
err = GetCatalogRecord(GPtr, attrKey.fileID, isHFSPlus, &catKey, &catRecord, &catRecordSize);
if (err) {
#if DEBUG_XATTR
printf ("%s: No matching catalog record found\n", __FUNCTION__);
#endif
if ((attrKey.fileID < kHFSFirstUserCatalogNodeID) &&
(attrKey.fileID != kHFSRootFolderID)) {
#if DEBUG_XATTR
printf ("%s: Ignore catalog check for fileID=%d for attribute=%s\n", __FUNCTION__, attrKey.fileID, attrName);
#endif
goto getnext;
}
err = DeleteBTreeRecord(GPtr->calculatedAttributesFCB, &attrKey);
if (err) {
#if DEBUG_XATTR
printf ("%s: Error in deleting attribute record\n", __FUNCTION__);
#endif
goto out;
}
#if DEBUG_XATTR
printf ("%s: Deleted attribute=%s for fileID=%d\n", __FUNCTION__, attrName, attrKey.fileID);
#endif
GPtr->ABTStat |= S_BTH + S_BTM;
goto getnext;
}
lastID.fileID = attrKey.fileID;
lastID.hasSecurity = false;
if (!(catRecord.hfsPlusFile.flags & kHFSHasAttributesMask)) {
catRecord.hfsPlusFile.flags |= kHFSHasAttributesMask;
didRecordChange = true;
}
if (!bcmp(attrKey.attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
lastID.hasSecurity = true;
if (!(catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
catRecord.hfsPlusFile.flags |= kHFSHasSecurityMask;
didRecordChange = true;
}
}
} else {
if ((lastID.hasSecurity == false) && !bcmp(attrKey.attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
lastID.hasSecurity = true;
if (!(catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
catRecord.hfsPlusFile.flags |= kHFSHasSecurityMask;
didRecordChange = true;
}
}
}
getnext:
err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &attrRecordSize, &hint);
} while (err == noErr);
err = noErr;
if (didRecordChange == true) {
err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, catRecordSize, &hint);
if (err) {
#if DEBUG_XATTR
printf ("%s: Error in replacing Catalog Record\n", __FUNCTION__);
#endif
goto out;
}
}
out:
return err;
}
static OSErr RepairAttributesCheckCBT(SGlobPtr GPtr, Boolean isHFSPlus)
{
OSErr err = noErr;
UInt16 selCode;
UInt16 recordSize;
UInt32 hint;
HFSPlusAttrKey *attrKey;
CatalogRecord catRecord;
CatalogKey catKey;
Boolean didRecordChange = false;
BTreeIterator iterator;
UInt32 curFileID;
Boolean curRecordHasAttributes = false;
Boolean curRecordHasSecurity = false;
selCode = 0x8001;
err = GetBTreeRecord(GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint);
if ( err != noErr ) goto out;
selCode = 1;
do {
if ( (catRecord.hfsPlusFile.recordType != kHFSPlusFileRecord) &&
(catRecord.hfsPlusFile.recordType != kHFSPlusFolderRecord)) {
goto getnext;
}
if ( ((catRecord.hfsPlusFile.flags & kHFSHasAttributesMask) == 0) &&
((catRecord.hfsPlusFile.flags & kHFSHasSecurityMask) == 0) ) {
goto getnext;
}
didRecordChange = false;
curRecordHasSecurity = false;
curRecordHasAttributes = false;
curFileID = catRecord.hfsPlusFile.fileID;
ClearMemory(&iterator, sizeof(BTreeIterator));
attrKey = (HFSPlusAttrKey *)&iterator.key;
attrKey->keyLength = kHFSPlusAttrKeyMinimumLength;
attrKey->fileID = curFileID;
err = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator, kInvalidMRUCacheKey, NULL, NULL, &iterator);
if (err && (err != btNotFound)) {
#if DEBUG_XATTR
printf ("%s: No matching attribute record found\n", __FUNCTION__);
#endif
goto out;
}
err = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL);
while ((err == noErr) && (attrKey->fileID == curFileID)) {
curRecordHasAttributes = true;
if (!bcmp(attrKey->attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) {
curRecordHasSecurity = true;
}
err = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL);
}
if ((curRecordHasAttributes == false) && (catRecord.hfsPlusFile.flags & kHFSHasAttributesMask)) {
catRecord.hfsPlusFile.flags &= ~kHFSHasAttributesMask;
didRecordChange = true;
}
if ((curRecordHasSecurity == false) && (catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) {
catRecord.hfsPlusFile.flags &= ~kHFSHasSecurityMask;
didRecordChange = true;
}
if (didRecordChange == true) {
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, recordSize, &hint );
if (err) {
#if DEBUG_XATTR
printf ("%s: Error writing catalog record\n", __FUNCTION__);
#endif
goto out;
}
}
getnext:
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint );
} while (err == noErr);
err = noErr;
out:
return err;
}
static OSErr RepairAttributes(SGlobPtr GPtr)
{
OSErr err = noErr;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus();
if (!isHFSPlus) {
goto out;
}
err = RepairAttributesCheckABT(GPtr, isHFSPlus);
if (err) {
goto out;
}
err = RepairAttributesCheckCBT(GPtr, isHFSPlus);
if (err) {
goto out;
}
out:
return err;
}
int cmpLongs ( const void *a, const void *b )
{
return( *(long*)a - *(long*)b );
}
static OSErr FixOverlappingExtents(SGlobPtr GPtr)
{
OSErr err = noErr;
Boolean isHFSPlus;
unsigned int i;
unsigned int numOverlapExtents = 0;
UInt32 newBlockCount;
UInt32 damagedDirID = 0;
ExtentInfo *extentInfo;
ExtentsTable **extentsTableH = GPtr->overlappedExtents;
unsigned int status = 0;
#define S_DISKFULL 0x01
#define S_SYMCREATE 0x02
#define S_LOOKDAMAGEDDIR 0x04
#define S_MOVEEXTENT 0x08
isHFSPlus = VolumeObjectIsHFSPlus();
if (isHFSPlus == false) {
err = R_RFail;
goto out;
}
if (extentsTableH == NULL) {
goto out;
}
numOverlapExtents = (**extentsTableH).count;
qsort((**extentsTableH).extentInfo, numOverlapExtents, sizeof(ExtentInfo),
CompareExtentBlockCount);
#if DEBUG_OVERLAP
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
printf ("%d: fileID = %d, startBlock = %d, blockCount = %d\n", i, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount);
}
#endif
err = UpdateVolumeBitMap(GPtr, false);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in updating on-disk bitmap\n", __FUNCTION__);
#endif
goto out;
}
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
err = BlockAllocate(GPtr->calculatedVCB, 0, extentInfo->blockCount,
extentInfo->blockCount, true,
&(extentInfo->newStartBlock), &newBlockCount);
if ((err != noErr) && (newBlockCount != extentInfo->blockCount)) {
status |= S_DISKFULL;
#if DEBUG_OVERLAP
printf ("%s: Not enough disk space to allocate extent for fileID = %d (start=%d, count=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount);
#endif
}
}
damagedDirID = CreateDirByName(GPtr, (u_char *)"DamagedFiles", kHFSRootFolderID);
if (damagedDirID == 0) {
status |= S_SYMCREATE;
#if DEBUG_OVERLAP
printf ("%s: Error in create/lookup for Damaged Files directory\n", __FUNCTION__);
#endif
}
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
if (extentInfo->newStartBlock == 0) {
continue;
}
err = MoveExtent(GPtr, extentInfo);
if (err != noErr) {
extentInfo->hasRepair = false;
#if DEBUG_OVERLAP
printf ("%s: Extent move failed for extent for fileID = %d (old=%d, new=%d, count=%d) (err=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount, err);
#endif
} else {
extentInfo->hasRepair = true;
status |= S_MOVEEXTENT;
#if DEBUG_OVERLAP
printf ("%s: Extent move success for extent for fileID = %d (old=%d, new=%d, count=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount);
#endif
}
if (damagedDirID != 0) {
err = CreateCorruptFileSymlink(GPtr, damagedDirID, extentInfo);
if (err != noErr) {
status |= S_SYMCREATE;
#if DEBUG_OVERLAP
printf ("%s: Error in creating symlink for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
} else {
status |= S_LOOKDAMAGEDDIR;
#if DEBUG_OVERLAP
printf ("%s: Created symlink for fileID = %d (old=%d, new=%d, count=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount);
#endif
}
}
}
out:
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
if (extentInfo->hasRepair == true) {
ReleaseBitmapBits (extentInfo->startBlock, extentInfo->blockCount);
}
}
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
if (extentInfo->hasRepair == false) {
CaptureBitmapBits (extentInfo->startBlock, extentInfo->blockCount);
if (extentInfo->newStartBlock != 0) {
ReleaseBitmapBits (extentInfo->newStartBlock, extentInfo->blockCount);
}
}
}
UpdateFreeBlockCount (GPtr);
if (status & S_DISKFULL) {
PrintError (GPtr, E_DiskFull, 0);
}
if (status & S_SYMCREATE) {
PrintError (GPtr, E_SymlinkCreate, 0);
}
if (status & S_LOOKDAMAGEDDIR) {
PrintStatus (GPtr, M_LookDamagedDir, 0);
}
if (status & S_MOVEEXTENT) {
err = noErr;
}
return err;
}
static int CompareExtentBlockCount(const void *first, const void *second)
{
return (((ExtentInfo *)second)->blockCount -
((ExtentInfo *)first)->blockCount);
}
static OSErr MoveExtent(SGlobPtr GPtr, ExtentInfo *extentInfo)
{
OSErr err = noErr;
Boolean isHFSPlus;
CatalogRecord catRecord;
CatalogKey catKey;
HFSPlusExtentKey extentKey;
HFSPlusExtentRecord extentData;
UInt16 recordSize;
UInt16 foundLocation;
#define CATALOG_BTREE 1
#define VOLUMEHEADER 2
#define EXTENTS_BTREE 3
UInt32 foundExtentIndex = 0;
Boolean noMoreExtents = true;
isHFSPlus = VolumeObjectIsHFSPlus();
if (extentInfo->fileID < kHFSFirstUserCatalogNodeID) {
if ((extentInfo->fileID == kHFSBadBlockFileID) ||
(extentInfo->fileID == kHFSRepairCatalogFileID) ||
(extentInfo->fileID == kHFSBogusExtentFileID)) {
#if DEBUG_OVERLAP
printf ("%s: Ignoring repair for fileID = %d\n", __FUNCTION__, extentInfo->fileID);
#endif
err = paramErr;
goto out;
}
err = SearchExtentInVH (GPtr, extentInfo, &foundExtentIndex, &noMoreExtents);
foundLocation = VOLUMEHEADER;
} else {
err = SearchExtentInCatalogBT (GPtr, extentInfo, &catKey, &catRecord,
&recordSize, &foundExtentIndex, &noMoreExtents);
foundLocation = CATALOG_BTREE;
}
if (err != noErr) {
if (noMoreExtents == false) {
err = SearchExtentInExtentBT (GPtr, extentInfo, &extentKey,
&extentData, &recordSize, &foundExtentIndex);
foundLocation = EXTENTS_BTREE;
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: No matching extent record found in extents btree for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
goto out;
}
} else {
#if DEBUG_OVERLAP
printf ("%s: No matching extent record found for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
goto out;
}
}
err = CopyDiskBlocks(GPtr, extentInfo->startBlock, extentInfo->blockCount,
extentInfo->newStartBlock);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in copying disk blocks for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
goto out;
}
if (foundLocation == CATALOG_BTREE) {
err = UpdateExtentInCatalogBT(GPtr, extentInfo, &catKey, &catRecord,
&recordSize, foundExtentIndex);
} else if (foundLocation == VOLUMEHEADER) {
err = UpdateExtentInVH(GPtr, extentInfo, foundExtentIndex);
} else if (foundLocation == EXTENTS_BTREE) {
extentData[foundExtentIndex].startBlock = extentInfo->newStartBlock;
err = UpdateExtentRecord(GPtr->calculatedVCB, NULL, &extentKey, extentData, kNoHint);
}
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in updating extent record (err=%d)\n", __FUNCTION__, err);
#endif
goto out;
}
out:
return err;
}
static OSErr CreateCorruptFileSymlink(SGlobPtr GPtr, UInt32 targetParentID, ExtentInfo *extentInfo)
{
OSErr err = noErr;
Boolean isHFSPlus;
char *filename = NULL;
unsigned int filenamelen;
char *data = NULL;
unsigned int datalen;
unsigned int filenameoffset;
unsigned int dataoffset;
UInt16 status;
UInt16 fileType;
isHFSPlus = VolumeObjectIsHFSPlus();
filenamelen = kHFSPlusMaxFileNameChars * 4;
filename = malloc(filenamelen);
if (!filename) {
err = memFullErr;
goto out;
}
datalen = PATH_MAX * 4;
data = malloc(datalen);
if (!data) {
err = memFullErr;
goto out;
}
if (extentInfo->fileID >= kHFSFirstUserCatalogNodeID) {
char *name;
char *path;
dataoffset = sprintf (data, "..");
path = data + dataoffset;
datalen -= dataoffset;
filenameoffset = sprintf (filename, "%08x ", extentInfo->fileID);
name = filename + filenameoffset;
filenamelen -= filenameoffset;
err = GetFileNamePathByID(GPtr, extentInfo->fileID, path, &datalen,
name, &filenamelen, &status);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in getting name/path for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
goto out;
}
filenamelen += filenameoffset;
datalen += dataoffset;
if (!isHFSPlus || (status & FPATH_BIGNAME) || (datalen > PATH_MAX)) {
fileType = S_IFREG;
} else {
fileType = S_IFLNK;
}
} else {
fileType = S_IFREG;
filenameoffset = sprintf (filename, "%08x ", extentInfo->fileID);
filenamelen -= filenameoffset;
err = GetSystemFileName (extentInfo->fileID, (filename + filenameoffset), &filenamelen);
filenamelen += filenameoffset;
dataoffset = sprintf (data, "System File: ");
datalen -= dataoffset;
err = GetSystemFileName (extentInfo->fileID, (data + dataoffset), &datalen);
datalen += dataoffset;
}
err = CreateFileByName (GPtr, targetParentID, fileType, (u_char *)filename,
filenamelen, (u_char *)data, datalen);
if (err == EEXIST) {
err = noErr;
}
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in creating fileType = %d for fileID = %d (err=%d)\n", __FUNCTION__, fileType, extentInfo->fileID, err);
#endif
goto out;
}
out:
if (data) {
free (data);
}
if (filename) {
free (filename);
}
return err;
}
static OSErr SearchExtentInVH(SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
{
OSErr err = fnfErr;
Boolean isHFSPlus;
SFCB *fcb = NULL;
isHFSPlus = VolumeObjectIsHFSPlus();
*noMoreExtents = true;
switch (extentInfo->fileID) {
case kHFSExtentsFileID:
fcb = GPtr->calculatedVCB->vcbExtentsFile;
break;
case kHFSCatalogFileID:
fcb = GPtr->calculatedVCB->vcbCatalogFile;
break;
case kHFSAllocationFileID:
fcb = GPtr->calculatedVCB->vcbAllocationFile;
break;
case kHFSStartupFileID:
fcb = GPtr->calculatedVCB->vcbStartupFile;
break;
case kHFSAttributesFileID:
fcb = GPtr->calculatedVCB->vcbAttributesFile;
break;
};
if (fcb != NULL) {
if (isHFSPlus) {
err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
extentInfo->blockCount, fcb->fcbExtents32,
foundExtentIndex, noMoreExtents);
} else {
err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
extentInfo->blockCount,
(*(HFSPlusExtentRecord *)fcb->fcbExtents16),
foundExtentIndex, noMoreExtents);
}
}
return err;
}
static OSErr UpdateExtentInVH (SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 foundExtentIndex)
{
OSErr err = fnfErr;
Boolean isHFSPlus;
SFCB *fcb = NULL;
isHFSPlus = VolumeObjectIsHFSPlus();
switch (extentInfo->fileID) {
case kHFSExtentsFileID:
fcb = GPtr->calculatedVCB->vcbExtentsFile;
break;
case kHFSCatalogFileID:
fcb = GPtr->calculatedVCB->vcbCatalogFile;
break;
case kHFSAllocationFileID:
fcb = GPtr->calculatedVCB->vcbAllocationFile;
break;
case kHFSStartupFileID:
fcb = GPtr->calculatedVCB->vcbStartupFile;
break;
case kHFSAttributesFileID:
fcb = GPtr->calculatedVCB->vcbAttributesFile;
break;
};
if (fcb != NULL) {
if (isHFSPlus) {
fcb->fcbExtents32[foundExtentIndex].startBlock = extentInfo->newStartBlock;
} else {
fcb->fcbExtents16[foundExtentIndex].startBlock = extentInfo->newStartBlock;
}
MarkVCBDirty(GPtr->calculatedVCB);
err = noErr;
}
return err;
}
static OSErr SearchExtentInCatalogBT(SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
{
OSErr err;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus();
err = GetCatalogRecord(GPtr, extentInfo->fileID, isHFSPlus, catKey, catRecord,
recordSize);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: No matching catalog record found for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
goto out;
}
if (isHFSPlus) {
if (extentInfo->forkType == kDataFork) {
err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
extentInfo->blockCount,
catRecord->hfsPlusFile.dataFork.extents,
foundExtentIndex, noMoreExtents);
} else {
err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
extentInfo->blockCount,
catRecord->hfsPlusFile.resourceFork.extents,
foundExtentIndex, noMoreExtents);
}
} else {
if (extentInfo->forkType == kDataFork) {
err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
extentInfo->blockCount,
(*(HFSPlusExtentRecord *)catRecord->hfsFile.dataExtents),
foundExtentIndex, noMoreExtents);
} else {
err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
extentInfo->blockCount,
(*(HFSPlusExtentRecord *)catRecord->hfsFile.rsrcExtents),
foundExtentIndex, noMoreExtents);
}
}
out:
return err;
}
static OSErr UpdateExtentInCatalogBT (SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 foundInExtentIndex)
{
OSErr err;
Boolean isHFSPlus;
UInt32 foundHint;
isHFSPlus = VolumeObjectIsHFSPlus();
if (isHFSPlus) {
if (extentInfo->forkType == kDataFork) {
catRecord->hfsPlusFile.dataFork.extents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
} else {
catRecord->hfsPlusFile.resourceFork.extents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
}
} else {
if (extentInfo->forkType == kDataFork) {
catRecord->hfsFile.dataExtents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
} else {
catRecord->hfsFile.rsrcExtents[foundInExtentIndex].startBlock = extentInfo->newStartBlock;
}
}
err = ReplaceBTreeRecord (GPtr->calculatedCatalogFCB, catKey, kNoHint,
catRecord, *recordSize, &foundHint);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in replacing catalog record for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
}
return err;
}
static OSErr SearchExtentInExtentBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusExtentKey *extentKey, HFSPlusExtentRecord *extentRecord, UInt16 *recordSize, UInt32 *foundExtentIndex)
{
OSErr err = noErr;
Boolean isHFSPlus;
Boolean noMoreExtents = true;
UInt32 hint;
isHFSPlus = VolumeObjectIsHFSPlus();
BuildExtentKey (isHFSPlus, extentInfo->forkType, extentInfo->fileID, 0, extentKey);
err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, extentKey, kNoHint,
extentKey, extentRecord, recordSize, &hint);
if ((err != noErr) && (err != btNotFound)) {
#if DEBUG_OVERLAP
printf ("%s: Error on searching first record for fileID = %d in Extents Btree (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
goto out;
}
if (err == btNotFound)
{
err = GetBTreeRecord (GPtr->calculatedExtentsFCB, 1, extentKey,
extentRecord, recordSize, &hint);
}
while (err == noErr)
{
if (isHFSPlus) {
if ((extentKey->fileID != extentInfo->fileID) ||
(extentKey->forkType != extentInfo->forkType)) {
err = fnfErr;
break;
}
} else {
if ((((HFSExtentKey *)extentKey)->fileID != extentInfo->fileID) ||
(((HFSExtentKey *)extentKey)->forkType != extentInfo->forkType)) {
err = fnfErr;
break;
}
}
err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock,
extentInfo->blockCount, *extentRecord,
foundExtentIndex, &noMoreExtents);
if (err == noErr) {
break;
}
if (noMoreExtents == true) {
err = fnfErr;
break;
}
err = GetBTreeRecord (GPtr->calculatedExtentsFCB, 1, extentKey,
extentRecord, recordSize, &hint);
}
out:
return err;
}
static OSErr FindExtentInExtentRec (Boolean isHFSPlus, UInt32 startBlock, UInt32 blockCount, const HFSPlusExtentRecord extentData, UInt32 *foundExtentIndex, Boolean *noMoreExtents)
{
OSErr err = noErr;
UInt32 numOfExtents;
Boolean foundExtent;
int i;
foundExtent = false;
*noMoreExtents = false;
*foundExtentIndex = 0;
if (isHFSPlus) {
numOfExtents = kHFSPlusExtentDensity;
} else {
numOfExtents = kHFSExtentDensity;
}
for (i=0; i<numOfExtents; i++) {
if (extentData[i].blockCount == 0) {
*noMoreExtents = true;
break;
}
if ((startBlock == extentData[i].startBlock) &&
(blockCount == extentData[i].blockCount)) {
foundExtent = true;
*foundExtentIndex = i;
break;
}
}
if (foundExtent == false) {
err = fnfErr;
}
return err;
}
OSErr GetSystemFileName(UInt32 fileID, char *filename, unsigned int *filenamelen)
{
OSErr err = noErr;
unsigned int len;
if (filename) {
len = *filenamelen - 1;
switch (fileID) {
case kHFSExtentsFileID:
strncpy (filename, "Extents Overflow BTree", len);
break;
case kHFSCatalogFileID:
strncpy (filename, "Catalog BTree", len);
break;
case kHFSAllocationFileID:
strncpy (filename, "Allocation File", len);
break;
case kHFSStartupFileID:
strncpy (filename, "Startup File", len);
break;
case kHFSAttributesFileID:
strncpy (filename, "Attributes BTree", len);
break;
case kHFSBadBlockFileID:
strncpy (filename, "Bad Allocation File", len);
break;
case kHFSRepairCatalogFileID:
strncpy (filename, "Repair Catalog File", len);
break;
case kHFSBogusExtentFileID:
strncpy (filename, "Bogus Extents File", len);
break;
default:
strncpy (filename, "Unknown File", len);
break;
};
filename[len] = '\0';
*filenamelen = strlen (filename);
}
return err;
}
struct fsPathString
{
char *name;
unsigned int namelen;
struct fsPathString *childPtr;
};
OSErr GetFileNamePathByID(SGlobPtr GPtr, UInt32 fileID, char *fullPath, unsigned int *fullPathLen, char *fileName, unsigned int *fileNameLen, u_int16_t *status)
{
OSErr err = noErr;
Boolean isHFSPlus;
UInt16 recordSize;
UInt16 curStatus = 0;
UInt32 hint;
CatalogKey catKey;
CatalogRecord catRecord;
struct fsPathString *listHeadPtr = NULL;
struct fsPathString *listTailPtr = NULL;
struct fsPathString *curPtr = NULL;
u_char *filename = NULL;
size_t namelen;
if (!fullPath && !fileName) {
goto out;
}
if (fileID < kHFSFirstUserCatalogNodeID) {
curStatus = F_RESERVE_FILEID;
err = paramErr;
goto out;
}
isHFSPlus = VolumeObjectIsHFSPlus();
if (isHFSPlus) {
filename = malloc(kHFSPlusMaxFileNameChars * 3 + 1);
} else {
filename = malloc(kHFSMaxFileNameChars + 1);
}
if (!filename) {
err = memFullErr;
#if DEBUG_OVERLAP
printf ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
#endif
goto out;
}
while (fileID != kHFSRootFolderID) {
BuildCatalogKey(fileID, NULL, isHFSPlus, &catKey);
err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint,
&catKey, &catRecord, &recordSize, &hint);
if (err) {
#if DEBUG_OVERLAP
printf ("%s: Error finding thread record for fileID = %d (err=%d)\n", __FUNCTION__, fileID, err);
#endif
goto out;
}
if ((catRecord.hfsPlusThread.recordType != kHFSPlusFileThreadRecord) &&
(catRecord.hfsPlusThread.recordType != kHFSPlusFolderThreadRecord) &&
(catRecord.hfsThread.recordType != kHFSFileThreadRecord) &&
(catRecord.hfsThread.recordType != kHFSFolderThreadRecord)) {
err = paramErr;
#if DEBUG_OVERLAP
printf ("%s: Error finding valid thread record for fileID = %d\n", __FUNCTION__, fileID);
#endif
goto out;
}
if (isHFSPlus) {
(void) utf_encodestr(catRecord.hfsPlusThread.nodeName.unicode,
catRecord.hfsPlusThread.nodeName.length * 2,
filename, &namelen);
} else {
namelen = catRecord.hfsThread.nodeName[0];
memcpy (filename, catKey.hfs.nodeName, namelen);
}
curPtr = malloc(sizeof(struct fsPathString));
if (!curPtr) {
err = memFullErr;
#if DEBUG_OVERLAP
printf ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
#endif
goto out;
}
curPtr->namelen = namelen;
curPtr->name = malloc(namelen);
if (!curPtr->name) {
err = memFullErr;
#if DEBUG_OVERLAP
printf ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
#endif
}
memcpy (curPtr->name, filename, namelen);
curPtr->childPtr = listHeadPtr;
listHeadPtr = curPtr;
if (listTailPtr == NULL) {
listTailPtr = curPtr;
}
if (isHFSPlus) {
fileID = catRecord.hfsPlusThread.parentID;
} else {
fileID = catRecord.hfsThread.parentID;
}
if (fullPath == NULL) {
break;
}
}
if (fileName) {
if (*fileNameLen < (listTailPtr->namelen + 1)) {
*fileNameLen = *fileNameLen - 1;
curStatus |= FNAME_BUF2SMALL;
} else {
*fileNameLen = listTailPtr->namelen;
}
if (*fileNameLen > NAME_MAX) {
curStatus |= FNAME_BIGNAME;
}
memcpy (fileName, listTailPtr->name, *fileNameLen);
fileName[*fileNameLen] = '\0';
}
if (fullPath) {
unsigned int bytesRemain = *fullPathLen - 1;
*fullPathLen = 0;
while (listHeadPtr != NULL) {
if (bytesRemain == 0) {
break;
}
memcpy ((fullPath + *fullPathLen), "/", 1);
*fullPathLen += 1;
bytesRemain--;
if (bytesRemain == 0) {
break;
}
if (bytesRemain < listHeadPtr->namelen) {
namelen = bytesRemain;
curStatus |= FPATH_BUF2SMALL;
} else {
namelen = listHeadPtr->namelen;
}
if (namelen > NAME_MAX) {
curStatus |= FPATH_BIGNAME;
}
memcpy ((fullPath + *fullPathLen), listHeadPtr->name, namelen);
*fullPathLen += namelen;
bytesRemain -= namelen;
curPtr = listHeadPtr;
listHeadPtr = listHeadPtr->childPtr;
free(curPtr->name);
free(curPtr);
}
fullPath[*fullPathLen] = '\0';
}
err = noErr;
out:
if (status) {
*status = curStatus;
}
while (listHeadPtr != NULL) {
curPtr = listHeadPtr;
listHeadPtr = listHeadPtr->childPtr;
if (curPtr->name) {
free (curPtr->name);
}
free (curPtr);
}
if (filename) {
free (filename);
}
return err;
}
OSErr CopyDiskBlocks(SGlobPtr GPtr, const UInt32 startAllocationBlock, const UInt32 blockCount, const UInt32 newStartAllocationBlock )
{
OSErr err = noErr;
int i;
int drive;
char *tmpBuffer = NULL;
SVCB *vcb;
UInt32 sectorsPerBlock;
UInt32 sectorsInBuffer;
UInt32 ioReqCount;
UInt32 oldDiskBlock;
UInt32 newDiskBlock;
UInt32 actBytes;
UInt32 numberOfBuffersToWrite;
tmpBuffer = malloc(DISK_IOSIZE);
if (!tmpBuffer) {
err = memFullErr;
#if DEBUG_OVERLAP
printf ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err);
#endif
goto out;
}
vcb = GPtr->calculatedVCB;
drive = vcb->vcbDriveNumber;
sectorsPerBlock = vcb->vcbBlockSize / Blk_Size;
ioReqCount = DISK_IOSIZE;
sectorsInBuffer = DISK_IOSIZE / Blk_Size;
numberOfBuffersToWrite = ((blockCount * sectorsPerBlock) + sectorsInBuffer - 1) / sectorsInBuffer;
CacheFlushRange(vcb->vcbBlockCache,
startAllocationBlock * vcb->vcbBlockSize,
blockCount * vcb->vcbBlockSize,
true);
for (i=0; i<numberOfBuffersToWrite; i++) {
if (i == (numberOfBuffersToWrite - 1)) {
ioReqCount = ((blockCount * sectorsPerBlock) - (i * sectorsInBuffer)) * Blk_Size;
}
oldDiskBlock = vcb->vcbAlBlSt + (sectorsPerBlock * startAllocationBlock) + (i * sectorsInBuffer);
err = DeviceRead(vcb->vcbDriverReadRef, drive, tmpBuffer,
(SInt64)((UInt64)oldDiskBlock << Log2BlkLo), ioReqCount, &actBytes);
if (err != noErr) {
goto out;
}
newDiskBlock = vcb->vcbAlBlSt + (sectorsPerBlock * newStartAllocationBlock) + (i * sectorsInBuffer);
err = DeviceWrite(vcb->vcbDriverWriteRef, drive, tmpBuffer,
(SInt64)((UInt64)newDiskBlock << Log2BlkLo), ioReqCount, &actBytes);
if (err != noErr) {
goto out;
}
#if 0 // DEBUG_OVERLAP
printf ("%s: Moved %d bytes of data from block %d to %d\n", __FUNCTION__, ioReqCount, oldDiskBlock, newDiskBlock);
#endif
}
out:
if (tmpBuffer) {
free (tmpBuffer);
}
return err;
}
OSErr WriteDiskBlocks(SGlobPtr GPtr, UInt32 startBlock, UInt32 blockCount, u_char *buffer, int bufLen)
{
OSErr err = noErr;
int i;
SVCB *vcb;
int drive;
u_char *dataBuffer;
UInt32 sectorsPerBlock;
UInt32 sectorsInBuffer;
UInt32 ioReqCount;
UInt32 diskBlock;
UInt32 actBytes;
UInt32 numberOfBuffersToWrite;
UInt32 bufOffset = 0;
dataBuffer = buffer;
vcb = GPtr->calculatedVCB;
drive = vcb->vcbDriveNumber;
sectorsPerBlock = vcb->vcbBlockSize / Blk_Size;
ioReqCount = DISK_IOSIZE;
sectorsInBuffer = DISK_IOSIZE / Blk_Size;
numberOfBuffersToWrite = ((blockCount * sectorsPerBlock) + sectorsInBuffer - 1) / sectorsInBuffer;
for (i=0; i<numberOfBuffersToWrite; i++) {
if (i == (numberOfBuffersToWrite - 1)) {
ioReqCount = bufLen - bufOffset;
if (ioReqCount % Blk_Size) {
ioReqCount = ((ioReqCount / Blk_Size) + 1) * Blk_Size;
} else {
ioReqCount = (ioReqCount / Blk_Size) * Blk_Size;
}
dataBuffer = calloc (1, ioReqCount);
if (!dataBuffer) {
err = memFullErr;
goto out;
}
memcpy (dataBuffer, buffer + bufOffset, (bufLen - bufOffset));
bufOffset = 0;
}
diskBlock = vcb->vcbAlBlSt + (sectorsPerBlock * startBlock) + (i * sectorsInBuffer);
err = DeviceWrite(vcb->vcbDriverWriteRef, drive, dataBuffer + bufOffset,
(SInt64)((UInt64)diskBlock << Log2BlkLo), ioReqCount, &actBytes);
if (err != noErr) {
goto out;
}
bufOffset += ioReqCount;
}
out:
if (dataBuffer != buffer) {
free (dataBuffer);
}
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;
Boolean headsUp;
UInt32 lostAndFoundDirID;
lostAndFoundDirID = 0;
headsUp = false;
for (mtp = GPtr->missingThreadList; mtp != NULL; mtp = mtp->link) {
if ( mtp->threadID == 0 )
continue;
if ( mtp->thread.parentID == 0 ) {
char myString[32];
if ( lostAndFoundDirID == 0 )
lostAndFoundDirID = CreateDirByName( GPtr , (u_char *)"lost+found", kHFSRootFolderID);
if ( lostAndFoundDirID == 0 ) {
if ( GPtr->logLevel >= kDebugLog )
printf( "\tCould not create lost+found directory \n" );
return( R_RFail );
}
sprintf( myString, "%ld", (long)mtp->threadID );
PrintError( GPtr, E_NoDir, 1, myString );
result = FixMissingDirectory( GPtr, mtp->threadID, lostAndFoundDirID );
if ( result != 0 ) {
if ( GPtr->logLevel >= kDebugLog )
printf( "\tCould not recreate a missing directory \n" );
printf ("result = %d\n", (int) result);
return( R_RFail );
}
else
headsUp = true;
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;
}
if ( headsUp )
PrintStatus( GPtr, M_Look, 0 );
return (0);
}
static OSErr
FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID )
{
Boolean isHFSPlus;
UInt16 recSize;
OSErr result;
int nameLen;
UInt32 hint;
UInt32 myItemsCount;
char myString[ 32 ];
CatalogName myName;
CatalogRecord catRec;
CatalogKey myKey, myThreadKey;
isHFSPlus = VolumeObjectIsHFSPlus( );
sprintf( myString, "%ld", (long)theObjID );
nameLen = strlen( myString );
if ( isHFSPlus )
{
int i;
myName.ustr.length = nameLen;
for ( i = 0; i < myName.ustr.length; i++ )
myName.ustr.unicode[ i ] = (u_int16_t) myString[ i ];
}
else
{
myName.pstr[0] = nameLen;
memcpy( &myName.pstr[1], &myString[0], nameLen );
}
BuildCatalogKey( theParID, &myName, isHFSPlus, &myKey );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &myKey, kNoHint,
NULL, &catRec, &recSize, &hint );
if ( result == noErr )
return( R_IntErr );
recSize = BuildThreadRec( &myKey, &catRec, isHFSPlus, true );
BuildCatalogKey( theObjID, NULL, isHFSPlus, &myThreadKey );
result = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &myThreadKey, &catRec, recSize, &hint );
if ( result != noErr )
return( result );
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &myThreadKey, kNoHint,
NULL, &catRec, &recSize, &hint );
if ( result != noErr )
return( result );
myItemsCount = 0;
for ( ;; ) {
CatalogKey foundKey;
result = GetBTreeRecord( GPtr->calculatedCatalogFCB, 1, &foundKey, &catRec, &recSize, &hint );
if ( result != noErr )
break;
if ( isHFSPlus ) {
if ( foundKey.hfsPlus.parentID != theObjID )
break;
} else {
if ( foundKey.hfs.parentID != theObjID )
break;
}
if ( catRec.recordType == kHFSPlusFolderRecord || catRec.recordType == kHFSPlusFileRecord ||
catRec.recordType == kHFSFolderRecord || catRec.recordType == kHFSFileRecord ) {
myItemsCount++;
}
}
recSize = BuildFolderRec( 01777, theObjID, isHFSPlus, &catRec );
if ( isHFSPlus )
catRec.hfsPlusFolder.valence = myItemsCount;
else
catRec.hfsFolder.valence = myItemsCount;
result = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &myKey, &catRec, recSize, &hint );
if ( result != noErr )
return( result );
result = UpdateFolderCount( GPtr->calculatedVCB, theParID, NULL,
((isHFSPlus) ? kHFSPlusFolderRecord : kHFSFolderRecord),
kNoHint, 1 );
UpdateBTreeHeader( GPtr->calculatedCatalogFCB );
return( result );
}
static HFSCatalogNodeID
GetObjectID( CatalogRecord * theRecPtr )
{
HFSCatalogNodeID myObjID;
switch ( theRecPtr->recordType ) {
case kHFSPlusFolderRecord:
myObjID = theRecPtr->hfsPlusFolder.folderID;
break;
case kHFSPlusFileRecord:
myObjID = theRecPtr->hfsPlusFile.fileID;
break;
case kHFSFolderRecord:
myObjID = theRecPtr->hfsFolder.folderID;
break;
case kHFSFileRecord:
myObjID = theRecPtr->hfsFile.fileID;
break;
default:
myObjID = 0;
}
return( myObjID );
}
OSErr CreateFileByName(SGlobPtr GPtr, UInt32 parentID, UInt16 fileType, u_char *fileName, unsigned int filenameLen, u_char *data, unsigned int dataLen)
{
OSErr err = noErr;
Boolean isHFSPlus;
Boolean isCatUpdated = false;
CatalogName fName;
CatalogRecord catRecord;
CatalogKey catKey;
CatalogKey threadKey;
UInt32 hint;
UInt16 recordSize;
UInt32 totalBlocks = 0;
UInt32 startBlock = 0;
UInt32 blockCount = 0;
UInt32 nextCNID;
isHFSPlus = VolumeObjectIsHFSPlus();
if (isHFSPlus) {
size_t namelen;
if (filenameLen < kHFSPlusMaxFileNameChars) {
(void) utf_decodestr (fileName, filenameLen, fName.ustr.unicode, &namelen);
namelen /= 2;
fName.ustr.length = namelen;
} else {
UInt16 *unicodename;
unicodename = malloc (filenameLen * 4);
if (unicodename == NULL) {
err = memFullErr;
goto out;
}
(void) utf_decodestr (fileName, filenameLen, unicodename, &namelen);
namelen /= 2;
if (namelen > kHFSPlusMaxFileNameChars) {
namelen = kHFSPlusMaxFileNameChars;
}
memcpy (fName.ustr.unicode, unicodename, (namelen * 2));
free (unicodename);
fName.ustr.length = namelen;
}
} else {
if (filenameLen > kHFSMaxFileNameChars) {
filenameLen = kHFSMaxFileNameChars;
}
fName.pstr[0] = filenameLen;
memcpy(&fName.pstr[1], fileName, filenameLen);
}
BuildCatalogKey(parentID, &fName, isHFSPlus, &catKey);
err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint, NULL,
&catRecord, &recordSize, &hint);
if (err != fsBTRecordNotFoundErr) {
#if DEBUG_OVERLAP
printf ("%s: %s probably exists in dirID = %d (err=%d)\n", __FUNCTION__, fileName, parentID, err);
#endif
err = EEXIST;
goto out;
}
if (data) {
if (dataLen % (GPtr->calculatedVCB->vcbBlockSize)) {
totalBlocks = (dataLen / (GPtr->calculatedVCB->vcbBlockSize)) + 1;
} else {
totalBlocks = dataLen / (GPtr->calculatedVCB->vcbBlockSize);
}
if (totalBlocks) {
err = BlockAllocate(GPtr->calculatedVCB, 0, totalBlocks, totalBlocks,
true, &startBlock, &blockCount);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Not enough disk space (err=%d)\n", __FUNCTION__, err);
#endif
goto out;
}
err = WriteDiskBlocks(GPtr, startBlock, blockCount, data, dataLen);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in writing data of %s to disk (err=%d)\n", __FUNCTION__, fileName, err);
#endif
goto out;
}
}
}
nextCNID = GPtr->calculatedVCB->vcbNextCatalogID;
if (!isHFSPlus && nextCNID == 0xffffFFFF) {
goto out;
}
recordSize = BuildThreadRec(&catKey, &catRecord, isHFSPlus, false);
for (;;) {
BuildCatalogKey(nextCNID, NULL, isHFSPlus, &threadKey);
err = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &threadKey, &catRecord,
recordSize, &hint );
if (err == fsBTDuplicateRecordErr && isHFSPlus) {
++nextCNID;
if (nextCNID < kHFSFirstUserCatalogNodeID) {
GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
MarkVCBDirty(GPtr->calculatedVCB);
nextCNID = kHFSFirstUserCatalogNodeID;
}
continue;
}
break;
}
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error inserting thread record for file = %s (err=%d)\n", __FUNCTION__, fileName, err);
#endif
goto out;
}
recordSize = BuildFileRec(fileType, 0666, nextCNID, isHFSPlus, &catRecord);
if (recordSize == 0) {
#if DEBUG_OVERLAP
printf ("%s: Incorrect fileType\n", __FUNCTION__);
#endif
err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in removing thread record\n", __FUNCTION__);
#endif
}
err = paramErr;
goto out;
}
if (isHFSPlus) {
catRecord.hfsPlusFile.dataFork.logicalSize = dataLen;
catRecord.hfsPlusFile.dataFork.totalBlocks = totalBlocks;
catRecord.hfsPlusFile.dataFork.extents[0].startBlock = startBlock;
catRecord.hfsPlusFile.dataFork.extents[0].blockCount = blockCount;
} else {
catRecord.hfsFile.dataLogicalSize = dataLen;
catRecord.hfsFile.dataPhysicalSize = totalBlocks * GPtr->calculatedVCB->vcbBlockSize;
catRecord.hfsFile.dataExtents[0].startBlock = startBlock;
catRecord.hfsFile.dataExtents[0].blockCount = blockCount;
}
err = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, &catRecord, recordSize, &hint );
if (err == noErr) {
isCatUpdated = true;
#if DEBUG_OVERLAP
printf ("Created \"%s\" with ID = %d startBlock = %d, blockCount = %d, dataLen = %d\n", fileName, nextCNID, startBlock, blockCount, dataLen);
#endif
} else {
#if DEBUG_OVERLAP
printf ("%s: Error in inserting file record for file = %s (err=%d)\n", __FUNCTION__, fileName, err);
#endif
err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in removing thread record\n", __FUNCTION__);
#endif
}
err = paramErr;
goto out;
}
GPtr->calculatedVCB->vcbNextCatalogID = nextCNID + 1;
if (GPtr->calculatedVCB->vcbNextCatalogID < kHFSFirstUserCatalogNodeID) {
GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
GPtr->calculatedVCB->vcbNextCatalogID = kHFSFirstUserCatalogNodeID;
}
MarkVCBDirty( GPtr->calculatedVCB );
UpdateBTreeHeader(GPtr->calculatedCatalogFCB);
err = UpdateFolderCount(GPtr->calculatedVCB, parentID, NULL, kHFSPlusFileRecord, kNoHint, 1);
if (err != noErr) {
#if DEBUG_OVERLAP
printf ("%s: Error in updating parent folder count (err=%d)\n", __FUNCTION__, err);
#endif
goto out;
}
out:
if (err && (isCatUpdated == false) && startBlock) {
ReleaseBitmapBits (startBlock, blockCount);
}
return err;
}
UInt32 CreateDirByName(SGlob *GPtr , const u_char *dirName, const UInt32 parentID)
{
Boolean isHFSPlus;
UInt16 recSize;
UInt16 myMode;
int result;
int nameLen;
UInt32 hint;
UInt32 nextCNID;
SFCB * fcbPtr;
CatalogKey myKey;
CatalogName myName;
CatalogRecord catRec;
isHFSPlus = VolumeObjectIsHFSPlus( );
fcbPtr = GPtr->calculatedCatalogFCB;
nameLen = strlen( (char *)dirName );
if ( isHFSPlus )
{
int i;
myName.ustr.length = nameLen;
for ( i = 0; i < myName.ustr.length; i++ )
myName.ustr.unicode[ i ] = (u_int16_t) dirName[ i ];
}
else
{
myName.pstr[0] = nameLen;
memcpy( &myName.pstr[1], &dirName[0], nameLen );
}
BuildCatalogKey( parentID, &myName, isHFSPlus, &myKey );
result = SearchBTreeRecord( fcbPtr, &myKey, kNoHint, NULL, &catRec, &recSize, &hint );
if ( result == noErr ) {
if ( isHFSPlus ) {
if ( catRec.recordType == kHFSPlusFolderRecord )
return( catRec.hfsPlusFolder.folderID );
}
else if ( catRec.recordType == kHFSFolderRecord )
return( catRec.hfsFolder.folderID );
return( 0 ); }
nextCNID = GPtr->calculatedVCB->vcbNextCatalogID;
if ( !isHFSPlus && nextCNID == 0xFFFFFFFF )
return( 0 );
recSize = BuildThreadRec( &myKey, &catRec, isHFSPlus, true );
for (;;) {
CatalogKey key;
BuildCatalogKey( nextCNID, NULL, isHFSPlus, &key );
result = InsertBTreeRecord( fcbPtr, &key, &catRec, recSize, &hint );
if ( result == fsBTDuplicateRecordErr && isHFSPlus ) {
++nextCNID;
if ( nextCNID < kHFSFirstUserCatalogNodeID ) {
GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
MarkVCBDirty( GPtr->calculatedVCB );
nextCNID = kHFSFirstUserCatalogNodeID;
}
continue;
}
break;
}
if ( result != 0 )
return( 0 );
myMode = ( GPtr->lostAndFoundMode == 0 ) ? 01777 : GPtr->lostAndFoundMode;
recSize = BuildFolderRec( myMode, nextCNID, isHFSPlus, &catRec );
result = InsertBTreeRecord( fcbPtr, &myKey, &catRec, recSize, &hint );
if ( result != 0 )
return( 0 );
GPtr->calculatedVCB->vcbNextCatalogID = nextCNID + 1;
if ( GPtr->calculatedVCB->vcbNextCatalogID < kHFSFirstUserCatalogNodeID ) {
GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask;
GPtr->calculatedVCB->vcbNextCatalogID = kHFSFirstUserCatalogNodeID;
}
MarkVCBDirty( GPtr->calculatedVCB );
result = UpdateFolderCount( GPtr->calculatedVCB, parentID, NULL, kHFSPlusFolderRecord, kNoHint, 1 );
UpdateBTreeHeader( GPtr->calculatedCatalogFCB );
return( nextCNID );
}
static int
BuildFolderRec( u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus, CatalogRecord * theRecPtr )
{
UInt16 recSize;
UInt32 createTime;
ClearMemory( (Ptr)theRecPtr, sizeof(*theRecPtr) );
if ( isHFSPlus ) {
createTime = GetTimeUTC();
theRecPtr->hfsPlusFolder.recordType = kHFSPlusFolderRecord;
theRecPtr->hfsPlusFolder.folderID = theObjID;
theRecPtr->hfsPlusFolder.createDate = createTime;
theRecPtr->hfsPlusFolder.contentModDate = createTime;
theRecPtr->hfsPlusFolder.attributeModDate = createTime;
theRecPtr->hfsPlusFolder.bsdInfo.ownerID = getuid( );
theRecPtr->hfsPlusFolder.bsdInfo.groupID = getgid( );
theRecPtr->hfsPlusFolder.bsdInfo.fileMode = S_IFDIR;
theRecPtr->hfsPlusFolder.bsdInfo.fileMode |= theMode;
recSize= sizeof(HFSPlusCatalogFolder);
}
else {
createTime = GetTimeLocal( true );
theRecPtr->hfsFolder.recordType = kHFSFolderRecord;
theRecPtr->hfsFolder.folderID = theObjID;
theRecPtr->hfsFolder.createDate = createTime;
theRecPtr->hfsFolder.modifyDate = createTime;
recSize= sizeof(HFSCatalogFolder);
}
return( recSize );
}
static int
BuildThreadRec( CatalogKey * theKeyPtr, CatalogRecord * theRecPtr,
Boolean isHFSPlus, Boolean isDirectory )
{
int size = 0;
if ( isHFSPlus ) {
HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)theKeyPtr;
HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)theRecPtr;
size = sizeof(HFSPlusCatalogThread);
if ( isDirectory )
rec->recordType = kHFSPlusFolderThreadRecord;
else
rec->recordType = kHFSPlusFileThreadRecord;
rec->reserved = 0;
rec->parentID = key->parentID;
bcopy(&key->nodeName, &rec->nodeName,
sizeof(UniChar) * (key->nodeName.length + 1));
size -= (sizeof(rec->nodeName.unicode) -
(rec->nodeName.length * sizeof(UniChar)));
}
else {
HFSCatalogKey *key = (HFSCatalogKey *)theKeyPtr;
HFSCatalogThread *rec = (HFSCatalogThread *)theRecPtr;
size = sizeof(HFSCatalogThread);
bzero(rec, size);
if ( isDirectory )
rec->recordType = kHFSFolderThreadRecord;
else
rec->recordType = kHFSFileThreadRecord;
rec->parentID = key->parentID;
bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
}
return (size);
}
static int BuildFileRec(UInt16 fileType, UInt16 fileMode, UInt32 fileID, Boolean isHFSPlus, CatalogRecord *catRecord)
{
UInt16 recordSize = 0;
UInt32 createTime;
if (((fileType != S_IFREG) && (fileType != S_IFLNK)) ||
((isHFSPlus == false) && (fileType == S_IFLNK))) {
goto out;
}
ClearMemory((Ptr)catRecord, sizeof(*catRecord));
if ( isHFSPlus ) {
createTime = GetTimeUTC();
catRecord->hfsPlusFile.recordType = kHFSPlusFileRecord;
catRecord->hfsPlusFile.fileID = fileID;
catRecord->hfsPlusFile.createDate = createTime;
catRecord->hfsPlusFile.contentModDate = createTime;
catRecord->hfsPlusFile.attributeModDate = createTime;
catRecord->hfsPlusFile.bsdInfo.ownerID = getuid();
catRecord->hfsPlusFile.bsdInfo.groupID = getgid();
catRecord->hfsPlusFile.bsdInfo.fileMode = fileType;
catRecord->hfsPlusFile.bsdInfo.fileMode |= fileMode;
if (fileType == S_IFLNK) {
catRecord->hfsPlusFile.userInfo.fdType = kSymLinkFileType;
catRecord->hfsPlusFile.userInfo.fdCreator = kSymLinkCreator;
} else {
catRecord->hfsPlusFile.userInfo.fdType = kTextFileType;
catRecord->hfsPlusFile.userInfo.fdCreator = kTextFileCreator;
}
recordSize= sizeof(HFSPlusCatalogFile);
}
else {
createTime = GetTimeLocal(true);
catRecord->hfsFile.recordType = kHFSFileRecord;
catRecord->hfsFile.fileID = fileID;
catRecord->hfsFile.createDate = createTime;
catRecord->hfsFile.modifyDate = createTime;
catRecord->hfsFile.userInfo.fdType = kTextFileType;
catRecord->hfsFile.userInfo.fdCreator = kTextFileCreator;
recordSize= sizeof(HFSCatalogFile);
}
out:
return(recordSize);
}