#include "Scavenger.h"
#include "DecompDataEnums.h"
#include "DecompData.h"
struct CatalogIterationSummary {
UInt32 parentID;
UInt32 rootDirCount;
UInt32 rootFileCount;
UInt32 dirCount;
UInt32 dirThreads;
UInt32 fileCount;
UInt32 filesWithThreads;
UInt32 fileThreads;
UInt32 nextCNID;
UInt64 encodings;
void * hardLinkRef;
};
struct CatalogIterationSummary gCIS;
SGlobPtr gScavGlobals;
static int CheckCatalogRecord(SGlobPtr GPtr, const HFSPlusCatalogKey *key,
const CatalogRecord *rec, UInt16 reclen);
static int CheckCatalogRecord_HFS(const HFSCatalogKey *key,
const CatalogRecord *rec, UInt16 reclen);
static int CheckIfAttributeExists(const UInt32 fileID);
static int CheckDirectory(const HFSPlusCatalogKey * key, const HFSPlusCatalogFolder * dir);
static int CheckFile(const HFSPlusCatalogKey * key, const HFSPlusCatalogFile * file);
static int CheckThread(const HFSPlusCatalogKey * key, const HFSPlusCatalogThread * thread);
static int CheckDirectory_HFS(const HFSCatalogKey * key, const HFSCatalogFolder * dir);
static int CheckFile_HFS(const HFSCatalogKey * key, const HFSCatalogFile * file);
static int CheckThread_HFS(const HFSCatalogKey * key, const HFSCatalogThread * thread);
static void CheckBSDInfo(const HFSPlusCatalogKey * key, const HFSPlusBSDInfo * bsdInfo, int isdir);
static int CheckCatalogName(u_int16_t charCount, const u_int16_t *uniChars,
u_int32_t parentID, Boolean thread);
static int CheckCatalogName_HFS(u_int16_t charCount, const u_char *filename,
u_int32_t parentID, Boolean thread);
static int RecordBadAllocation(UInt32 parID, unsigned char * filename, UInt32 forkType, UInt32 oldBlkCnt, UInt32 newBlkCnt);
static int RecordTruncation(UInt32 parID, unsigned char * filename, UInt32 forkType, UInt64 oldSize, UInt64 newSize);
static int CaptureMissingThread(UInt32 threadID, const HFSPlusCatalogKey *nextKey);
static OSErr UniqueDotName( SGlobPtr GPtr,
CatalogName * theNewNamePtr,
UInt32 theParID,
Boolean isSingleDotName,
Boolean isHFSPlus );
static Boolean FixDecomps( u_int16_t charCount, const u_int16_t *inFilename, HFSUniStr255 *outFilename );
static int FindOrigOverlapFiles(SGlobPtr GPtr);
static void CheckHFSPlusExtentRecords(SGlobPtr GPtr, UInt32 fileID, HFSPlusExtentRecord extent, UInt8 forkType);
static void CheckHFSExtentRecords(SGlobPtr GPtr, UInt32 fileID, HFSExtentRecord extent, UInt8 forkType);
static Boolean DoesOverlap(SGlobPtr GPtr, UInt32 fileID, UInt32 startBlock, UInt32 blockCount, UInt8 forkType);
void PrintOverlapFiles (SGlobPtr GPtr);
static int CompareExtentFileID(const void *first, const void *second);
OSErr
CheckCatalogBTree( SGlobPtr GPtr )
{
OSErr err;
int hfsplus;
gScavGlobals = GPtr;
hfsplus = VolumeObjectIsHFSPlus( );
ClearMemory(&gCIS, sizeof(gCIS));
gCIS.parentID = kHFSRootParentID;
gCIS.nextCNID = kHFSFirstUserCatalogNodeID;
if (hfsplus)
HardLinkCheckBegin(gScavGlobals, &gCIS.hardLinkRef);
gScavGlobals->TarID = kHFSCatalogFileID;
GetVolumeObjectBlockNum( &gScavGlobals->TarBlock );
err = BTCheck(gScavGlobals, kCalculatedCatalogRefNum, (CheckLeafRecordProcPtr)CheckCatalogRecord);
if (err) goto exit;
if (GPtr->VIStat & S_OverlappingExtents) {
err = FindOrigOverlapFiles(GPtr);
if (err) goto exit;
(void) PrintOverlapFiles(GPtr);
}
if (gCIS.dirCount != gCIS.dirThreads) {
RcdError(gScavGlobals, E_IncorrectNumThdRcd);
gScavGlobals->CBTStat |= S_Orphan;
}
if (hfsplus && (gCIS.fileCount != gCIS.fileThreads)) {
RcdError(gScavGlobals, E_IncorrectNumThdRcd);
gScavGlobals->CBTStat |= S_Orphan;
}
if (!hfsplus && (gCIS.fileThreads != gCIS.filesWithThreads)) {
RcdError(gScavGlobals, E_IncorrectNumThdRcd);
gScavGlobals->CBTStat |= S_Orphan;
}
gScavGlobals->calculatedVCB->vcbEncodingsBitmap = gCIS.encodings;
gScavGlobals->calculatedVCB->vcbNextCatalogID = gCIS.nextCNID;
gScavGlobals->calculatedVCB->vcbFolderCount = gCIS.dirCount - 1;
gScavGlobals->calculatedVCB->vcbFileCount = gCIS.fileCount;
if (!hfsplus) {
gScavGlobals->calculatedVCB->vcbNmRtDirs = gCIS.rootDirCount;
gScavGlobals->calculatedVCB->vcbNmFls = gCIS.rootFileCount;
}
err = BTMapChk(gScavGlobals, kCalculatedCatalogRefNum);
if (err) goto exit;
err = CmpBTH(gScavGlobals, kCalculatedCatalogRefNum);
if (err) goto exit;
err = CmpBTM(gScavGlobals, kCalculatedCatalogRefNum);
if (hfsplus)
(void) CheckHardLinks(gCIS.hardLinkRef);
exit:
if (hfsplus)
HardLinkCheckEnd(gCIS.hardLinkRef);
return (err);
}
static int
CheckCatalogRecord(SGlobPtr GPtr, const HFSPlusCatalogKey *key, const CatalogRecord *rec, UInt16 reclen)
{
int result = 0;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus( );
++gScavGlobals->itemsProcessed;
if (!isHFSPlus)
return CheckCatalogRecord_HFS((HFSCatalogKey *)key, rec, reclen);
gScavGlobals->CNType = rec->recordType;
switch (rec->recordType) {
case kHFSPlusFolderRecord:
++gCIS.dirCount;
if (reclen != sizeof(HFSPlusCatalogFolder)){
RcdError(gScavGlobals, E_LenDir);
result = E_LenDir;
break;
}
if (key->parentID != gCIS.parentID) {
result = CaptureMissingThread(key->parentID, key);
if (result) break;
++gCIS.dirThreads;
gCIS.parentID = key->parentID;
}
result = CheckDirectory(key, (HFSPlusCatalogFolder *)rec);
break;
case kHFSPlusFileRecord:
++gCIS.fileCount;
if (reclen != sizeof(HFSPlusCatalogFile)){
RcdError(gScavGlobals, E_LenFil);
result = E_LenFil;
break;
}
if (key->parentID != gCIS.parentID) {
result = CaptureMissingThread(key->parentID, key);
if (result) break;
++gCIS.dirThreads;
gCIS.parentID = key->parentID;
}
result = CheckFile(key, (HFSPlusCatalogFile *)rec);
break;
case kHFSPlusFolderThreadRecord:
++gCIS.dirThreads;
gCIS.parentID = key->parentID;
case kHFSPlusFileThreadRecord:
if (rec->recordType == kHFSPlusFileThreadRecord)
++gCIS.fileThreads;
if (reclen > sizeof(HFSPlusCatalogThread) ||
reclen < sizeof(HFSPlusCatalogThread) - sizeof(HFSUniStr255)) {
RcdError(gScavGlobals, E_LenThd);
result = E_LenThd;
break;
} else if (reclen == sizeof(HFSPlusCatalogThread)) {
gScavGlobals->VeryMinorErrorsStat |= S_BloatedThreadRecordFound;
}
result = CheckThread(key, (HFSPlusCatalogThread *)rec);
break;
default:
RcdError(gScavGlobals, E_CatRec);
result = E_CatRec;
}
return (result);
}
static int
CheckCatalogRecord_HFS(const HFSCatalogKey *key, const CatalogRecord *rec, UInt16 reclen)
{
int result = 0;
gScavGlobals->CNType = rec->recordType;
switch (rec->recordType) {
case kHFSFolderRecord:
++gCIS.dirCount;
if (key->parentID == kHFSRootFolderID )
++gCIS.rootDirCount;
if (reclen != sizeof(HFSCatalogFolder)){
RcdError(gScavGlobals, E_LenDir);
result = E_LenDir;
break;
}
if (key->parentID != gCIS.parentID) {
result = CaptureMissingThread(key->parentID, (HFSPlusCatalogKey *)key);
if (result) break;
++gCIS.dirThreads;
gCIS.parentID = key->parentID;
}
result = CheckDirectory_HFS(key, (HFSCatalogFolder *)rec);
break;
case kHFSFileRecord:
++gCIS.fileCount;
if (key->parentID == kHFSRootFolderID )
++gCIS.rootFileCount;
if (reclen != sizeof(HFSCatalogFile)){
RcdError(gScavGlobals, E_LenFil);
result = E_LenFil;
break;
}
if (key->parentID != gCIS.parentID) {
result = CaptureMissingThread(key->parentID, (HFSPlusCatalogKey *)key);
if (result) break;
++gCIS.dirThreads;
gCIS.parentID = key->parentID;
}
result = CheckFile_HFS(key, (HFSCatalogFile *)rec);
break;
case kHFSFolderThreadRecord:
++gCIS.dirThreads;
gCIS.parentID = key->parentID;
case kHFSFileThreadRecord:
if (rec->recordType == kHFSFileThreadRecord)
++gCIS.fileThreads;
if (reclen != sizeof(HFSCatalogThread)) {
RcdError(gScavGlobals, E_LenThd);
result = E_LenThd;
break;
}
result = CheckThread_HFS(key, (HFSCatalogThread *)rec);
break;
default:
RcdError(gScavGlobals, E_CatRec);
result = E_CatRec;
}
return (result);
}
static int
CheckIfAttributeExists(const UInt32 fileID)
{
int result = 0;
HFSPlusAttrKey *attrKey;
BTreeIterator iterator;
ClearMemory(&iterator, sizeof(BTreeIterator));
attrKey = (HFSPlusAttrKey *)&iterator.key;
attrKey->keyLength = kHFSPlusAttrKeyMinimumLength;
attrKey->fileID = fileID;
result = BTSearchRecord(gScavGlobals->calculatedAttributesFCB, &iterator, kInvalidMRUCacheKey, NULL, NULL, &iterator);
if (result && (result != btNotFound)) {
#if 0 //DEBUG_XATTR
printf ("%s: No matching attribute record found\n", __FUNCTION__);
#endif
goto out;
}
result = BTIterateRecord(gScavGlobals->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL);
if ((result == noErr) && (attrKey->fileID == fileID)) {
result = 1;
}
out:
#if 0 //DEBUG_XATTR
printf ("%s: Retval=%d for fileID=%d\n", __FUNCTION__, result, fileID);
#endif
return result;
}
static int
CheckDirectory(const HFSPlusCatalogKey * key, const HFSPlusCatalogFolder * dir)
{
UInt32 dirID;
int result = 0;
dirID = dir->folderID;
if ((dir->flags & (kHFSFileLockedMask | kHFSThreadExistsMask)) != 0) {
RcdError(gScavGlobals, E_CatalogFlagsNotZero);
gScavGlobals->CBTStat |= S_ReservedNotZero;
}
result = CheckIfAttributeExists(dir->folderID);
if (result == 1) {
RecordXAttrBits(gScavGlobals, dir->flags, dir->folderID, kCalculatedCatalogRefNum);
#if DEBUG_XATTR
printf ("%s: Record folderID=%d for prime modulus calculations\n", __FUNCTION__, dir->folderID);
#endif
}
#if 0 //DEBUG_XATTR
else {
if (result == 0)
printf ("%s: Ignoring folder ID %d from prime modulus calculuation\n", __FUNCTION__, dir->folderID);
}
#endif
result = 0;
if (dirID < kHFSFirstUserCatalogNodeID &&
dirID != kHFSRootFolderID) {
RcdError(gScavGlobals, E_InvalidID);
return (E_InvalidID);
}
if (dirID >= gCIS.nextCNID )
gCIS.nextCNID = dirID + 1;
gCIS.encodings |= (u_int64_t)(1ULL << MapEncodingToIndex(dir->textEncoding & 0x7F));
CheckBSDInfo(key, &dir->bsdInfo, true);
CheckCatalogName(key->nodeName.length, &key->nodeName.unicode[0], key->parentID, false);
return (result);
}
static int
CheckFile(const HFSPlusCatalogKey * key, const HFSPlusCatalogFile * file)
{
UInt32 fileID;
UInt32 blocks;
UInt64 bytes;
int result = 0;
size_t len;
unsigned char filename[256 * 3];
(void) utf_encodestr(key->nodeName.unicode,
key->nodeName.length * 2,
filename, &len);
filename[len] = '\0';
result = CheckIfAttributeExists(file->fileID);
if (result == 1) {
RecordXAttrBits(gScavGlobals, file->flags, file->fileID, kCalculatedCatalogRefNum);
#if DEBUG_XATTR
printf ("%s: Record fileID=%d for prime modulus calculations\n", __FUNCTION__, file->fileID);
#endif
}
#if 0 //DEBUG_XATTR
else {
if (result == 0)
printf ("%s: Ignoring file ID %d from prime modulus calculuation\n", __FUNCTION__, file->fileID);
}
#endif
result = 0;
fileID = file->fileID;
if (fileID < kHFSFirstUserCatalogNodeID) {
RcdError(gScavGlobals, E_InvalidID);
result = E_InvalidID;
return (result);
}
if (fileID >= gCIS.nextCNID )
gCIS.nextCNID = fileID + 1;
gCIS.encodings |= (u_int64_t)(1ULL << MapEncodingToIndex(file->textEncoding & 0x7F));
CheckBSDInfo(key, &file->bsdInfo, false);
result = CheckFileExtents(gScavGlobals, file->fileID, 0,
file->dataFork.extents, &blocks);
if (result != noErr)
return (result);
if (file->dataFork.totalBlocks != blocks) {
result = RecordBadAllocation(key->parentID, filename, kDataFork,
file->dataFork.totalBlocks, blocks);
if (result)
return (result);
} else {
bytes = (UInt64)blocks * (UInt64)gScavGlobals->calculatedVCB->vcbBlockSize;
if (file->dataFork.logicalSize > bytes) {
result = RecordTruncation(key->parentID, filename, kDataFork,
file->dataFork.logicalSize, bytes);
if (result)
return (result);
}
}
result = CheckFileExtents(gScavGlobals, file->fileID, 0xFF,
file->resourceFork.extents, &blocks);
if (result != noErr)
return (result);
if (file->resourceFork.totalBlocks != blocks) {
result = RecordBadAllocation(key->parentID, filename, kRsrcFork,
file->resourceFork.totalBlocks, blocks);
if (result)
return (result);
} else {
bytes = (UInt64)blocks * (UInt64)gScavGlobals->calculatedVCB->vcbBlockSize;
if (file->resourceFork.logicalSize > bytes) {
result = RecordTruncation(key->parentID, filename, kRsrcFork,
file->resourceFork.logicalSize, bytes);
if (result)
return (result);
}
}
if (SWAP_BE32(file->userInfo.fdType) == kHardLinkFileType &&
SWAP_BE32(file->userInfo.fdCreator) == kHFSPlusCreator)
CaptureHardLink(gCIS.hardLinkRef, file->bsdInfo.special.iNodeNum);
CheckCatalogName(key->nodeName.length, &key->nodeName.unicode[0], key->parentID, false);
return (result);
}
static int
CheckThread(const HFSPlusCatalogKey * key, const HFSPlusCatalogThread * thread)
{
int result = 0;
if (key->nodeName.length != 0) {
RcdError(gScavGlobals, E_ThdKey);
return (E_ThdKey);
}
result = CheckCatalogName(thread->nodeName.length, &thread->nodeName.unicode[0],
thread->parentID, true);
if (result != noErr) {
RcdError(gScavGlobals, E_ThdCN);
return (E_ThdCN);
}
if (key->parentID < kHFSFirstUserCatalogNodeID &&
key->parentID != kHFSRootParentID &&
key->parentID != kHFSRootFolderID) {
RcdError(gScavGlobals, E_InvalidID);
return (E_InvalidID);
}
if (thread->parentID == kHFSRootParentID) {
if (key->parentID != kHFSRootFolderID) {
RcdError(gScavGlobals, E_InvalidID);
return (E_InvalidID);
}
} else if (thread->parentID < kHFSFirstUserCatalogNodeID &&
thread->parentID != kHFSRootFolderID) {
RcdError(gScavGlobals, E_InvalidID);
return (E_InvalidID);
}
return (0);
}
static int
CheckDirectory_HFS(const HFSCatalogKey * key, const HFSCatalogFolder * dir)
{
UInt32 dirID;
int result = 0;
dirID = dir->folderID;
if ((dir->flags & (kHFSFileLockedMask | kHFSThreadExistsMask)) != 0) {
RcdError(gScavGlobals, E_CatalogFlagsNotZero);
gScavGlobals->CBTStat |= S_ReservedNotZero;
}
if (dirID < kHFSFirstUserCatalogNodeID &&
dirID != kHFSRootFolderID) {
RcdError(gScavGlobals, E_InvalidID);
return (E_InvalidID);
}
if (dirID >= gCIS.nextCNID )
gCIS.nextCNID = dirID + 1;
CheckCatalogName_HFS(key->nodeName[0], &key->nodeName[1], key->parentID, false);
return (result);
}
static int
CheckFile_HFS(const HFSCatalogKey * key, const HFSCatalogFile * file)
{
UInt32 fileID;
UInt32 blocks;
int result = 0;
if (file->flags & kHFSThreadExistsMask)
++gCIS.filesWithThreads;
if ((file->dataStartBlock) ||
(file->rsrcStartBlock) ||
(file->reserved))
{
RcdError(gScavGlobals, E_CatalogFlagsNotZero);
gScavGlobals->CBTStat |= S_ReservedNotZero;
}
fileID = file->fileID;
if (fileID < kHFSFirstUserCatalogNodeID) {
RcdError(gScavGlobals, E_InvalidID);
result = E_InvalidID;
return (result);
}
if (fileID >= gCIS.nextCNID )
gCIS.nextCNID = fileID + 1;
result = CheckFileExtents(gScavGlobals, file->fileID, 0,
file->dataExtents, &blocks);
if (result != noErr)
return (result);
if (file->dataPhysicalSize > ((UInt64)blocks * (UInt64)gScavGlobals->calculatedVCB->vcbBlockSize)) {
PrintError(gScavGlobals, E_PEOF, 1, "");
return (noErr);
}
if (file->dataLogicalSize > file->dataPhysicalSize) {
PrintError(gScavGlobals, E_LEOF, 1, "");
return (noErr);
}
result = CheckFileExtents(gScavGlobals, file->fileID, 0xFF,
file->rsrcExtents, &blocks);
if (result != noErr)
return (result);
if (file->rsrcPhysicalSize > ((UInt64)blocks * (UInt64)gScavGlobals->calculatedVCB->vcbBlockSize)) {
PrintError(gScavGlobals, E_PEOF, 1, "");
return (noErr);
}
if (file->rsrcLogicalSize > file->rsrcPhysicalSize) {
PrintError(gScavGlobals, E_LEOF, 1, "");
return (noErr);
}
#if 1
if (PtrAndHand(&file->fileID, (Handle)gScavGlobals->validFilesList, sizeof(UInt32) ) )
return (R_NoMem);
#endif
CheckCatalogName_HFS(key->nodeName[0], &key->nodeName[1], key->parentID, false);
return (result);
}
static int
CheckThread_HFS(const HFSCatalogKey * key, const HFSCatalogThread * thread)
{
int result = 0;
if (key->nodeName[0] != 0) {
RcdError(gScavGlobals, E_ThdKey);
return (E_ThdKey);
}
result = CheckCatalogName_HFS(thread->nodeName[0], &thread->nodeName[1],
thread->parentID, true);
if (result != noErr) {
RcdError(gScavGlobals, E_ThdCN);
return (E_ThdCN);
}
if (key->parentID < kHFSFirstUserCatalogNodeID &&
key->parentID != kHFSRootParentID &&
key->parentID != kHFSRootFolderID) {
RcdError(gScavGlobals, E_InvalidID);
return (E_InvalidID);
}
if (thread->parentID == kHFSRootParentID) {
if (key->parentID != kHFSRootFolderID) {
RcdError(gScavGlobals, E_InvalidID);
return (E_InvalidID);
}
} else if (thread->parentID < kHFSFirstUserCatalogNodeID &&
thread->parentID != kHFSRootFolderID) {
RcdError(gScavGlobals, E_InvalidID);
return (E_InvalidID);
}
return (0);
}
#define FT_MASK 0170000
#define FT_FIFO 0010000
#define FT_CHR 0020000
#define FT_DIR 0040000
#define FT_BLK 0060000
#define FT_REG 0100000
#define FT_LNK 0120000
#define FT_SOCK 0140000
static void
CheckBSDInfo(const HFSPlusCatalogKey * key, const HFSPlusBSDInfo * bsdInfo, int isdir)
{
#define kObsoleteUnknownUID (-3)
#define kUnknownUID (99)
Boolean reset = false;
if (bsdInfo->fileMode == 0)
return;
switch (bsdInfo->fileMode & FT_MASK) {
case FT_DIR:
if (!isdir)
reset = true;
break;
case FT_REG:
case FT_BLK:
case FT_CHR:
case FT_LNK:
case FT_SOCK:
case FT_FIFO:
if (isdir)
reset = true;
break;
default:
reset = true;
}
if (reset ||
((long)bsdInfo->ownerID == kObsoleteUnknownUID) ||
((long)bsdInfo->groupID == kObsoleteUnknownUID)) {
RepairOrderPtr p;
int n;
if (reset) {
gScavGlobals->TarBlock = bsdInfo->fileMode & FT_MASK;
RcdError(gScavGlobals, E_InvalidPermissions);
}
n = CatalogNameSize( (CatalogName *) &key->nodeName, true );
p = AllocMinorRepairOrder(gScavGlobals, n);
if (p == NULL) return;
CopyCatalogName((const CatalogName *)&key->nodeName,
(CatalogName*)&p->name, true);
if (reset) {
p->type = E_InvalidPermissions;
p->correct = 0;
p->incorrect = bsdInfo->fileMode;
} else {
p->type = E_InvalidUID;
p->correct = kUnknownUID;
if ((long)bsdInfo->ownerID == kObsoleteUnknownUID)
p->incorrect = bsdInfo->ownerID;
else
p->incorrect = bsdInfo->groupID;
}
p->parid = key->parentID;
p->hint = 0;
gScavGlobals->CatStat |= S_Permissions;
}
}
static int
CheckCatalogName(u_int16_t charCount, const u_int16_t *uniChars, u_int32_t parentID, Boolean thread)
{
OSErr result;
u_int16_t * myPtr;
RepairOrderPtr roPtr;
int myLength;
CatalogName newName;
if ((charCount == 0) || (charCount > kHFSPlusMaxFileNameChars))
return( E_CName );
if ( thread )
return( noErr );
if ( charCount < 3 && *uniChars == 0x2E )
{
if ( charCount == 1 || (charCount == 2 && *(uniChars + 1) == 0x2E) )
{
PrintError( gScavGlobals, E_IllegalName, 0 );
if ( gScavGlobals->logLevel >= kDebugLog ) {
printf( "\tillegal name is 0x" );
PrintName( charCount, (UInt8 *) uniChars, true );
}
result = UniqueDotName( gScavGlobals, &newName, parentID,
((charCount == 1) ? true : false), true );
if ( result != noErr )
return( noErr );
myLength = (charCount + 1) * 2; myLength += ((newName.ustr.length + 1) * 2);
roPtr = AllocMinorRepairOrder( gScavGlobals, myLength );
if ( roPtr == NULL )
return( noErr );
myPtr = (u_int16_t *) &roPtr->name;
*myPtr++ = charCount; CopyMemory( uniChars, myPtr, (charCount * 2) ); myPtr += charCount; *myPtr++ = newName.ustr.length; CopyMemory( newName.ustr.unicode, myPtr, (newName.ustr.length * 2) ); if ( gScavGlobals->logLevel >= kDebugLog ) {
printf( "\treplacement name is 0x" );
PrintName( newName.ustr.length, (UInt8 *) &newName.ustr.unicode, true );
}
roPtr->type = E_IllegalName;
roPtr->parid = parentID;
gScavGlobals->CatStat |= S_IllName;
return( E_IllegalName );
}
}
if ( FixDecomps( charCount, uniChars, &newName.ustr ) )
{
PrintError( gScavGlobals, E_IllegalName, 0 );
if ( gScavGlobals->logLevel >= kDebugLog ) {
printf( "\tillegal name is 0x" );
PrintName( charCount, (UInt8 *) uniChars, true );
}
myLength = (charCount + 1) * 2; myLength += ((newName.ustr.length + 1) * 2);
roPtr = AllocMinorRepairOrder( gScavGlobals, myLength );
if ( roPtr == NULL )
return( noErr );
myPtr = (u_int16_t *) &roPtr->name;
*myPtr++ = charCount; CopyMemory( uniChars, myPtr, (charCount * 2) ); myPtr += charCount; *myPtr++ = newName.ustr.length; CopyMemory( newName.ustr.unicode, myPtr, (newName.ustr.length * 2) ); if ( gScavGlobals->logLevel >= kDebugLog ) {
printf( "\treplacement name is 0x" );
PrintName( newName.ustr.length, (UInt8 *) &newName.ustr.unicode, true );
}
roPtr->type = E_IllegalName;
roPtr->parid = parentID;
gScavGlobals->CatStat |= S_IllName;
return( E_IllegalName );
}
return( noErr );
}
static int
CheckCatalogName_HFS(u_int16_t charCount, const u_char *filename, u_int32_t parentID, Boolean thread)
{
u_char * myPtr;
RepairOrderPtr roPtr;
int myLength;
CatalogName newName;
if ((charCount == 0) || (charCount > kHFSMaxFileNameChars))
return( E_CName );
if ( thread )
return( noErr );
if ( charCount < 3 && *filename == 0x2E )
{
if ( charCount == 1 || (charCount == 2 && *(filename + 1) == 0x2E) )
{
OSErr result;
PrintError( gScavGlobals, E_IllegalName, 0 );
if ( gScavGlobals->logLevel >= kDebugLog ) {
printf( "\tillegal name is 0x" );
PrintName( charCount, filename, false );
}
result = UniqueDotName( gScavGlobals, &newName, parentID,
((charCount == 1) ? true : false), false );
if ( result != noErr )
return( noErr );
myLength = charCount + 1; myLength += (newName.pstr[0] + 1); roPtr = AllocMinorRepairOrder( gScavGlobals, myLength );
if ( roPtr == NULL )
return( noErr );
myPtr = (u_char *)&roPtr->name[0];
*myPtr++ = charCount; CopyMemory( filename, myPtr, charCount );
myPtr += charCount; *myPtr++ = newName.pstr[0]; CopyMemory( &newName.pstr[1], myPtr, newName.pstr[0] ); if ( gScavGlobals->logLevel >= kDebugLog ) {
printf( "\treplacement name is 0x" );
PrintName( newName.pstr[0], &newName.pstr[1], false );
}
roPtr->type = E_IllegalName;
roPtr->parid = parentID;
gScavGlobals->CatStat |= S_IllName;
return( E_IllegalName );
}
}
return( noErr );
}
static OSErr
UniqueDotName( SGlobPtr GPtr,
CatalogName * theNewNamePtr,
UInt32 theParID,
Boolean isSingleDotName,
Boolean isHFSPlus )
{
u_char newChar;
OSErr result;
int nameLen;
UInt16 recSize;
SFCB * fcbPtr;
u_char * myPtr;
CatalogRecord record;
CatalogKey catKey;
u_char dotName[] = {'d', 'o', 't', 'd', 'o', 't', 0x0d, 0x00};
fcbPtr = GPtr->calculatedCatalogFCB;
if ( isSingleDotName )
myPtr = &dotName[3];
else
myPtr = &dotName[0];
nameLen = strlen((char *) myPtr );
if ( isHFSPlus )
{
int i;
theNewNamePtr->ustr.length = nameLen;
for ( i = 0; i < theNewNamePtr->ustr.length; i++ )
theNewNamePtr->ustr.unicode[ i ] = (u_int16_t) *(myPtr + i);
}
else
{
theNewNamePtr->pstr[0] = nameLen;
memcpy( &theNewNamePtr->pstr[1], myPtr, nameLen );
}
for ( newChar = 0x30; newChar < 0x7F; newChar++ )
{
BuildCatalogKey( theParID, theNewNamePtr, isHFSPlus, &catKey );
result = SearchBTreeRecord( fcbPtr, &catKey, kNoHint, NULL, &record, &recSize, NULL );
if ( result != noErr )
return( noErr );
if ( isHFSPlus )
{
theNewNamePtr->ustr.unicode[ nameLen ] = (u_int16_t) newChar;
theNewNamePtr->ustr.length = nameLen + 1;
}
else
{
theNewNamePtr->pstr[ 0 ] = nameLen + 1;
theNewNamePtr->pstr[ nameLen + 1 ] = newChar;
}
}
return( -1 );
}
static int
RecordBadAllocation(UInt32 parID, unsigned char * filename, UInt32 forkType, UInt32 oldBlkCnt, UInt32 newBlkCnt)
{
RepairOrderPtr p;
char goodstr[16];
char badstr[16];
int n;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus( );
PrintError(gScavGlobals, E_PEOF, 1, filename);
sprintf(goodstr, "%d", newBlkCnt);
sprintf(badstr, "%d", oldBlkCnt);
PrintError(gScavGlobals, E_BadValue, 2, goodstr, badstr);
if ( !isHFSPlus )
return (E_PEOF);
n = strlen((char *)filename);
p = AllocMinorRepairOrder(gScavGlobals, n + 1);
if (p == NULL)
return (R_NoMem);
p->type = E_PEOF;
p->forkType = forkType;
p->incorrect = oldBlkCnt;
p->correct = newBlkCnt;
p->hint = 0;
p->parid = parID;
p->name[0] = n;
CopyMemory(filename, &p->name[1], n);
gScavGlobals->CatStat |= S_FileAllocation;
return (0);
}
static int
RecordTruncation(UInt32 parID, unsigned char * filename, UInt32 forkType, UInt64 oldSize, UInt64 newSize)
{
RepairOrderPtr p;
char oldSizeStr[48];
char newSizeStr[48];
int n;
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus( );
PrintError(gScavGlobals, E_LEOF, 1, filename);
sprintf(oldSizeStr, "%qd", oldSize);
sprintf(newSizeStr, "%qd", newSize);
PrintError(gScavGlobals, E_BadValue, 2, newSizeStr, oldSizeStr);
if ( !isHFSPlus )
return (E_LEOF);
n = strlen((char *)filename);
p = AllocMinorRepairOrder(gScavGlobals, n + 1);
if (p == NULL)
return (R_NoMem);
p->type = E_LEOF;
p->forkType = forkType;
p->incorrect = oldSize;
p->correct = newSize;
p->hint = 0;
p->parid = parID;
p->name[0] = n;
CopyMemory(filename, &p->name[1], n);
gScavGlobals->CatStat |= S_FileAllocation;
return (0);
}
static int
CaptureMissingThread(UInt32 threadID, const HFSPlusCatalogKey *nextKey)
{
MissingThread *mtp;
char idStr[16];
Boolean isHFSPlus;
isHFSPlus = VolumeObjectIsHFSPlus( );
sprintf(idStr, "%d", threadID);
PrintError(gScavGlobals, E_NoThd, 1, idStr);
if ( !isHFSPlus)
return (E_NoThd);
mtp = (MissingThread *) AllocateClearMemory(sizeof(MissingThread));
if (mtp == NULL)
return (R_NoMem);
mtp->link = gScavGlobals->missingThreadList;
gScavGlobals->missingThreadList = mtp;
mtp->threadID = threadID;
CopyMemory(nextKey, &mtp->nextKey, nextKey->keyLength + 2);
if (gScavGlobals->RepLevel == repairLevelNoProblemsFound)
gScavGlobals->RepLevel = repairLevelVolumeRecoverable;
gScavGlobals->CatStat |= S_MissingThread;
return (noErr);
}
static Boolean
FixDecomps( u_int16_t charCount, const u_int16_t *inFilename, HFSUniStr255 *outFilename )
{
const u_int16_t * inNamePtr = inFilename;
const u_int16_t * inNameLastPtr = &inFilename[charCount - 1];
u_int16_t * outNamePtr = outFilename->unicode;
u_int16_t * outNameLastPtr = &outFilename->unicode[kHFSPlusMaxFileNameChars - 1];
u_int16_t * outNameCombSeqPtr = NULL; u_int32_t maxClassValueInSeq = 0;
Boolean didModifyName = 0;
while (inNamePtr <= inNameLastPtr) {
u_int16_t shiftUniChar; int32_t rangeIndex;
u_int32_t shiftUniCharLo;
u_int32_t replDataIndex;
u_int32_t currCharClass;
shiftUniChar = *inNamePtr + kShiftUniCharOffset;
if ( shiftUniChar >= kShiftUniCharLimit )
goto CopyBaseChar;
rangeIndex = classAndReplIndex[shiftUniChar >> kLoFieldBitSize];
if ( rangeIndex < 0 )
goto CopyBaseChar;
shiftUniCharLo = shiftUniChar & kLoFieldMask;
replDataIndex = replaceRanges[rangeIndex][shiftUniCharLo];
if ( replDataIndex > 0 ) {
const u_int16_t * replDataPtr;
u_int32_t action;
u_int32_t copyCount = 0;
replDataPtr = &replaceData[replDataIndex];
action = *replDataPtr++;
switch (action) {
case kReplaceCurWithTwo:
case kReplaceCurWithThree:
inNamePtr++;
copyCount = (action == kReplaceCurWithTwo)? 2: 3;
break;
case kIfNextOneMatchesReplaceAllWithOne:
case kIfNextOneMatchesReplaceAllWithTwo:
if (inNamePtr + 1 <= inNameLastPtr && *(inNamePtr + 1) == *replDataPtr++) {
inNamePtr += 2;
copyCount = (action == kIfNextOneMatchesReplaceAllWithOne)? 1: 2;
} else {
goto CheckCombClass;
}
break;
case kIfNextTwoMatchReplaceAllWithOne:
if ( inNamePtr + 2 <= inNameLastPtr &&
*(inNamePtr + 1) == *replDataPtr++ &&
*(inNamePtr + 2) == *replDataPtr++)
{
inNamePtr += 3;
copyCount = 1;
} else {
goto CheckCombClass;
}
break;
}
if (outNamePtr + copyCount - 1 > outNameLastPtr) {
didModifyName = 0;
break;
}
while (copyCount-- > 0) {
currCharClass = 0;
shiftUniChar = *replDataPtr + kShiftUniCharOffset;
if ( shiftUniChar < kShiftUniCharLimit ) {
rangeIndex = classAndReplIndex[shiftUniChar >> kLoFieldBitSize];
if (rangeIndex >= 0) {
shiftUniCharLo = shiftUniChar & kLoFieldMask;
currCharClass = combClassRanges[rangeIndex][shiftUniCharLo];
}
}
if ( currCharClass == 0 ) {
outNameCombSeqPtr = NULL;
*outNamePtr++ = *replDataPtr++;
} else if ( outNameCombSeqPtr == NULL ) {
outNameCombSeqPtr = outNamePtr;
maxClassValueInSeq = currCharClass;
*outNamePtr++ = *replDataPtr++;
} else if ( currCharClass >= maxClassValueInSeq ) {
maxClassValueInSeq = currCharClass;
*outNamePtr++ = *replDataPtr++;
} else if ( outNamePtr - outNameCombSeqPtr == 1) {
*outNamePtr++ = *outNameCombSeqPtr;
*outNameCombSeqPtr = *replDataPtr++;
} else {
u_int16_t * outNameCombCharPtr;
u_int32_t combCharClass;
outNameCombCharPtr = outNamePtr++;
while (outNameCombCharPtr > outNameCombSeqPtr) {
shiftUniChar = *(outNameCombCharPtr - 1) + kShiftUniCharOffset;
rangeIndex = classAndReplIndex[shiftUniChar >> kLoFieldBitSize];
shiftUniCharLo = shiftUniChar & kLoFieldMask;
combCharClass = combClassRanges[rangeIndex][shiftUniCharLo];
if (combCharClass <= currCharClass)
break;
*outNameCombCharPtr = *(outNameCombCharPtr - 1);
outNameCombCharPtr--;
}
*outNameCombCharPtr = *replDataPtr++;
}
}
didModifyName = 1;
continue;
}
CheckCombClass:
currCharClass = combClassRanges[rangeIndex][shiftUniCharLo];
if ( currCharClass == 0 ) {
goto CopyBaseChar;
}
if ( outNameCombSeqPtr == NULL ) {
outNameCombSeqPtr = outNamePtr;
maxClassValueInSeq = currCharClass;
goto CopyChar;
}
if ( currCharClass >= maxClassValueInSeq ) {
maxClassValueInSeq = currCharClass;
goto CopyChar;
}
if (outNamePtr > outNameLastPtr) {
didModifyName = 0;
break;
}
if (outNamePtr - outNameCombSeqPtr == 1) {
*outNamePtr++ = *outNameCombSeqPtr;
*outNameCombSeqPtr = *inNamePtr++;
} else {
u_int16_t * outNameCombCharPtr;
u_int32_t combCharClass;
outNameCombCharPtr = outNamePtr++;
while (outNameCombCharPtr > outNameCombSeqPtr) {
shiftUniChar = *(outNameCombCharPtr - 1) + kShiftUniCharOffset;
rangeIndex = classAndReplIndex[shiftUniChar >> kLoFieldBitSize];
shiftUniCharLo = shiftUniChar & kLoFieldMask;
combCharClass = combClassRanges[rangeIndex][shiftUniCharLo];
if (combCharClass <= currCharClass)
break;
*outNameCombCharPtr = *(outNameCombCharPtr - 1);
outNameCombCharPtr--;
}
*outNameCombCharPtr = *inNamePtr++;
}
didModifyName = 1;
continue;
CopyBaseChar:
outNameCombSeqPtr = NULL;
CopyChar:
if (outNamePtr <= outNameLastPtr) {
*outNamePtr++ = *inNamePtr++;
} else {
didModifyName = 0;
break;
}
}
if (didModifyName) {
outFilename->length = outNamePtr - outFilename->unicode;
}
return didModifyName;
}
static Boolean DoesOverlap(SGlobPtr GPtr, UInt32 fileID, UInt32 startBlock, UInt32 blockCount, UInt8 forkType)
{
int i;
Boolean isOverlapped = false;
ExtentInfo *curExtentInfo;
ExtentsTable **extentsTableH = GPtr->overlappedExtents;
for (i = 0; i < (**extentsTableH).count; i++) {
curExtentInfo = &((**extentsTableH).extentInfo[i]);
if (curExtentInfo->startBlock < startBlock) {
if ((curExtentInfo->startBlock + curExtentInfo->blockCount) > startBlock) {
isOverlapped = true;
break;
}
} else {
if (curExtentInfo->startBlock < (startBlock + blockCount)) {
isOverlapped = true;
break;
}
}
}
if (isOverlapped) {
AddExtentToOverlapList(GPtr, fileID, startBlock, blockCount, forkType);
}
return isOverlapped;
}
static void CheckHFSPlusExtentRecords(SGlobPtr GPtr, UInt32 fileID, HFSPlusExtentRecord extent, UInt8 forkType)
{
int i;
for (i = 0; i < kHFSPlusExtentDensity; i++) {
if (extent[i].startBlock == 0) {
break;
}
DoesOverlap(GPtr, fileID, extent[i].startBlock, extent[i].blockCount, forkType);
}
return;
}
static void CheckHFSExtentRecords(SGlobPtr GPtr, UInt32 fileID, HFSExtentRecord extent, UInt8 forkType)
{
int i;
for (i = 0; i < kHFSExtentDensity; i++) {
if (extent[i].startBlock == 0) {
break;
}
DoesOverlap(GPtr, fileID, extent[i].startBlock, extent[i].blockCount, forkType);
}
return;
}
static int FindOrigOverlapFiles(SGlobPtr GPtr)
{
OSErr err = noErr;
Boolean isHFSPlus;
UInt16 selCode;
UInt16 recordSize;
UInt32 hint;
CatalogRecord catRecord;
CatalogKey catKey;
ExtentRecord extentRecord;
ExtentKey extentKey;
SVCB *calculatedVCB = GPtr->calculatedVCB;
isHFSPlus = VolumeObjectIsHFSPlus();
if (isHFSPlus) {
if (calculatedVCB->vcbAllocationFile) {
CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbAllocationFile->fcbFileID,
calculatedVCB->vcbAllocationFile->fcbExtents32, kDataFork);
}
if (calculatedVCB->vcbExtentsFile) {
CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbExtentsFile->fcbFileID,
calculatedVCB->vcbExtentsFile->fcbExtents32, kDataFork);
}
if (calculatedVCB->vcbCatalogFile) {
CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbCatalogFile->fcbFileID,
calculatedVCB->vcbCatalogFile->fcbExtents32, kDataFork);
}
if (calculatedVCB->vcbAttributesFile) {
CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbAttributesFile->fcbFileID,
calculatedVCB->vcbAttributesFile->fcbExtents32, kDataFork);
}
if (calculatedVCB->vcbStartupFile) {
CheckHFSPlusExtentRecords(GPtr, calculatedVCB->vcbStartupFile->fcbFileID,
calculatedVCB->vcbStartupFile->fcbExtents32, kDataFork);
}
} else {
if (calculatedVCB->vcbExtentsFile) {
CheckHFSExtentRecords(GPtr, calculatedVCB->vcbExtentsFile->fcbFileID,
calculatedVCB->vcbExtentsFile->fcbExtents16, kDataFork);
}
if (calculatedVCB->vcbCatalogFile) {
CheckHFSExtentRecords(GPtr, calculatedVCB->vcbCatalogFile->fcbFileID,
calculatedVCB->vcbCatalogFile->fcbExtents16, kDataFork);
}
}
selCode = 0x8001;
err = GetBTreeRecord(GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint);
if (err != noErr) {
goto traverseExtents;
}
selCode = 1;
do {
if ((catRecord.recordType == kHFSPlusFileRecord) ||
(catRecord.recordType == kHFSFileRecord)) {
if (isHFSPlus) {
CheckHFSPlusExtentRecords(GPtr, catRecord.hfsPlusFile.fileID,
catRecord.hfsPlusFile.dataFork.extents, kDataFork);
CheckHFSPlusExtentRecords(GPtr, catRecord.hfsPlusFile.fileID,
catRecord.hfsPlusFile.resourceFork.extents, kRsrcFork);
} else {
CheckHFSExtentRecords(GPtr, catRecord.hfsFile.fileID,
catRecord.hfsFile.dataExtents, kDataFork);
CheckHFSExtentRecords(GPtr, catRecord.hfsFile.fileID,
catRecord.hfsFile.rsrcExtents, kRsrcFork);
}
}
err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint );
} while (err == noErr);
traverseExtents:
selCode = 0x8001;
err = GetBTreeRecord(GPtr->calculatedExtentsFCB, selCode, &extentKey, &extentRecord, &recordSize, &hint);
if (err != noErr) {
goto out;
}
selCode = 1;
do {
if (isHFSPlus) {
CheckHFSPlusExtentRecords(GPtr, extentKey.hfsPlus.fileID,
extentRecord.hfsPlus, extentKey.hfsPlus.forkType);
} else {
CheckHFSExtentRecords(GPtr, extentKey.hfs.fileID, extentRecord.hfs,
extentKey.hfs.forkType);
}
err = GetBTreeRecord(GPtr->calculatedExtentsFCB, selCode, &extentKey, &extentRecord, &recordSize, &hint);
} while (err == noErr);
out:
if (err == btNotFound) {
err = noErr;
}
return err;
}
void PrintOverlapFiles (SGlobPtr GPtr)
{
OSErr err;
ExtentsTable **extentsTableH;
ExtentInfo *extentInfo;
unsigned int numOverlapExtents;
unsigned int buflen, filepathlen;
char *filepath = NULL;
char filenum[32];
UInt32 lastID = 0;
UInt32 bytesWritten;
Boolean printMsg;
Boolean isHFSPlus;
int i;
isHFSPlus = VolumeObjectIsHFSPlus();
extentsTableH = GPtr->overlappedExtents;
numOverlapExtents = (**extentsTableH).count;
qsort((**extentsTableH).extentInfo, numOverlapExtents, sizeof(ExtentInfo),
CompareExtentFileID);
buflen = PATH_MAX * 4;
if (isHFSPlus) {
filepath = malloc (buflen);
}
for (i = 0; i < numOverlapExtents; i++) {
extentInfo = &((**extentsTableH).extentInfo[i]);
if (lastID == extentInfo->fileID) {
continue;
}
lastID = extentInfo->fileID;
printMsg = false;
if (filepath) {
bytesWritten = sprintf (filepath, "%u ", extentInfo->fileID);
filepathlen = buflen - bytesWritten;
if (extentInfo->fileID >= kHFSFirstUserCatalogNodeID) {
err = GetFileNamePathByID (GPtr, extentInfo->fileID, (filepath + bytesWritten), &filepathlen, NULL, NULL, NULL);
} else {
err = GetSystemFileName (extentInfo->fileID, (filepath + bytesWritten), &filepathlen);
}
if (err == noErr) {
PrintError(GPtr, E_OvlExt, 1, filepath);
printMsg = true;
}
}
if (printMsg == false) {
sprintf(filenum, "%u", extentInfo->fileID);
PrintError(GPtr, E_OvlExt, 1, filenum);
}
}
if (filepath) {
free (filepath);
}
return;
}
static int CompareExtentFileID(const void *first, const void *second)
{
return (((ExtentInfo *)first)->fileID -
((ExtentInfo *)second)->fileID);
}