#include "Scavenger.h"
#include <unistd.h>
#include <sys/stat.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 );
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 );
static OSErr FixIllegalNames( SGlobPtr GPtr, RepairOrderPtr roPtr );
static HFSCatalogNodeID GetObjectID( CatalogRecord * theRecPtr );
static UInt32 CreateLostAndFoundDir( SGlob *GPtr );
static int BuildThreadRec( CatalogKey * theKeyPtr, CatalogRecord * theRecPtr,
Boolean isHFSPlus, Boolean isDirectory );
static int BuildFolderRec( u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus,
CatalogRecord * theRecPtr );
static OSErr FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID );
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,
&foundKey, &threadRecord, &recordSize, &hint2 );
if ( err != noErr )
{
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 );
}
else
{
break;
}
}
break;
case kHFSFolderThreadRecord:
case kHFSFileThreadRecord:
case kHFSPlusFolderThreadRecord:
case kHFSPlusFileThreadRecord:
if ( isHFSPlus )
BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, isHFSPlus, &key );
else
BuildCatalogKey( record.hfsThread.parentID, (const CatalogName *)&record.hfsThread.nodeName, isHFSPlus, &key );
err = SearchBTreeRecord ( GPtr->calculatedCatalogFCB, &key, kNoHint, &tempKey, &record2, &recordSize, &hint2 );
if ( err != noErr )
{
err = DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey );
}
break;
default:
M_DebugStr("\p Unknown record type");
break;
}
} while ( err == noErr );
if ( err == btNotFound )
err = noErr;
return( err );
}
static OSErr RepairReservedBTreeFields ( SGlobPtr GPtr )
{
CatalogRecord record;
CatalogKey foundKey;
UInt16 recordSize;
SInt16 selCode;
UInt32 hint;
UInt32 *reserved;
OSErr err;
selCode = 0x8001;
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
if ( err != noErr ) goto EXIT;
selCode = 1;
do
{
switch( record.recordType )
{
case kHFSPlusFolderRecord:
if ( record.hfsPlusFolder.flags != 0 )
{
record.hfsPlusFolder.flags = 0;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
case kHFSPlusFileRecord:
if ( ( record.hfsPlusFile.flags & (UInt16) ~(0X83) ) != 0 )
{
record.hfsPlusFile.flags &= 0X83;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
case kHFSFolderRecord:
if ( record.hfsFolder.flags != 0 )
{
record.hfsFolder.flags = 0;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
case kHFSFolderThreadRecord:
case kHFSFileThreadRecord:
reserved = (UInt32*) &(record.hfsThread.reserved);
if ( reserved[0] || reserved[1] )
{
reserved[0] = 0;
reserved[1] = 0;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
case kHFSFileRecord:
if ( ( ( record.hfsFile.flags & (UInt8) ~(0X83) ) != 0 )
|| ( record.hfsFile.dataStartBlock != 0 )
|| ( record.hfsFile.rsrcStartBlock != 0 )
|| ( record.hfsFile.reserved != 0 ) )
{
record.hfsFile.flags &= 0X83;
record.hfsFile.dataStartBlock = 0;
record.hfsFile.rsrcStartBlock = 0;
record.hfsFile.reserved = 0;
err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint );
}
break;
default:
break;
}
if ( err != noErr ) goto EXIT;
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint );
} while ( err == noErr );
if ( err == btNotFound )
err = noErr;
EXIT:
return( err );
}
static OSErr 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;
}
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 = R_RFail;
#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;
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 = CreateLostAndFoundDir( GPtr );
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" );
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 );
}
static UInt32
CreateLostAndFoundDir( SGlob *GPtr )
{
Boolean isHFSPlus;
UInt16 recSize;
UInt16 myMode;
int result;
int nameLen;
UInt32 hint;
UInt32 nextCNID;
SFCB * fcbPtr;
u_char lostAndFoundDirName[] = "lost+found";
CatalogKey myKey;
CatalogName myName;
CatalogRecord catRec;
isHFSPlus = VolumeObjectIsHFSPlus( );
fcbPtr = GPtr->calculatedCatalogFCB;
nameLen = strlen( lostAndFoundDirName );
if ( isHFSPlus )
{
int i;
myName.ustr.length = nameLen;
for ( i = 0; i < myName.ustr.length; i++ )
myName.ustr.unicode[ i ] = (u_int16_t) lostAndFoundDirName[ i ];
}
else
{
myName.pstr[0] = nameLen;
memcpy( &myName.pstr[1], &lostAndFoundDirName[0], nameLen );
}
BuildCatalogKey( kHFSRootFolderID, &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, kHFSRootFolderID, 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.backupDate = 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;
theRecPtr->hfsFolder.backupDate = 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);
}