#include "Scavenger.h"
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include "../cache.h"
enum {
clearBlocks,
addBitmapBit,
deleteExtents
};
static int MRepair( SGlobPtr GPtr );
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 FixLinkChainNext( SGlobPtr GPtr, RepairOrderPtr p );
static OSErr FixLinkChainPrev( 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 FixAttrSize(SGlobPtr GPtr, RepairOrderPtr p);
static OSErr FixOrphanAttrRecord(SGlobPtr GPtr);
static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p);
static OSErr FixHardLinkFinderInfo(SGlobPtr, RepairOrderPtr);
static OSErr FixOrphanLink(SGlobPtr GPtr, RepairOrderPtr p);
static OSErr FixOrphanInode(SGlobPtr GPtr, RepairOrderPtr p);
static OSErr FixDirLinkOwnerFlags(SGlobPtr GPtr, RepairOrderPtr p);
static int DeleteCatalogRecordByID(SGlobPtr GPtr, uint32_t id, Boolean for_rename);
static int MoveCatalogRecordByID(SGlobPtr GPtr, uint32_t id, uint32_t new_parentid);
static int DeleteAllAttrsByID(SGlobPtr GPtr, uint32_t id);
static int delete_attr_record(SGlobPtr GPtr, HFSPlusAttrKey *attr_key, HFSPlusAttrRecord *attr_record);
static int ZeroFillUnusedNodes(SGlobPtr GPtr, short fileRefNum);
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 fileID);
static OSErr SearchExtentInAttributeBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord, UInt16 *recordSize, UInt32 *foundExtentIndex);
static OSErr UpdateExtentInAttributeBT (SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord, UInt16 *recordSize, UInt32 foundInExtentIndex);
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 WriteBufferToDisk(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( SGlob*, 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);
static void BuildAttributeKey(u_int32_t fileID, u_int32_t startBlock, unsigned char *attrName, u_int16_t attrNameLen, HFSPlusAttrKey *key);
OSErr RepairVolume( SGlobPtr GPtr )
{
OSErr err;
SetDFAStage( kAboutToRepairStage ); err = CheckForStop( GPtr ); ReturnIfError( err );
SetDFAStage( kRepairStage );
err = MRepair( GPtr );
return( err );
}
static int MRepair( SGlobPtr GPtr )
{
OSErr err;
SVCB *calculatedVCB = GPtr->calculatedVCB;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus( );
if ( GPtr->CBTStat & S_RebuildBTree )
{
err = RebuildCatalogBTree( GPtr );
return( err );
}
if (GPtr->ABTStat & S_UnusedNodesNotZero)
{
err = ZeroFillUnusedNodes(GPtr, kCalculatedAttributesRefNum);
ReturnIfError(err);
}
if (GPtr->EBTStat & S_UnusedNodesNotZero)
{
err = ZeroFillUnusedNodes(GPtr, kCalculatedExtentRefNum);
ReturnIfError(err);
}
if (GPtr->CBTStat & S_UnusedNodesNotZero)
{
err = ZeroFillUnusedNodes(GPtr, kCalculatedCatalogRefNum);
ReturnIfError(err);
}
if ((calculatedVCB->vcbAttributes & kHFSUnusedNodeFixMask) == 0)
{
calculatedVCB->vcbAttributes |= kHFSUnusedNodeFixMask;
MarkVCBDirty(calculatedVCB);
}
if (GPtr->CatStat & S_FileHardLinkChain) {
err = RepairHardLinkChains(GPtr, false);
ReturnIfError(err);
}
err = CheckForStop( GPtr ); ReturnIfError( err );
if (GPtr->CatStat & S_DirHardLinkChain) {
err = RepairHardLinkChains(GPtr, true);
ReturnIfError(err);
}
err = CheckForStop( GPtr ); ReturnIfError( err ); err = DoMinorOrders( GPtr );
ReturnIfError( err );
err = CheckForStop( GPtr ); ReturnIfError( err );
GPtr->CatStat &= ~(S_FileAllocation | S_Permissions | S_UnlinkedFile | S_LinkCount | S_IllName | S_BadExtent | S_LinkErrRepair | S_FileHardLinkChain | S_DirHardLinkChain);
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->MinorRepairsP) {
err = DoMinorOrders(GPtr);
ReturnIfError( err );
}
if ( (GPtr->EBTStat & S_OrphanedExtent) != 0 ) {
err = FixOrphanedExtent( GPtr );
GPtr->EBTStat &= ~S_OrphanedExtent;
ReturnIfError( err );
}
err = CheckForStop( GPtr ); ReturnIfError( err );
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->EBTStat & S_BTH) || (GPtr->EBTStat & S_ReservedBTH) )
{
err = UpdateBTreeHeader( GPtr->calculatedExtentsFCB );
if ( (err == noErr) && (GPtr->EBTStat & S_ReservedBTH) )
{
err = FixBTreeHeaderReservedFields( GPtr, kCalculatedExtentRefNum );
}
ReturnIfError( err );
}
if ( (GPtr->EBTStat & S_BTM) != 0 )
{
err = UpdBTM( GPtr, kCalculatedExtentRefNum ); ReturnIfError( err );
}
err = CheckForStop( GPtr ); ReturnIfError( err );
if ( (GPtr->CBTStat & S_BTH) || (GPtr->CBTStat & S_ReservedBTH) )
{
err = UpdateBTreeHeader( GPtr->calculatedCatalogFCB );
if ( (err == noErr) && (GPtr->CBTStat & S_ReservedBTH) )
{
err = FixBTreeHeaderReservedFields( GPtr, kCalculatedCatalogRefNum );
}
ReturnIfError( err );
}
if ( GPtr->CBTStat & S_BTM )
{
err = UpdBTM( GPtr, kCalculatedCatalogRefNum ); ReturnIfError( err );
}
if ( (GPtr->CBTStat & S_ReservedNotZero) != 0 )
{
err = RepairReservedBTreeFields( GPtr ); ReturnIfError( err );
}
if ( (GPtr->ABTStat & S_AttrRec) )
{
err = FixOrphanAttrRecord( 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) );
}
OSErr FixBadLinkChainFirst(SGlobPtr GPtr, RepairOrderPtr p)
{
CatalogRecord rec;
uint16_t recsize;
OSErr retval = 0;
HFSPlusAttrData *attrRec;
HFSPlusAttrKey *attrKey;
BTreeIterator iterator;
FSBufferDescriptor bt_data;
u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
size_t unicode_bytes = 0;
ClearMemory(&iterator, sizeof(iterator));
retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, (CatalogKey*)&iterator.key, &rec, &recsize);
if (retval != 0) {
if (retval == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
retval = 0;
}
goto done;
}
switch (rec.recordType) {
case kHFSPlusFolderRecord: attrKey = (HFSPlusAttrKey*)&iterator.key;
utf_decodestr((unsigned char *)FIRST_LINK_XATTR_NAME,
strlen(FIRST_LINK_XATTR_NAME), attrKey->attrName,
&unicode_bytes, sizeof(attrKey->attrName));
attrKey->attrNameLen = unicode_bytes / sizeof(UniChar);
attrKey->keyLength = kHFSPlusAttrKeyMinimumLength + unicode_bytes;
attrKey->pad = 0;
attrKey->fileID = p->parid;
attrKey->startBlock = 0;
attrRec = (HFSPlusAttrData*)&attrdata[0];
attrRec->recordType = kHFSPlusAttrInlineData;
attrRec->reserved[0] = 0;
attrRec->reserved[1] = 0;
(void)snprintf((char*)&attrRec->attrData[0],
sizeof(attrRec) - (4 * sizeof(uint32_t)),
"%lu", (unsigned long)(p->correct));
attrRec->attrSize = 1 + strlen((char*)&attrRec->attrData[0]);
bt_data.bufferAddress = attrRec;
recsize = sizeof(HFSPlusAttrData) - 2 + attrRec->attrSize + ((attrRec->attrSize & 1) ? 1 : 0);
bt_data.itemSize = recsize;
bt_data.itemCount = 1;
retval = BTInsertRecord(GPtr->calculatedAttributesFCB, &iterator, &bt_data, recsize);
if (retval == btExists) {
retval = BTReplaceRecord(GPtr->calculatedAttributesFCB, &iterator, &bt_data, recsize);
}
if (retval) {
if ((GPtr->calculatedAttributesFCB->fcbPhysicalSize == 0) &&
(GPtr->calculatedAttributesFCB->fcbLogicalSize == 0) &&
(GPtr->calculatedAttributesFCB->fcbClumpSize == 0) &&
(fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
plog ("\tFixBadLinkChainFirst: Attribute btree does not exists.\n");
}
}
break;
case kHFSPlusFileRecord: rec.hfsPlusFile.hl_firstLinkID = (UInt32)p->correct;
bt_data.bufferAddress = &rec;
bt_data.itemSize = recsize;
bt_data.itemCount = 1;
retval = BTReplaceRecord(GPtr->calculatedCatalogFCB, &iterator, &bt_data, recsize);
break;
default:
retval = IntError(GPtr, R_IntErr);
break;
}
done:
return retval;
}
static OSErr FixPrivDirBadPerms(SGlobPtr GPtr, RepairOrderPtr p)
{
CatalogKey key;
CatalogRecord rec;
uint16_t recsize;
OSErr retval = 0;
UInt32 hint;
retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize);
if (retval != 0) {
if (retval == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
retval = 0;
}
goto done;
}
if (rec.recordType != kHFSPlusFolderRecord) {
retval = IntError(GPtr, R_IntErr);
goto done;
}
rec.hfsPlusFolder.bsdInfo.ownerFlags |= UF_IMMUTABLE;
rec.hfsPlusFolder.bsdInfo.fileMode |= S_ISVTX;
retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint);
done:
return retval;
}
static OSErr FixOrphanLink(SGlobPtr GPtr, RepairOrderPtr p)
{
int retval;
retval = DeleteCatalogRecordByID(GPtr, p->parid, false);
if (retval == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
retval = 0;
}
return retval;
}
static OSErr FixOrphanInode(SGlobPtr GPtr, RepairOrderPtr p)
{
int retval;
uint32_t lost_found_id;
static int msg_display = 0;
lost_found_id = CreateDirByName(GPtr, (u_char *)"lost+found",
kHFSRootFolderID);
if (lost_found_id == 0) {
retval = ENOENT;
goto out;
}
retval = MoveCatalogRecordByID(GPtr, p->parid, lost_found_id);
if (retval == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
retval = 0;
}
if (msg_display == 0) {
fsckPrint(GPtr->context, fsckLostFoundDirectory, "lost+found");
msg_display = 1;
}
out:
return retval;
}
static OSErr FixDirLinkOwnerFlags(SGlobPtr GPtr, RepairOrderPtr p)
{
CatalogKey key;
CatalogRecord rec;
uint16_t recsize;
OSErr retval = 0;
UInt32 hint;
retval = GetCatalogRecordByID(GPtr, p->parid, true, &key, &rec, &recsize);
if (retval != 0) {
if (retval == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
retval = 0;
}
goto done;
}
rec.hfsPlusFile.bsdInfo.ownerFlags = p->correct;
retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint,
&rec, recsize, &hint);
done:
return retval;
}
static OSErr FixBadFlags(SGlobPtr GPtr, RepairOrderPtr p)
{
CatalogKey key;
CatalogRecord rec;
uint16_t recsize;
OSErr retval = 0;
UInt32 hint;
retval = GetCatalogRecordByID(GPtr, p->parid, true, &key, &rec, &recsize);
if (retval != 0) {
if (retval == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
retval = 0;
}
goto done;
}
if (p->type == E_DirInodeBadFlags) {
if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
fplog(stderr, "\tFixBadFlags (folder): old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct);
}
rec.hfsPlusFolder.flags = p->correct;
} else if (p->type == E_DirLinkAncestorFlags) {
if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
fplog(stderr, "\tFixBadFlags (parent folder): old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct);
}
rec.hfsPlusFolder.flags = p->correct;
} else {
if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
fplog(stderr, "\tFixBadFlags (file): old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct);
}
rec.hfsPlusFile.flags = p->correct;
}
retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint,
&rec, recsize, &hint);
done:
return retval;
}
OSErr UpdFolderCount( SGlobPtr GPtr, RepairOrderPtr p)
{
OSErr result = -1;
CatalogRecord record;
CatalogKey key, foundKey;
UInt16 recSize = 0;
UInt32 hint = 0;
#define DPRINT(where, fmt, ...) \
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) \
fplog(where, fmt, ## __VA_ARGS__);
BuildCatalogKey( p->parid, NULL, true, &key);
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
&foundKey, &record, &recSize, &hint);
if (result) {
if (result == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
return 0;
} else {
DPRINT(stderr, "\tUpdFolderCount: first SearchBTreeRecord failed, parid = %u, result = %d\n", p->parid, result);
return IntError(GPtr, R_IntErr);
}
}
if (record.recordType != kHFSPlusFolderThreadRecord) {
GPtr->CBTStat |= S_Orphan;
GPtr->minorRepairFalseSuccess = true;
return 0;
}
BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, true, &key);
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
&foundKey, &record, &recSize, &hint);
if (result) {
DPRINT(stderr, "UpdFolderCount: second SearchBTreeRecord failed (thread.parentID = %u, result = %d), just returning without complaint\n", record.hfsPlusThread.parentID, result);
return 0;
}
if (record.recordType != kHFSPlusFolderRecord) {
DPRINT(stderr, "UpdFolderCount: actual record type (%d) != FolderRecord\n", record.recordType);
return IntError(GPtr, R_IntErr);
}
#if 0
if ((UInt32)p->incorrect != record.hfsPlusFolder.folderCount) {
DPRINT(stderr, "UpdFolderCount: incorrect (%u) != expected folderCount (%u)\n", (UInt32)p->incorrect, record.hfsPlusFolder.folderCount);
return IntError( GPtr, R_IntErr);
}
#else
if (record.hfsPlusFolder.folderCount == p->correct) {
return noErr;
}
#endif
record.hfsPlusFolder.folderCount = p->correct;
result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint,
&record, recSize, &hint);
if (result) {
DPRINT(stderr, "UpdFolderCount: ReplaceBTreeRecord failed (%d)\n", result);
return IntError( GPtr, R_IntErr );
}
return noErr;
}
#undef DPRINT
OSErr UpdHasFolderCount( SGlobPtr GPtr, RepairOrderPtr p)
{
OSErr result = -1;
CatalogRecord record;
CatalogKey key, foundKey;
UInt16 recSize = 0;
UInt32 hint = 0;
BuildCatalogKey( p->parid, NULL, true, &key);
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
&foundKey, &record, &recSize, &hint);
if (result) {
if (result == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
return 0;
} else {
return IntError(GPtr, R_IntErr);
}
}
if (record.recordType != kHFSPlusFolderThreadRecord) {
GPtr->CBTStat |= S_Orphan;
GPtr->minorRepairFalseSuccess = true;
return 0;
}
BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, true, &key);
result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint,
&foundKey, &record, &recSize, &hint);
if (result) {
return IntError(GPtr, R_IntErr);
}
if (record.recordType != kHFSPlusFolderRecord) {
return IntError(GPtr, R_IntErr);
}
if ((record.hfsPlusFolder.flags & kHFSHasFolderCountMask) == 0) {
record.hfsPlusFolder.flags |= kHFSHasFolderCountMask;
result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint,
&record, recSize, &hint);
if (result) {
return IntError( GPtr, R_IntErr );
}
}
return noErr;
}
static OSErr DoMinorOrders( SGlobPtr GPtr ) {
RepairOrderPtr p;
RepairOrderPtr cur;
OSErr err = noErr;
cur = GPtr->MinorRepairsP;
GPtr->MinorRepairsP = NULL;
while( (p = cur) && (err == noErr) ) {
cur = p->link;
GPtr->minorRepairFalseSuccess = false;
switch( p->type ) {
case E_FldCount: err = UpdFolderCount( GPtr, p );
break;
case E_HsFldCount: err = UpdHasFolderCount( GPtr, p );
break;
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_InvalidLinkChainPrev:
err = FixLinkChainPrev( GPtr, p );
break;
case E_InvalidLinkChainNext:
err = FixLinkChainNext( GPtr, p );
break;
case E_DirHardLinkFinderInfo:
case E_FileHardLinkFinderInfo:
err = FixHardLinkFinderInfo( GPtr, p );
break;
case E_InvalidPermissions:
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;
case E_PEOAttr:
case E_LEOAttr:
err = FixAttrSize(GPtr, p);
break;
case E_ExtEnt:
err = FixBadExtent(GPtr, p);
break;
case E_DirInodeBadFlags:
case E_DirLinkAncestorFlags:
case E_FileInodeBadFlags:
case E_DirLinkBadFlags:
case E_FileLinkBadFlags:
err = FixBadFlags(GPtr, p);
break;
case E_BadPermPrivDir:
err = FixPrivDirBadPerms(GPtr, p);
break;
case E_InvalidLinkChainFirst:
err = FixBadLinkChainFirst(GPtr, p);
break;
case E_OrphanFileLink:
case E_OrphanDirLink:
err = FixOrphanLink(GPtr, p);
break;
case E_OrphanFileInode:
case E_OrphanDirInode:
err = FixOrphanInode(GPtr, p);
break;
case E_DirHardLinkOwnerFlags:
err = FixDirLinkOwnerFlags(GPtr, p);
break;
default: if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\tUnknown repair order found (type = %d)\n", p->type);
}
err = IntError( GPtr, R_IntErr ); break;
}
if ((err != 0) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) {
plog ("\tDoMinorRepair: Repair for type=%d failed (err=%d).\n", p->type, err);
}
if (GPtr->minorRepairFalseSuccess == true) {
p->link = GPtr->MinorRepairsP;
GPtr->MinorRepairsP = p;
} else {
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 != largeCatalogFolderP->userInfo.frFlags)
{
if ( p->correct < p->incorrect )
largeCatalogFolderP->userInfo.frFlags &= ~((UInt16)p->maskBit);
else
largeCatalogFolderP->userInfo.frFlags |= (UInt16)p->maskBit;
}
else
{
largeCatalogFolderP->userInfo.frFlags = (UInt16)p->correct;
}
}
else
{
HFSCatalogFolder *smallCatalogFolderP = (HFSCatalogFolder *) &record;
if ( p->incorrect != smallCatalogFolderP->userInfo.frFlags) {
if ( p->correct < p->incorrect )
smallCatalogFolderP->userInfo.frFlags &= ~((UInt16)p->maskBit);
else
smallCatalogFolderP->userInfo.frFlags |= (UInt16)p->maskBit;
}
else
{
smallCatalogFolderP->userInfo.frFlags = (UInt16)p->correct;
}
}
result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recSize, &hint ); if ( result )
return( IntError( GPtr, result ) );
return( noErr ); }
static OSErr FixHardLinkFinderInfo(SGlobPtr GPtr, RepairOrderPtr p)
{
CatalogRecord rec;
CatalogKey key;
uint16_t recsize;
OSErr retval = 0;
UInt32 hint;
retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize);
if (retval != 0) {
if (retval == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
retval = 0;
}
goto done;
}
if (rec.recordType != kHFSPlusFileRecord) {
retval = IntError(GPtr, R_IntErr);
goto done;
}
if (p->type == E_DirHardLinkFinderInfo) {
rec.hfsPlusFile.userInfo.fdType = kHFSAliasType;
rec.hfsPlusFile.userInfo.fdCreator = kHFSAliasCreator;
rec.hfsPlusFile.userInfo.fdFlags |= kIsAlias;
} else if (p->type == E_FileHardLinkFinderInfo) {
rec.hfsPlusFile.userInfo.fdType = kHardLinkFileType;
rec.hfsPlusFile.userInfo.fdCreator = kHFSPlusCreator;
} else {
retval = IntError(GPtr, R_IntErr);
goto done;
}
retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint);
done:
return retval;
}
static OSErr
FixLinkChainPrev(SGlobPtr GPtr, RepairOrderPtr p)
{
SFCB *fcb;
CatalogRecord rec;
CatalogKey key, foundKey;
UInt16 recSize;
OSErr result;
Boolean isHFSPlus;
UInt32 hint = 0;
isHFSPlus = VolumeObjectIsHFSPlus( );
if (!isHFSPlus)
return (0);
fcb = GPtr->calculatedCatalogFCB;
BuildCatalogKey( p->parid, NULL, true, &key );
result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint );
if (result) {
if (result == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
return 0;
} else {
return IntError(GPtr, R_IntErr);
}
}
if (rec.recordType != kHFSPlusFileThreadRecord) {
return IntError(GPtr, R_IntErr);
}
BuildCatalogKey( rec.hfsPlusThread.parentID, (const CatalogName*)&rec.hfsPlusThread.nodeName, true, &key);
result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint);
if (result) {
return IntError(GPtr, R_IntErr);
}
if (rec.recordType != kHFSPlusFileRecord) {
return IntError(GPtr, R_IntErr);
}
if ((UInt32)p->incorrect != rec.hfsPlusFile.hl_prevLinkID) {
return IntError(GPtr, R_IntErr);
}
rec.hfsPlusFile.hl_prevLinkID = (UInt32)p->correct;
result = ReplaceBTreeRecord(fcb, &foundKey, hint, &rec, recSize, &hint);
if (result) {
return IntError(GPtr, R_IntErr);
}
return noErr;
}
static OSErr
FixLinkChainNext(SGlobPtr GPtr, RepairOrderPtr p)
{
SFCB *fcb;
CatalogRecord rec;
CatalogKey key, foundKey;
UInt16 recSize;
OSErr result;
Boolean isHFSPlus;
UInt32 hint = 0;
isHFSPlus = VolumeObjectIsHFSPlus( );
if (!isHFSPlus)
return (0);
fcb = GPtr->calculatedCatalogFCB;
BuildCatalogKey( p->parid, NULL, true, &key );
result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint );
if (result) {
if (result == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
return 0;
} else {
return IntError(GPtr, R_IntErr);
}
}
if (rec.recordType != kHFSPlusFileThreadRecord) {
return IntError(GPtr, R_IntErr);
}
BuildCatalogKey( rec.hfsPlusThread.parentID, (const CatalogName*)&rec.hfsPlusThread.nodeName, true, &key);
result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint);
if (result) {
return IntError(GPtr, R_IntErr);
}
if (rec.recordType != kHFSPlusFileRecord) {
return IntError(GPtr, R_IntErr);
}
if ((UInt32)p->incorrect != rec.hfsPlusFile.hl_nextLinkID) {
return IntError(GPtr, R_IntErr);
}
rec.hfsPlusFile.hl_nextLinkID = (UInt32)p->correct;
result = ReplaceBTreeRecord(fcb, &foundKey, hint, &rec, recSize, &hint);
if (result) {
return IntError(GPtr, R_IntErr);
}
return noErr;
}
static OSErr
FixLinkCount(SGlobPtr GPtr, RepairOrderPtr p)
{
CatalogRecord rec;
CatalogKey key;
OSErr result;
UInt16 recSize;
UInt32 hint;
Boolean isHFSPlus;
Boolean isdir = 0;
int lc;
isHFSPlus = VolumeObjectIsHFSPlus( );
if (!isHFSPlus)
return (0);
result = GetCatalogRecordByID(GPtr, p->parid, isHFSPlus, &key, &rec, &recSize);
if (result) {
if (result == btNotFound) {
GPtr->minorRepairFalseSuccess = true;
result = 0;
}
return result;
}
if (rec.recordType != kHFSPlusFileRecord && rec.recordType != kHFSPlusFolderRecord)
return (noErr);
isdir = (rec.recordType == kHFSPlusFolderRecord);
lc = (isdir ? rec.hfsPlusFolder.bsdInfo.special.linkCount : rec.hfsPlusFile.bsdInfo.special.linkCount);
if ((UInt32)p->correct != lc) {
if (isdir)
rec.hfsPlusFolder.bsdInfo.special.linkCount = (UInt32)p->correct;
else
rec.hfsPlusFile.bsdInfo.special.linkCount = (UInt32)p->correct;
result = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key,
kNoHint, &rec, recSize, &hint);
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 ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
plog( "\treplacement name already exists \n" );
plog( "\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 ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
plog( "\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, sizeof(filename));
filename[namelen] = '\0';
if (p->type == E_InvalidPermissions &&
((UInt16)p->incorrect == rec.hfsPlusFile.bsdInfo.fileMode)) {
if (fsckGetVerbosity(GPtr->context) >= kDebugLog)
plog("\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 (result)
return (IntError(GPtr, result));
else
return (noErr);
}
static OSErr
DeleteUnlinkedFile(SGlobPtr GPtr, RepairOrderPtr p)
{
OSErr result = -1;
CatalogName name;
CatalogName *cNameP;
Boolean isHFSPlus;
size_t len;
CatalogKey key;
CatalogRecord record;
uint32_t id = 0;
UInt16 recSize;
UInt32 hint;
isHFSPlus = VolumeObjectIsHFSPlus( );
if (!isHFSPlus)
return (0);
if (p->name[0] > 0) {
(void) utf_decodestr(&p->name[1], p->name[0], name.ustr.unicode, &len, sizeof(name.ustr.unicode));
name.ustr.length = len / 2;
cNameP = &name;
} else {
cNameP = NULL;
goto out;
}
BuildCatalogKey(p->parid, cNameP, true, &key);
result = SearchBTreeRecord (GPtr->calculatedCatalogFCB, &key, kNoHint,
NULL, &record, &recSize, &hint);
if (result) {
if (result == btNotFound) {
result = 0;
}
goto out;
}
result = DeleteCatalogNode(GPtr->calculatedVCB, p->parid, cNameP, p->hint, false);
if (result) {
goto out;
}
GPtr->VIStat |= S_MDB;
GPtr->VIStat |= S_VBM;
if (record.recordType == kHFSPlusFileRecord) {
id = record.hfsPlusFile.fileID;
} else if (record.recordType == kHFSPlusFolderRecord) {
id = record.hfsPlusFolder.folderID;
}
result = DeleteAllAttrsByID(GPtr, id);
out:
return result;
}
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, sizeof(keyp->nodeName.unicode));
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 FixAttrSize(SGlobPtr GPtr, RepairOrderPtr p)
{
OSErr result = noErr;
HFSPlusAttrKey *key;
HFSPlusAttrRecord record;
BTreeIterator iterator;
FSBufferDescriptor btRecord;
u_int16_t recSize;
u_int64_t bytes;
Boolean doReplace = false;
ClearMemory(&iterator, sizeof(iterator));
key = (HFSPlusAttrKey *)&iterator.key;
BuildAttributeKey(p->parid, 0, &p->name[1], p->name[0], key);
btRecord.bufferAddress = &record;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof(record);
result = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator,
kInvalidMRUCacheKey, &btRecord, &recSize, &iterator);
if (result) {
dprintf (d_error|d_xattr, "%s: Cannot find attribute record (err = %d)\n", __FUNCTION__, result);
goto out;
}
if (record.recordType != kHFSPlusAttrForkData) {
dprintf (d_error|d_xattr, "%s: Record found is not attribute fork data\n", __FUNCTION__);
result = btNotFound;
goto out;
}
if (p->type == E_PEOAttr) {
if ((u_int32_t)p->incorrect == record.forkData.theFork.totalBlocks) {
record.forkData.theFork.totalBlocks = (u_int32_t)p->correct;
doReplace = true;
bytes = p->correct * (u_int64_t)GPtr->calculatedVCB->vcbBlockSize;
if (record.forkData.theFork.logicalSize > bytes) {
record.forkData.theFork.logicalSize = bytes;
}
}
} else if (p->type == E_LEOAttr) {
if (p->incorrect == record.forkData.theFork.logicalSize) {
record.forkData.theFork.logicalSize = p->correct;
doReplace = true;
}
}
if (doReplace == true) {
result = BTReplaceRecord(GPtr->calculatedAttributesFCB, &iterator,
&btRecord, recSize);
if (result) {
dprintf (d_error|d_xattr, "%s: Cannot replace attribute record (err=%d)\n", __FUNCTION__, result);
goto out;
}
}
out:
return(result);
}
static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p)
{
OSErr err = noErr;
UInt32 badExtentIndex;
UInt32 extentStartBlock, foundStartBlock;
UInt32 fileID;
int i;
UInt8 forkType;
UInt16 recordSize;
ExtentRecord extentRecord;
ExtentKey extentKey;
UInt32 hint;
Boolean isHFSPlus;
Boolean didRepair;
fileID = p->parid;
badExtentIndex = p->correct;
extentStartBlock = p->hint;
forkType = p->forkType;
isHFSPlus = VolumeObjectIsHFSPlus();
didRepair = false;
assert (forkType != kEAData);
if (extentStartBlock == 0) {
CatalogRecord catRecord;
CatalogKey catKey;
HFSPlusExtentDescriptor *hfsPlusExtent;
HFSExtentDescriptor *hfsExtent;
err = GetCatalogRecord(GPtr, fileID, isHFSPlus, &catKey, &catRecord, &recordSize);
if (err) {
goto out;
}
assert ((catRecord.recordType == kHFSPlusFileRecord) ||
(catRecord.recordType == kHFSFileRecord));
if (isHFSPlus) {
if (forkType == kDataFork) {
hfsPlusExtent = catRecord.hfsPlusFile.dataFork.extents;
} else {
hfsPlusExtent = catRecord.hfsPlusFile.resourceFork.extents;
}
for (i = badExtentIndex; i < GPtr->numExtents; i++) {
hfsPlusExtent[i].startBlock = 0;
hfsPlusExtent[i].blockCount = 0;
}
} else {
if (forkType == kDataFork) {
hfsExtent = catRecord.hfsFile.dataExtents;
} else {
hfsExtent = catRecord.hfsFile.rsrcExtents;
}
for (i = badExtentIndex; i < GPtr->numExtents; i++) {
hfsExtent[i].startBlock = 0;
hfsExtent[i].blockCount = 0;
}
}
err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint,
&catRecord, recordSize, &hint);
if (err) {
goto out;
}
didRepair = true;
} else {
if (badExtentIndex == 0) {
goto del_overflow_extents;
}
BuildExtentKey (isHFSPlus, forkType, fileID, extentStartBlock, &extentKey);
err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, &extentKey, kNoHint,
&extentKey, &extentRecord, &recordSize, &hint);
if (err) {
goto out;
}
if (isHFSPlus) {
for (i = badExtentIndex; i < GPtr->numExtents; i++) {
extentRecord.hfsPlus[i].startBlock = 0;
extentRecord.hfsPlus[i].blockCount = 0;
}
} else {
for (i = badExtentIndex; i < GPtr->numExtents; i++) {
extentRecord.hfs[i].startBlock = 0;
extentRecord.hfs[i].blockCount = 0;
}
}
err = ReplaceBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey, hint,
&extentRecord, recordSize, &hint);
if (err) {
goto out;
}
didRepair = true;
extentStartBlock++;
}
del_overflow_extents:
BuildExtentKey (isHFSPlus, forkType, fileID, extentStartBlock, &extentKey);
err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, &extentKey, kNoHint,
&extentKey, &extentRecord, &recordSize, &hint);
if ((err != noErr) && (err != btNotFound)) {
goto create_symlink;
}
if (err == btNotFound) {
err = GetBTreeRecord(GPtr->calculatedExtentsFCB, 1, &extentKey, &extentRecord,
&recordSize, &hint);
}
while (err == noErr) {
if (isHFSPlus) {
if ((fileID != extentKey.hfsPlus.fileID) ||
(forkType != extentKey.hfsPlus.forkType)) {
break;
}
foundStartBlock = extentKey.hfsPlus.startBlock;
} else {
if ((fileID != extentKey.hfs.fileID) ||
(forkType != extentKey.hfs.forkType)) {
break;
}
foundStartBlock = extentKey.hfs.startBlock;
}
err = DeleteBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey);
dprintf (d_info, "%s: Deleting extent overflow for fileID=%u, forkType=%u, startBlock=%u\n", __FUNCTION__, fileID, forkType, foundStartBlock);
if (err) {
goto create_symlink;
}
didRepair = true;
err = GetBTreeRecord(GPtr->calculatedExtentsFCB, 1, &extentKey, &extentRecord,
&recordSize, &hint);
}
if (err == btNotFound) {
err = noErr;
}
UpdateBTreeHeader(GPtr->calculatedExtentsFCB);
create_symlink:
if (didRepair == true) {
(void) CreateCorruptFileSymlink(GPtr, fileID);
}
out:
return err;
}
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 foundRecType;
SInt16 selCode = 0x8001;
Boolean isHFSPlus;
BTreeControlBlock *btcb = GetBTreeControlBlock( kCalculatedCatalogRefNum );
Boolean isDirectory;
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;
isDirectory = false;
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;
isDirectory = true;
break;
case kHFSFileRecord:
cNodeID = record.hfsFile.fileID;
break;
case kHFSPlusFolderRecord:
cNodeID = record.hfsPlusFolder.folderID;
isDirectory = true;
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) {
foundRecType = threadRecord.hfsPlusThread.recordType;
if (isDirectory == true) {
if (foundRecType != kHFSPlusFolderThreadRecord) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
}
}
} else {
if (foundRecType != kHFSPlusFileThreadRecord) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
}
}
}
if (parentID != threadRecord.hfsPlusThread.parentID) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\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)))) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID);
}
}
if (err == btNotFound) {
(void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
}
} else {
foundRecType = threadRecord.hfsThread.recordType;
if (isDirectory == true) {
if (foundRecType != kHFSFolderThreadRecord) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
}
}
} else {
if (foundRecType != kHFSFileThreadRecord) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
}
}
}
if (parentID != threadRecord.hfsThread.parentID) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\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])))) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID);
}
}
if (err == btNotFound) {
(void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey);
}
}
}
if ( err == btNotFound )
{
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 (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: Created thread record for id=%u (err=%u)\n", __FUNCTION__, cNodeID, err);
}
}
break;
case kHFSFolderThreadRecord:
case kHFSPlusFolderThreadRecord:
isDirectory = true;
case kHFSFileThreadRecord:
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) {
foundRecType = record2.hfsPlusFile.recordType;
if (isDirectory == true) {
if (foundRecType != kHFSPlusFolderRecord) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
}
}
} else {
if (foundRecType != kHFSPlusFileRecord) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
}
}
}
if (foundKey.hfsPlus.parentID != record2.hfsPlusFile.fileID) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfsPlus.parentID, record2.hfsPlusFile.fileID, record.hfsPlusThread.parentID);
}
}
} else {
foundRecType = record2.hfsFile.recordType;
if (isDirectory == true) {
if (foundRecType != kHFSFolderRecord) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
}
}
} else {
if (foundRecType != kHFSFileRecord) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType);
}
}
}
if (foundKey.hfs.parentID != record2.hfsFile.fileID) {
err = btNotFound;
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
if (recordType == kHFSFolderThreadRecord) {
plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFolder.folderID, record.hfsThread.parentID);
} else {
plog ("\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 (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
if (isHFSPlus) {
plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfsPlus.parentID, err);
} else {
plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfs.parentID, err);
}
}
}
break;
default:
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\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 FixOrphanAttrRecord(SGlobPtr GPtr)
{
OSErr err = noErr;
UInt16 selCode;
UInt32 hint;
HFSPlusAttrRecord record;
HFSPlusAttrKey key;
UInt16 recordSize;
bzero (&(GPtr->lastAttrInfo), sizeof(GPtr->lastAttrInfo));
selCode = 0x8001;
err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &key,
&record, &recordSize, &hint);
if (err != noErr) {
goto out;
}
selCode = 1;
do {
err = CheckAttributeRecord(GPtr, &key, &record, recordSize);
if (err) {
break;
}
err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &key,
&record, &recordSize, &hint);
} while (err == noErr);
if (err == btNotFound) {
err = noErr;
}
out:
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
plog ("%s: No matching catalog thread record found\n", __FUNCTION__);
#endif
goto out;
}
#if DEBUG_XATTR
plog ("%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
plog ("%s: No matching catalog record found\n", __FUNCTION__);
#endif
goto out;
}
#if DEBUG_XATTR
plog ("%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;
attributeInfo 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 == btNotFound) {
err = noErr;
goto out;
}
if (err != noErr) goto out;
selCode = 1;
do {
#if DEBUG_XATTR
(void) utf_encodestr(attrKey.attrName, attrKey.attrNameLen * 2, attrName, &len, sizeof(attrName));
attrName[len] = '\0';
plog ("%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 (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: Error in replacing catalog record for id=%u\n", __FUNCTION__, lastID.fileID);
}
goto out;
}
}
didRecordChange = false;
err = GetCatalogRecord(GPtr, attrKey.fileID, isHFSPlus, &catKey, &catRecord, &catRecordSize);
if (err) {
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: No matching catalog record found for id=%u\n", __FUNCTION__, attrKey.fileID);
}
if ((attrKey.fileID < kHFSFirstUserCatalogNodeID) &&
(attrKey.fileID != kHFSRootFolderID)) {
#if DEBUG_XATTR
plog ("%s: Ignore catalog check for fileID=%d for attribute=%s\n", __FUNCTION__, attrKey.fileID, attrName);
#endif
goto getnext;
}
err = delete_attr_record(GPtr, &attrKey, &attrRecord);
if (err) {
if (fsckGetVerbosity(GPtr->context) >= kDebugLog) {
plog ("\t%s: Error in deleting attribute record for id=%u\n", __FUNCTION__, attrKey.fileID);
}
goto out;
}
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
plog ("%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
plog ("%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
plog ("%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;
ExtentInfo *extentInfo;
ExtentsTable **extentsTableH = GPtr->overlappedExtents;
unsigned int status = 0;
#define S_DISKFULL 0x01
#define S_MOVEEXTENT 0x02
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]);
plog ("%d: fileID = %d, startBlock = %d, blockCount = %d\n", i, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount);
}
#endif
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
err = AllocateContigBitmapBits (GPtr->calculatedVCB, extentInfo->blockCount, &(extentInfo->newStartBlock));
if ((err != noErr)) {
status |= S_DISKFULL;
#if DEBUG_OVERLAP
plog ("%s: Not enough disk space to allocate extent for fileID = %d (start=%d, count=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount);
#endif
}
}
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
if (extentInfo->newStartBlock == 0) {
continue;
}
err = MoveExtent(GPtr, extentInfo);
if (err != noErr) {
extentInfo->didRepair = false;
#if DEBUG_OVERLAP
plog ("%s: Extent move failed for extent for fileID = %u (old=%u, new=%u, count=%u) (err=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount, err);
#endif
} else {
extentInfo->didRepair = true;
status |= S_MOVEEXTENT;
#if DEBUG_OVERLAP
plog ("%s: Extent move success for extent for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount);
#endif
}
err = CreateCorruptFileSymlink(GPtr, extentInfo->fileID);
if (err != noErr) {
#if DEBUG_OVERLAP
plog ("%s: Error in creating symlink for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
#endif
} else {
#if DEBUG_OVERLAP
plog ("%s: Created symlink for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount);
#endif
}
}
out:
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
if (extentInfo->didRepair == true) {
ReleaseBitmapBits (extentInfo->startBlock, extentInfo->blockCount);
}
}
for (i=0; i<numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
if (extentInfo->didRepair == false) {
CaptureBitmapBits (extentInfo->startBlock, extentInfo->blockCount);
if (extentInfo->newStartBlock != 0) {
ReleaseBitmapBits (extentInfo->newStartBlock, extentInfo->blockCount);
}
}
}
UpdateFreeBlockCount (GPtr);
if (status & S_DISKFULL) {
fsckPrint(GPtr->context, E_DiskFull);
}
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;
HFSPlusAttrKey attrKey;
HFSPlusAttrRecord attrRecord;
UInt16 recordSize;
enum locationTypes {volumeHeader, catalogBTree, extentsBTree, attributeBTree} foundLocation;
UInt32 foundExtentIndex = 0;
Boolean noMoreExtents = true;
isHFSPlus = VolumeObjectIsHFSPlus();
if (extentInfo->forkType == kEAData) {
assert(isHFSPlus == true);
err = SearchExtentInAttributeBT (GPtr, extentInfo, &attrKey, &attrRecord,
&recordSize, &foundExtentIndex);
if (err != noErr) {
goto out;
}
foundLocation = attributeBTree;
} else {
if (extentInfo->fileID < kHFSFirstUserCatalogNodeID) {
if ((extentInfo->fileID == kHFSBadBlockFileID) ||
(extentInfo->fileID == kHFSRepairCatalogFileID) ||
(extentInfo->fileID == kHFSBogusExtentFileID)) {
err = paramErr;
goto out;
}
err = SearchExtentInVH (GPtr, extentInfo, &foundExtentIndex, &noMoreExtents);
foundLocation = volumeHeader;
} else {
err = SearchExtentInCatalogBT (GPtr, extentInfo, &catKey, &catRecord,
&recordSize, &foundExtentIndex, &noMoreExtents);
foundLocation = catalogBTree;
}
if (err != noErr) {
if (noMoreExtents == false) {
err = SearchExtentInExtentBT (GPtr, extentInfo, &extentKey,
&extentData, &recordSize, &foundExtentIndex);
foundLocation = extentsBTree;
if (err != noErr) {
dprintf (d_error|d_overlap, "%s: No matching extent record found in extents btree for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
goto out;
}
} else {
dprintf (d_error|d_overlap, "%s: No matching extent record found for fileID = %d\n", __FUNCTION__, extentInfo->fileID);
goto out;
}
}
}
err = CopyDiskBlocks(GPtr, extentInfo->startBlock, extentInfo->blockCount,
extentInfo->newStartBlock);
if (err != noErr) {
dprintf (d_error|d_overlap, "%s: Error in copying disk blocks for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
goto out;
}
if (foundLocation == catalogBTree) {
err = UpdateExtentInCatalogBT(GPtr, extentInfo, &catKey, &catRecord,
&recordSize, foundExtentIndex);
} else if (foundLocation == volumeHeader) {
err = UpdateExtentInVH(GPtr, extentInfo, foundExtentIndex);
} else if (foundLocation == extentsBTree) {
extentData[foundExtentIndex].startBlock = extentInfo->newStartBlock;
err = UpdateExtentRecord(GPtr->calculatedVCB, NULL, &extentKey, extentData, kNoHint);
} else if (foundLocation == attributeBTree) {
err = UpdateExtentInAttributeBT(GPtr, extentInfo, &attrKey, &attrRecord,
&recordSize, foundExtentIndex);
}
if (err != noErr) {
dprintf (d_error|d_overlap, "%s: Error in updating extent record for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err);
goto out;
}
out:
return err;
}
static OSErr CreateCorruptFileSymlink(SGlobPtr GPtr, UInt32 fileID)
{
OSErr err = noErr;
Boolean isHFSPlus;
char *filename = NULL;
unsigned int filenamelen;
char *data = NULL;
unsigned int datalen;
unsigned int filenameoffset;
unsigned int dataoffset;
UInt32 damagedDirID;
UInt16 status;
UInt16 fileType;
isHFSPlus = VolumeObjectIsHFSPlus();
damagedDirID = CreateDirByName(GPtr, (u_char *)"DamagedFiles", kHFSRootFolderID);
if (damagedDirID == 0) {
goto out;
}
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 (fileID >= kHFSFirstUserCatalogNodeID) {
char *name;
char *path;
dataoffset = sprintf (data, "..");
path = data + dataoffset;
datalen -= dataoffset;
filenameoffset = sprintf (filename, "%08x ", fileID);
name = filename + filenameoffset;
filenamelen -= filenameoffset;
err = GetFileNamePathByID(GPtr, fileID, path, &datalen,
name, &filenamelen, &status);
if (err != noErr) {
#if DEBUG_OVERLAP
plog ("%s: Error in getting name/path for fileID = %d (err=%d)\n", __FUNCTION__, 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 ", fileID);
filenamelen -= filenameoffset;
err = GetSystemFileName (fileID, (filename + filenameoffset), &filenamelen);
filenamelen += filenameoffset;
dataoffset = sprintf (data, "System File: ");
datalen -= dataoffset;
err = GetSystemFileName (fileID, (data + dataoffset), &datalen);
datalen += dataoffset;
}
err = CreateFileByName (GPtr, damagedDirID, fileType, (u_char *)filename,
filenamelen, (u_char *)data, datalen);
if (err == EEXIST) {
err = noErr;
}
if (err != noErr) {
#if DEBUG_OVERLAP
plog ("%s: Error in creating fileType = %d for fileID = %d (err=%d)\n", __FUNCTION__, fileType, fileID, err);
#endif
goto out;
}
out:
if (err) {
if ((GPtr->PrintStat & S_SymlinkCreate) == 0) {
fsckPrint(GPtr->context, E_SymlinkCreate);
GPtr->PrintStat|= S_SymlinkCreate;
}
} else {
if ((GPtr->PrintStat & S_DamagedDir) == 0) {
fsckPrint(GPtr->context, fsckCorruptFilesDirectory, "DamagedFiles");
GPtr->PrintStat|= S_DamagedDir;
}
}
if (data) {
free (data);
}
if (filename) {
free (filename);
}
return err;
}
static OSErr SearchExtentInAttributeBT(SGlobPtr GPtr, ExtentInfo *extentInfo,
HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord,
UInt16 *recordSize, UInt32 *foundExtentIndex)
{
OSErr result = fnfErr;
BTreeIterator iterator;
FSBufferDescriptor btRecord;
HFSPlusAttrKey *key;
Boolean noMoreExtents;
unsigned char *attrname = NULL;
size_t attrnamelen;
assert((extentInfo->attrname != NULL));
attrname = malloc (XATTR_MAXNAMELEN + 1);
if (!attrname) {
result = memFullErr;
goto out;
}
ClearMemory(&iterator, sizeof(BTreeIterator));
key = (HFSPlusAttrKey *)&iterator.key;
attrnamelen = strlen(extentInfo->attrname);
BuildAttributeKey(extentInfo->fileID, 0, (unsigned char *)extentInfo->attrname, attrnamelen, key);
btRecord.bufferAddress = attrRecord;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof(HFSPlusAttrRecord);
result = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator,
kInvalidMRUCacheKey, &btRecord, recordSize, &iterator);
if (result) {
dprintf (d_error|d_overlap, "%s: Error finding attribute record (err=%d) for fileID = %d, attrname = %d\n", __FUNCTION__, result, extentInfo->fileID, extentInfo->attrname);
goto out;
}
while (1) {
if (attrRecord->recordType == kHFSPlusAttrForkData) {
result = FindExtentInExtentRec(true, extentInfo->startBlock,
extentInfo->blockCount, attrRecord->forkData.theFork.extents,
foundExtentIndex, &noMoreExtents);
if ((result == noErr) || (noMoreExtents == true)) {
goto out;
}
} else if (attrRecord->recordType == kHFSPlusAttrExtents) {
result = FindExtentInExtentRec(true, extentInfo->startBlock,
extentInfo->blockCount, attrRecord->overflowExtents.extents,
foundExtentIndex, &noMoreExtents);
if ((result == noErr) || (noMoreExtents == true)) {
goto out;
}
} else {
result = fnfErr;
goto out;
}
result = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord,
&iterator, &btRecord, recordSize);
if (result) {
goto out;
}
(void) utf_encodestr(key->attrName, key->attrNameLen * 2, attrname, &attrnamelen, XATTR_MAXNAMELEN + 1);
attrname[attrnamelen] = '\0';
if ((key->fileID != extentInfo->fileID) ||
(strcmp((char *)attrname, extentInfo->attrname))) {
result = fnfErr;
goto out;
}
}
out:
if (result == noErr) {
CopyMemory(key, attrKey, sizeof(HFSPlusAttrKey));
}
if (attrname != NULL) {
free (attrname);
}
return (result);
}
static OSErr UpdateExtentInAttributeBT (SGlobPtr GPtr, ExtentInfo *extentInfo,
HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord,
UInt16 *recordSize, UInt32 foundInExtentIndex)
{
OSErr err;
UInt32 foundHint;
assert ((attrRecord->recordType == kHFSPlusAttrForkData) ||
(attrRecord->recordType == kHFSPlusAttrExtents));
if (attrRecord->recordType == kHFSPlusAttrForkData) {
attrRecord->forkData.theFork.extents[foundInExtentIndex].startBlock =
extentInfo->newStartBlock;
} else if (attrRecord->recordType == kHFSPlusAttrExtents) {
attrRecord->overflowExtents.extents[foundInExtentIndex].startBlock =
extentInfo->newStartBlock;
}
err = ReplaceBTreeRecord (GPtr->calculatedAttributesFCB, attrKey, kNoHint,
attrRecord, *recordSize, &foundHint);
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
plog ("%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
plog ("%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
plog ("%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
plog ("%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
plog ("%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
plog ("%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, kHFSPlusMaxFileNameChars * 3 + 1);
} else {
namelen = catRecord.hfsThread.nodeName[0];
memcpy (filename, catKey.hfs.nodeName, namelen);
}
curPtr = malloc(sizeof(struct fsPathString));
if (!curPtr) {
err = memFullErr;
#if DEBUG_OVERLAP
plog ("%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
plog ("%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;
SVCB *vcb;
uint64_t old_offset;
uint64_t new_offset;
uint32_t sectorsPerBlock;
vcb = GPtr->calculatedVCB;
sectorsPerBlock = vcb->vcbBlockSize / Blk_Size;
old_offset = (vcb->vcbAlBlSt + (sectorsPerBlock * startAllocationBlock)) << Log2BlkLo;
new_offset = (vcb->vcbAlBlSt + (sectorsPerBlock * newStartAllocationBlock)) << Log2BlkLo;
err = CacheCopyDiskBlocks (vcb->vcbBlockCache, old_offset, new_offset,
blockCount * vcb->vcbBlockSize);
return err;
}
OSErr WriteBufferToDisk(SGlobPtr GPtr, UInt32 startBlock, UInt32 blockCount, u_char *buffer, int bufLen)
{
OSErr err = noErr;
SVCB *vcb;
uint64_t offset;
uint32_t write_len;
vcb = GPtr->calculatedVCB;
offset = (vcb->vcbAlBlSt + ((vcb->vcbBlockSize / Blk_Size) * startBlock)) << Log2BlkLo;
write_len = blockCount * vcb->vcbBlockSize;
err = CacheWriteBufferToDisk (vcb->vcbBlockCache, offset, write_len, buffer, bufLen);
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 ) {
if ( lostAndFoundDirID == 0 )
lostAndFoundDirID = CreateDirByName( GPtr , (u_char *)"lost+found", kHFSRootFolderID);
if ( lostAndFoundDirID == 0 ) {
if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
plog( "\tCould not create lost+found directory \n" );
return( R_RFail );
}
fsckPrint(GPtr->context, E_NoDir, mtp->threadID);
result = FixMissingDirectory( GPtr, mtp->threadID, lostAndFoundDirID );
if ( result != 0 ) {
if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
plog( "\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 )
fsckPrint(GPtr->context, fsckLostFoundDirectory, "lost+found");
return (0);
}
static OSErr
FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID )
{
Boolean isHFSPlus;
UInt16 recSize;
OSErr result;
int nameLen;
UInt32 hint;
UInt32 myItemsCount;
UInt32 myFolderCount;
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 );
recSize = BuildFolderRec( GPtr, 01777, theObjID, isHFSPlus, &catRec );
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 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, sizeof(fName.ustr.unicode));
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, filenameLen * 4);
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
plog ("%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)) {
blockCount = (dataLen / (GPtr->calculatedVCB->vcbBlockSize)) + 1;
} else {
blockCount = dataLen / (GPtr->calculatedVCB->vcbBlockSize);
}
if (blockCount) {
err = AllocateContigBitmapBits (GPtr->calculatedVCB, blockCount, &startBlock);
if (err != noErr) {
#if DEBUG_OVERLAP
plog ("%s: Not enough disk space (err=%d)\n", __FUNCTION__, err);
#endif
goto out;
}
err = WriteBufferToDisk(GPtr, startBlock, blockCount, data, dataLen);
if (err != noErr) {
#if DEBUG_OVERLAP
plog ("%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
plog ("%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
plog ("%s: Incorrect fileType\n", __FUNCTION__);
#endif
err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey);
if (err != noErr) {
#if DEBUG_OVERLAP
plog ("%s: Error in removing thread record\n", __FUNCTION__);
#endif
}
err = paramErr;
goto out;
}
if (isHFSPlus) {
catRecord.hfsPlusFile.dataFork.logicalSize = dataLen;
catRecord.hfsPlusFile.dataFork.totalBlocks = blockCount;
catRecord.hfsPlusFile.dataFork.extents[0].startBlock = startBlock;
catRecord.hfsPlusFile.dataFork.extents[0].blockCount = blockCount;
} else {
catRecord.hfsFile.dataLogicalSize = dataLen;
catRecord.hfsFile.dataPhysicalSize = blockCount * 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
plog ("Created \"%s\" with ID = %d startBlock = %d, blockCount = %d, dataLen = %d\n", fileName, nextCNID, startBlock, blockCount, dataLen);
#endif
} else {
#if DEBUG_OVERLAP
plog ("%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
plog ("%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
plog ("%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( GPtr, 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 void
CountFolderItems(SGlobPtr GPtr, UInt32 folderID, Boolean isHFSPlus, UInt32 *itemCount, UInt32 *folderCount)
{
SFCB *fcb = GPtr->calculatedCatalogFCB;
OSErr err = 0;
BTreeIterator iterator;
FSBufferDescriptor btRecord;
union {
HFSPlusCatalogFolder catRecord;
HFSPlusCatalogFile catFile;
} catRecord;
HFSPlusCatalogKey *key;
UInt16 recordSize = 0;
int fCount = 0, iCount = 0;
ClearMemory(&iterator, sizeof(iterator));
key = (HFSPlusCatalogKey*)&iterator.key;
BuildCatalogKey(folderID, NULL, isHFSPlus, (CatalogKey*)key);
btRecord.bufferAddress = &catRecord;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof(catRecord);
for (err = BTSearchRecord(fcb, &iterator, kNoHint, &btRecord, &recordSize, &iterator);
err == 0;
err = BTIterateRecord(fcb, kBTreeNextRecord, &iterator, &btRecord, &recordSize)) {
if (catRecord.catRecord.recordType == kHFSPlusFolderThreadRecord ||
catRecord.catRecord.recordType == kHFSPlusFileThreadRecord ||
catRecord.catRecord.recordType == kHFSFolderThreadRecord ||
catRecord.catRecord.recordType == kHFSFileThreadRecord)
continue;
if (key->parentID != folderID)
break;
if (isHFSPlus &&
(catRecord.catRecord.recordType == kHFSPlusFileRecord) &&
(catRecord.catFile.flags & kHFSHasLinkChainMask) &&
(catRecord.catFile.userInfo.fdType == kHFSAliasType) &&
(catRecord.catFile.userInfo.fdCreator == kHFSAliasCreator) &&
(key->parentID != GPtr->filelink_priv_dir_id)) {
fCount++;
}
if (catRecord.catRecord.recordType == kHFSPlusFolderRecord)
fCount++;
iCount++;
}
if (itemCount)
*itemCount = iCount;
if (folderCount)
*folderCount = fCount;
return;
}
static int
BuildFolderRec( SGlob *GPtr, u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus, CatalogRecord * theRecPtr )
{
UInt16 recSize;
UInt32 createTime;
UInt32 vCount = 0, fCount = 0;
ClearMemory( (Ptr)theRecPtr, sizeof(*theRecPtr) );
CountFolderItems(GPtr, theObjID, isHFSPlus, &vCount, &fCount);
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;
theRecPtr->hfsPlusFolder.valence = vCount;
recSize= sizeof(HFSPlusCatalogFolder);
if (VolumeObjectIsHFSX(GPtr)) {
theRecPtr->hfsPlusFolder.flags |= kHFSHasFolderCountMask;
theRecPtr->hfsPlusFolder.folderCount = fCount;
}
}
else {
createTime = GetTimeLocal( true );
theRecPtr->hfsFolder.recordType = kHFSFolderRecord;
theRecPtr->hfsFolder.folderID = theObjID;
theRecPtr->hfsFolder.createDate = createTime;
theRecPtr->hfsFolder.modifyDate = createTime;
theRecPtr->hfsFolder.valence = vCount;
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);
}
static void BuildAttributeKey(u_int32_t fileID, u_int32_t startBlock,
unsigned char *attrName, u_int16_t attrNameLen, HFSPlusAttrKey *key)
{
size_t attrNameLenBytes;
assert(VolumeObjectIsHFSPlus() == true);
key->pad = 0;
key->fileID = fileID;
key->startBlock = startBlock;
(void) utf_decodestr(attrName, attrNameLen, key->attrName, &attrNameLenBytes, sizeof(key->attrName));
key->attrNameLen = attrNameLenBytes / 2;
key->keyLength = kHFSPlusAttrKeyMinimumLength + attrNameLenBytes;
}
static int DeleteCatalogRecordByID(SGlobPtr GPtr, uint32_t id, Boolean for_rename)
{
int retval;
CatalogRecord rec;
CatalogKey key;
UInt16 recsize;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus();
retval = GetCatalogRecordByID(GPtr, id, isHFSPlus, &key, &rec, &recsize);
if (retval) {
return retval;
}
if (isHFSPlus) {
retval = DeleteCatalogNode(GPtr->calculatedVCB,
key.hfsPlus.parentID,
(const CatalogName *)&key.hfsPlus.nodeName,
kNoHint, for_rename);
} else {
retval = DeleteCatalogNode(GPtr->calculatedVCB,
key.hfs.parentID,
(const CatalogName *)&key.hfs.nodeName,
kNoHint, for_rename);
}
if ((retval == 0) && (for_rename == false) && (isHFSPlus == true)) {
retval = DeleteAllAttrsByID(GPtr, id);
}
return retval;
}
static int MoveCatalogRecordByID(SGlobPtr GPtr, uint32_t id, uint32_t new_parentid)
{
int retval;
CatalogRecord rec;
CatalogKey key;
UInt32 hint;
UInt16 recsize;
Boolean isFolder = false;
BTreeIterator iterator;
assert (VolumeObjectIsHFSPlus() == true);
retval = GetCatalogRecordByID(GPtr, id, true, &key, &rec, &recsize);
if (retval) {
goto out;
}
retval = DeleteCatalogRecordByID(GPtr, id, true);
if (retval) {
goto out;
}
key.hfsPlus.parentID = new_parentid;
if (rec.recordType == kHFSPlusFolderRecord) {
rec.hfsPlusFolder.flags &= ~kHFSHasLinkChainMask;
isFolder = true;
} else if (rec.recordType == kHFSPlusFileRecord) {
rec.hfsPlusFile.flags &= ~kHFSHasLinkChainMask;
isFolder = false;
}
retval = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &key, &rec,
recsize, &hint);
if (retval) {
goto out;
}
recsize = BuildThreadRec(&key, &rec, true, isFolder);
BuildCatalogKey(id, NULL, true, &key);
retval = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &key, &rec,
recsize, &hint);
if (retval) {
goto out;
}
ClearMemory(&iterator, sizeof(iterator));
retval = GetCatalogRecordByID(GPtr, new_parentid, true, &key, &rec, &recsize);
if (retval) {
if ((retval == btNotFound) && (GPtr->CBTStat & S_Orphan)) {
retval = 0;
}
goto out;
}
if (rec.recordType != kHFSPlusFolderRecord) {
goto out;
}
rec.hfsPlusFolder.valence++;
if ((isFolder == true) &&
(rec.hfsPlusFolder.flags & kHFSHasFolderCountMask)) {
rec.hfsPlusFolder.folderCount++;
}
retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key,
kNoHint, &rec, recsize, &hint);
if (retval) {
goto out;
}
if (isFolder == true) {
GPtr->calculatedVCB->vcbFolderCount++;
} else {
GPtr->calculatedVCB->vcbFileCount++;
}
GPtr->VIStat |= S_MDB;
GPtr->CBTStat |= S_BTH;
out:
return retval;
}
static int DeleteAllAttrsByID(SGlobPtr GPtr, uint32_t id)
{
int retval;
BTreeIterator iterator;
FSBufferDescriptor btrec;
HFSPlusAttrKey *attr_key;
HFSPlusAttrRecord attr_record;
UInt16 record_size;
ClearMemory(&iterator, sizeof(BTreeIterator));
attr_key = (HFSPlusAttrKey *)&iterator.key;
attr_key->keyLength = kHFSPlusAttrKeyMinimumLength;
attr_key->fileID = id;
ClearMemory(&btrec, sizeof(FSBufferDescriptor));
btrec.bufferAddress = &attr_record;
btrec.itemCount = 1;
btrec.itemSize = sizeof(HFSPlusAttrRecord);
retval = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator,
kInvalidMRUCacheKey, &btrec, &record_size, &iterator);
if ((retval != 0) && (retval != btNotFound)) {
goto out;
}
retval = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord,
&iterator, &btrec, &record_size);
while ((retval == 0) && (attr_key->fileID == id)) {
retval = delete_attr_record(GPtr, attr_key, &attr_record);
if (retval) {
break;
}
retval = BTIterateRecord(GPtr->calculatedAttributesFCB,
kBTreeNextRecord, &iterator, &btrec, &record_size);
}
if (retval == btNotFound) {
retval = 0;
}
out:
return retval;
}
static int delete_attr_record(SGlobPtr GPtr, HFSPlusAttrKey *attr_key, HFSPlusAttrRecord *attr_record)
{
int retval;
UInt32 num_blocks_freed;
Boolean last_extent;
retval = DeleteBTreeRecord(GPtr->calculatedAttributesFCB, attr_key);
if (retval == 0) {
GPtr->ABTStat |= S_BTH + S_BTM;
if (attr_record->recordType == kHFSPlusAttrForkData) {
retval = ReleaseExtents(GPtr->calculatedVCB,
attr_record->forkData.theFork.extents,
&num_blocks_freed, &last_extent);
} else if (attr_record->recordType == kHFSPlusAttrExtents) {
retval = ReleaseExtents(GPtr->calculatedVCB,
attr_record->overflowExtents.extents,
&num_blocks_freed, &last_extent);
}
}
return retval;
}
static int ZeroFillUnusedNodes(SGlobPtr GPtr, short fileRefNum)
{
BTreeControlBlock *btcb = GetBTreeControlBlock(fileRefNum);
unsigned char *bitmap = (unsigned char *) ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr;
unsigned char mask = 0x80;
OSErr err;
UInt32 nodeNum;
BlockDescriptor node;
node.buffer = NULL;
for (nodeNum = 0; nodeNum < btcb->totalNodes; ++nodeNum)
{
if ((*bitmap & mask) == 0)
{
err = btcb->getBlockProc(btcb->fcbPtr, nodeNum, kGetBlock|kGetEmptyBlock, &node);
if (err)
{
if (debug) plog("Couldn't read node #%u\n", nodeNum);
return err;
}
bzero(node.buffer, node.blockSize);
(void) btcb->releaseBlockProc(btcb->fcbPtr, &node, kReleaseBlock|kMarkBlockDirty);
node.buffer = NULL;
}
mask >>= 1;
if (mask == 0)
{
mask = 0x80;
++bitmap;
}
}
return 0;
}