#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, char * filename, UInt32 forkType, UInt32 oldBlkCnt, UInt32 newBlkCnt);
static int RecordTruncation(UInt32 parID, 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 );
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 (gCIS.dirCount != gCIS.dirThreads)
gScavGlobals->CBTStat |= S_Orphan;
if (hfsplus && (gCIS.fileCount != gCIS.fileThreads))
gScavGlobals->CBTStat |= S_Orphan;
if (!hfsplus && (gCIS.fileThreads != gCIS.filesWithThreads))
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;
AttributeKey *attrKey;
BTreeIterator iterator;
ClearMemory(&iterator, sizeof(BTreeIterator));
attrKey = (AttributeKey *)&iterator.key;
attrKey->keyLength = kAttributeKeyMinimumLength;
attrKey->cnid = 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->cnid == 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;
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 = &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( 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, 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(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, 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(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;
}