#include "BTree.h"
#include "Scavenger.h"
enum
{
kTwoGigSectors = 0x00400000,
kDataForkType = 0,
kResourceForkType = 0xFF,
kPreviousRecord = -1,
kSectorSize = 512 };
static OSErr ExtentsToExtDataRec(
HFSPlusExtentRecord oldExtents,
HFSExtentRecord newExtents);
OSErr FindExtentRecord(
const SVCB *vcb,
UInt8 forkType,
UInt32 fileID,
UInt32 startBlock,
Boolean allowPrevious,
HFSPlusExtentKey *foundKey,
HFSPlusExtentRecord foundData,
UInt32 *foundHint);
OSErr DeleteExtentRecord(
const SVCB *vcb,
UInt8 forkType,
UInt32 fileID,
UInt32 startBlock);
static OSErr CreateExtentRecord(
const SVCB *vcb,
HFSPlusExtentKey *key,
HFSPlusExtentRecord extents,
UInt32 *hint);
OSErr GetFCBExtentRecord(
const SVCB *vcb,
const SFCB *fcb,
HFSPlusExtentRecord extents);
static OSErr SetFCBExtentRecord(
const SVCB *vcb,
SFCB *fcb,
HFSPlusExtentRecord extents);
static OSErr SearchExtentFile(
const SVCB *vcb,
const SFCB *fcb,
UInt64 filePosition,
HFSPlusExtentKey *foundExtentKey,
HFSPlusExtentRecord foundExtentData,
UInt32 *foundExtentDataIndex,
UInt32 *extentBTreeHint,
UInt32 *endingFABNPlusOne );
static OSErr SearchExtentRecord(
const SVCB *vcb,
UInt32 searchFABN,
const HFSPlusExtentRecord extentData,
UInt32 extentDataStartFABN,
UInt32 *foundExtentDataOffset,
UInt32 *endingFABNPlusOne,
Boolean *noMoreExtents);
#if 0
static OSErr DeallocateFork(
SVCB *vcb,
HFSCatalogNodeID fileID,
UInt8 forkType,
HFSPlusExtentRecord catalogExtents,
Boolean * recordDeleted);
static OSErr TruncateExtents(
SVCB *vcb,
UInt8 forkType,
UInt32 fileID,
UInt32 startBlock,
Boolean * recordDeleted);
#endif
static OSErr MapFileBlockFromFCB(
const SVCB *vcb,
const SFCB *fcb,
UInt64 offset, UInt32 *firstFABN, UInt32 *firstBlock, UInt32 *nextFABN);
static Boolean ExtentsAreIntegral(
const HFSPlusExtentRecord extentRecord,
UInt32 mask,
UInt32 *blocksChecked,
Boolean *checkedLastExtent);
OSErr FindExtentRecord(
const SVCB *vcb,
UInt8 forkType,
UInt32 fileID,
UInt32 startBlock,
Boolean allowPrevious,
HFSPlusExtentKey *foundKey,
HFSPlusExtentRecord foundData,
UInt32 *foundHint)
{
OSErr err;
UInt16 foundSize;
err = noErr;
if (vcb->vcbSignature == kHFSSigWord) {
HFSExtentKey key;
HFSExtentKey extentKey;
HFSExtentRecord extentData;
key.keyLength = kHFSExtentKeyMaximumLength;
key.forkType = forkType;
key.fileID = fileID;
key.startBlock = startBlock;
err = SearchBTreeRecord(vcb->vcbExtentsFile, &key, kNoHint, &extentKey, &extentData,
&foundSize, foundHint);
if (err == btNotFound && allowPrevious) {
err = GetBTreeRecord(vcb->vcbExtentsFile, kPreviousRecord, &extentKey, &extentData,
&foundSize, foundHint);
if (err == (OSErr) fsBTStartOfIterationErr) err = btNotFound;
if (err == noErr) {
if (extentKey.fileID != fileID || extentKey.forkType != forkType)
err = btNotFound;
}
}
if (err == noErr) {
UInt16 i;
foundKey->keyLength = kHFSPlusExtentKeyMaximumLength;
foundKey->forkType = extentKey.forkType;
foundKey->pad = 0;
foundKey->fileID = extentKey.fileID;
foundKey->startBlock = extentKey.startBlock;
foundData[0].startBlock = extentData[0].startBlock;
foundData[0].blockCount = extentData[0].blockCount;
foundData[1].startBlock = extentData[1].startBlock;
foundData[1].blockCount = extentData[1].blockCount;
foundData[2].startBlock = extentData[2].startBlock;
foundData[2].blockCount = extentData[2].blockCount;
for (i = 3; i < kHFSPlusExtentDensity; ++i)
{
foundData[i].startBlock = 0;
foundData[i].blockCount = 0;
}
}
}
else { HFSPlusExtentKey key;
HFSPlusExtentKey extentKey;
HFSPlusExtentRecord extentData;
key.keyLength = kHFSPlusExtentKeyMaximumLength;
key.forkType = forkType;
key.pad = 0;
key.fileID = fileID;
key.startBlock = startBlock;
err = SearchBTreeRecord(vcb->vcbExtentsFile, &key, kNoHint, &extentKey, &extentData,
&foundSize, foundHint);
if (err == btNotFound && allowPrevious) {
err = GetBTreeRecord(vcb->vcbExtentsFile, kPreviousRecord, &extentKey, &extentData,
&foundSize, foundHint);
if (err == (OSErr) fsBTStartOfIterationErr) err = btNotFound;
if (err == noErr) {
if (extentKey.fileID != fileID || extentKey.forkType != forkType)
err = btNotFound;
}
}
if (err == noErr) {
CopyMemory(&extentKey, foundKey, sizeof(HFSPlusExtentKey));
CopyMemory(&extentData, foundData, sizeof(HFSPlusExtentRecord));
}
}
return err;
}
static OSErr CreateExtentRecord(
const SVCB *vcb,
HFSPlusExtentKey *key,
HFSPlusExtentRecord extents,
UInt32 *hint)
{
OSErr err;
err = noErr;
if (vcb->vcbSignature == kHFSSigWord) {
HFSExtentKey hfsKey;
HFSExtentRecord data;
hfsKey.keyLength = kHFSExtentKeyMaximumLength;
hfsKey.forkType = key->forkType;
hfsKey.fileID = key->fileID;
hfsKey.startBlock = key->startBlock;
err = ExtentsToExtDataRec(extents, data);
if (err == noErr)
err = InsertBTreeRecord(vcb->vcbExtentsFile, &hfsKey, data, sizeof(HFSExtentRecord), hint);
}
else { err = InsertBTreeRecord(vcb->vcbExtentsFile, key, extents, sizeof(HFSPlusExtentRecord), hint);
}
return err;
}
OSErr DeleteExtentRecord(
const SVCB *vcb,
UInt8 forkType,
UInt32 fileID,
UInt32 startBlock)
{
OSErr err;
err = noErr;
if (vcb->vcbSignature == kHFSSigWord) {
HFSExtentKey key;
key.keyLength = kHFSExtentKeyMaximumLength;
key.forkType = forkType;
key.fileID = fileID;
key.startBlock = startBlock;
err = DeleteBTreeRecord( vcb->vcbExtentsFile, &key );
}
else { HFSPlusExtentKey key;
key.keyLength = kHFSPlusExtentKeyMaximumLength;
key.forkType = forkType;
key.pad = 0;
key.fileID = fileID;
key.startBlock = startBlock;
err = DeleteBTreeRecord( vcb->vcbExtentsFile, &key );
}
return err;
}
OSErr MapFileBlockC (
SVCB *vcb, SFCB *fcb, UInt32 numberOfBytes, UInt64 sectorOffset, UInt64 *startSector, UInt32 *availableBytes) {
OSErr err;
UInt32 allocBlockSize; HFSPlusExtentKey foundKey;
HFSPlusExtentRecord foundData;
UInt32 foundIndex;
UInt32 hint;
UInt32 firstFABN = 0; UInt32 nextFABN; UInt64 dataEnd; UInt32 startBlock = 0; UInt64 temp;
allocBlockSize = vcb->vcbBlockSize >> kSectorShift;
err = MapFileBlockFromFCB(vcb, fcb, sectorOffset, &firstFABN, &startBlock, &nextFABN);
if (err != noErr) {
err = SearchExtentFile(vcb, fcb, sectorOffset, &foundKey, foundData, &foundIndex, &hint, &nextFABN);
if (err == noErr) {
startBlock = foundData[foundIndex].startBlock;
firstFABN = nextFABN - foundData[foundIndex].blockCount;
}
}
if (err != noErr)
{
return err;
}
temp = fcb->fcbPhysicalSize >> kSectorShift;
dataEnd = (UInt64) nextFABN * allocBlockSize; if (temp < dataEnd) dataEnd = temp;
temp = sectorOffset - ((UInt64) firstFABN * allocBlockSize); temp += (UInt64)startBlock * (UInt64)allocBlockSize; if (vcb->vcbSignature == kHFSPlusSigWord)
temp += vcb->vcbEmbeddedOffset/512; else
temp += vcb->vcbAlBlSt;
*startSector = temp;
temp = dataEnd - sectorOffset;
if (temp >= kTwoGigSectors)
temp = kTwoGigSectors-1; temp <<= kSectorShift; if (temp > numberOfBytes)
*availableBytes = numberOfBytes; else
*availableBytes = temp;
return noErr;
}
#if 1
OSErr ReleaseExtents(
SVCB *vcb,
const HFSPlusExtentRecord extentRecord,
UInt32 *numReleasedAllocationBlocks,
Boolean *releasedLastExtent)
{
UInt32 extentIndex;
UInt32 numberOfExtents;
OSErr err = noErr;
*numReleasedAllocationBlocks = 0;
*releasedLastExtent = false;
if (vcb->vcbSignature == kHFSPlusSigWord)
numberOfExtents = kHFSPlusExtentDensity;
else
numberOfExtents = kHFSExtentDensity;
for( extentIndex = 0; extentIndex < numberOfExtents; extentIndex++)
{
UInt32 numAllocationBlocks;
numAllocationBlocks = extentRecord[extentIndex].blockCount;
if ( numAllocationBlocks == 0 )
{
*releasedLastExtent = true;
break;
}
err = ReleaseBitmapBits( extentRecord[extentIndex].startBlock, numAllocationBlocks );
if ( err != noErr )
break;
*numReleasedAllocationBlocks += numAllocationBlocks; }
return( err );
}
#endif
static OSErr TruncateExtents(
SVCB *vcb,
UInt8 forkType,
UInt32 fileID,
UInt32 startBlock,
Boolean * recordDeleted)
{
OSErr err;
Boolean releasedLastExtent;
UInt32 numberExtentsReleased;
UInt32 hint;
HFSPlusExtentKey key;
HFSPlusExtentRecord extents;
while (true) {
err = FindExtentRecord(vcb, forkType, fileID, startBlock, false, &key, extents, &hint);
if (err != noErr) {
if (err == btNotFound)
err = noErr;
break;
}
err = ReleaseExtents( vcb, extents, &numberExtentsReleased, &releasedLastExtent );
if (err != noErr) break;
err = DeleteExtentRecord(vcb, forkType, fileID, startBlock);
if (err != noErr) break;
*recordDeleted = true; startBlock += numberExtentsReleased;
}
return err;
}
static OSErr DeallocateFork(
SVCB *vcb,
HFSCatalogNodeID fileID,
UInt8 forkType,
HFSPlusExtentRecord catalogExtents,
Boolean * recordDeleted) {
OSErr err;
UInt32 numReleasedAllocationBlocks;
Boolean releasedLastExtent;
err = ReleaseExtents( vcb, catalogExtents, &numReleasedAllocationBlocks, &releasedLastExtent );
if (err == noErr && !releasedLastExtent)
err = TruncateExtents(vcb, forkType, fileID, numReleasedAllocationBlocks, recordDeleted);
return( err );
}
OSErr FlushExtentFile( SVCB *vcb )
{
OSErr err;
err = BTFlushPath(vcb->vcbExtentsFile);
if ( err == noErr )
{
if( ( vcb->vcbExtentsFile->fcbFlags & fcbModifiedMask ) != 0 )
{
(void) MarkVCBDirty( vcb );
err = FlushVolumeControlBlock( vcb );
}
}
return( err );
}
OSErr DeallocateFile(SVCB *vcb, CatalogRecord * fileRec)
{
int i;
OSErr errDF, errRF;
Boolean recordDeleted = false;
errDF = errRF = 0;
if (fileRec->recordType == kHFSFileRecord) {
HFSPlusExtentRecord dataForkExtents;
HFSPlusExtentRecord rsrcForkExtents;
for (i = 0; i < kHFSExtentDensity; ++i) {
dataForkExtents[i].startBlock =
(UInt32) (fileRec->hfsFile.dataExtents[i].startBlock);
dataForkExtents[i].blockCount =
(UInt32) (fileRec->hfsFile.dataExtents[i].blockCount);
rsrcForkExtents[i].startBlock =
(UInt32) (fileRec->hfsFile.rsrcExtents[i].startBlock);
rsrcForkExtents[i].blockCount =
(UInt32) (fileRec->hfsFile.rsrcExtents[i].blockCount);
}
ClearMemory(&dataForkExtents[i].startBlock,
sizeof(HFSPlusExtentRecord) - sizeof(HFSExtentRecord));
ClearMemory(&rsrcForkExtents[i].startBlock,
sizeof(HFSPlusExtentRecord) - sizeof(HFSExtentRecord));
errDF = DeallocateFork(vcb, fileRec->hfsFile.fileID, kDataForkType,
dataForkExtents, &recordDeleted );
errRF = DeallocateFork(vcb, fileRec->hfsFile.fileID, kResourceForkType,
rsrcForkExtents, &recordDeleted );
}
else if (fileRec->recordType == kHFSPlusFileRecord) {
errDF = DeallocateFork(vcb, fileRec->hfsPlusFile.fileID, kDataForkType,
fileRec->hfsPlusFile.dataFork.extents, &recordDeleted );
errRF = DeallocateFork(vcb, fileRec->hfsPlusFile.fileID, kResourceForkType,
fileRec->hfsPlusFile.resourceFork.extents, &recordDeleted );
}
if (recordDeleted)
(void) FlushExtentFile(vcb);
MarkVCBDirty(vcb);
return (errDF ? errDF : errRF);
}
OSErr ExtendFileC (
SVCB *vcb, SFCB *fcb, UInt32 sectorsToAdd, UInt32 flags, UInt32 *actualSectorsAdded){
OSErr err;
Boolean wantContig;
Boolean needsFlush;
UInt32 sectorsPerBlock;
UInt32 blocksToAdd; UInt32 blocksPerClump; UInt32 maxBlocksToAdd; UInt32 eofBlocks; HFSPlusExtentKey foundKey; HFSPlusExtentRecord foundData;
UInt32 foundIndex; UInt32 hint; UInt32 nextBlock; UInt32 startBlock;
UInt32 actualStartBlock;
UInt32 actualNumBlocks;
UInt32 numExtentsPerRecord;
UInt32 blocksAdded;
needsFlush = false; blocksAdded = 0;
*actualSectorsAdded = 0;
if (vcb->vcbSignature == kHFSPlusSigWord)
numExtentsPerRecord = kHFSPlusExtentDensity;
else
numExtentsPerRecord = kHFSExtentDensity;
sectorsPerBlock = vcb->vcbBlockSize >> kSectorShift;
blocksToAdd = DivideAndRoundUp(sectorsToAdd, sectorsPerBlock);
eofBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
if ( vcb->vcbSignature == kHFSPlusSigWord )
{
}
else
{
UInt32 maxFileBlocks; maxFileBlocks = (kTwoGigSectors-1) / sectorsPerBlock;
if (blocksToAdd > maxFileBlocks || (blocksToAdd + eofBlocks) > maxFileBlocks) {
err = fileBoundsErr;
goto ErrorExit;
}
}
if ((flags & kEFAllMask) && blocksToAdd > vcb->vcbFreeBlocks) {
err = dskFulErr;
goto ErrorExit;
}
err = SearchExtentFile(vcb, fcb, (eofBlocks+blocksToAdd) * sectorsPerBlock - 1, &foundKey, foundData, &foundIndex, &hint, &nextBlock);
if (err == noErr) {
eofBlocks += blocksToAdd; blocksAdded += blocksToAdd;
goto Exit;
}
if (err != fxRangeErr) goto ErrorExit;
if (nextBlock > eofBlocks) {
blocksAdded += nextBlock - eofBlocks;
blocksToAdd -= nextBlock - eofBlocks;
eofBlocks = nextBlock;
}
blocksPerClump = fcb->fcbClumpSize / vcb->vcbBlockSize;
if (blocksPerClump == 0)
blocksPerClump = 1;
err = noErr;
wantContig = true;
do {
if (flags & kEFNoClumpMask) {
maxBlocksToAdd = blocksToAdd;
}
else {
maxBlocksToAdd = DivideAndRoundUp(blocksToAdd, blocksPerClump);
maxBlocksToAdd *= blocksPerClump;
}
startBlock = foundData[foundIndex].startBlock + foundData[foundIndex].blockCount;
err = BlockAllocate(vcb, startBlock, blocksToAdd, maxBlocksToAdd, wantContig, &actualStartBlock, &actualNumBlocks);
if (err == dskFulErr) {
if (flags & kEFContigMask)
break; if (wantContig) {
err = noErr;
wantContig = false;
continue;
}
if (actualNumBlocks != 0)
err = noErr;
}
if (err == noErr) {
if (actualStartBlock == startBlock) {
foundData[foundIndex].blockCount += actualNumBlocks;
err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
if (err != noErr) break;
}
else {
UInt16 i;
if (foundData[foundIndex].blockCount != 0) ++foundIndex; if (foundIndex == numExtentsPerRecord) {
if (fcb->fcbFileID == kHFSExtentsFileID || (flags & kEFNoExtOvflwMask)) {
(void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
err = fxOvFlErr; break;
}
foundKey.keyLength = kHFSPlusExtentKeyMaximumLength;
if (fcb->fcbFlags & fcbResourceMask)
foundKey.forkType = kResourceForkType;
else
foundKey.forkType = kDataForkType;
foundKey.pad = 0;
foundKey.fileID = fcb->fcbFileID;
foundKey.startBlock = nextBlock;
foundData[0].startBlock = actualStartBlock;
foundData[0].blockCount = actualNumBlocks;
for (i = 1; i < kHFSPlusExtentDensity; ++i)
{
foundData[i].startBlock = 0;
foundData[i].blockCount = 0;
}
foundIndex = 0;
err = CreateExtentRecord(vcb, &foundKey, foundData, &hint);
if (err == fxOvFlErr) {
(void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks);
err = dskFulErr;
}
if (err != noErr) break;
needsFlush = true; }
else {
foundData[foundIndex].startBlock = actualStartBlock;
foundData[foundIndex].blockCount = actualNumBlocks;
err = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
if (err != noErr) break;
}
}
nextBlock += actualNumBlocks;
if (actualNumBlocks > blocksToAdd) {
blocksAdded += blocksToAdd;
eofBlocks += blocksToAdd;
blocksToAdd = 0;
}
else {
blocksAdded += actualNumBlocks;
blocksToAdd -= actualNumBlocks;
eofBlocks += actualNumBlocks;
}
if (flags & kEFContigMask) {
if (blocksToAdd != 0)
err = dskFulErr;
break; }
}
} while (err == noErr && blocksToAdd);
ErrorExit:
Exit:
*actualSectorsAdded = blocksAdded * sectorsPerBlock;
if (blocksAdded) {
fcb->fcbPhysicalSize = (UInt64)eofBlocks * (UInt64)vcb->vcbBlockSize;
fcb->fcbFlags |= fcbModifiedMask;
}
if (needsFlush)
(void) FlushExtentFile(vcb);
return err;
}
#if 0
OSErr TruncateFileC (
SVCB *vcb, SFCB *fcb, UInt32 eofSectors, Boolean truncateToExtent) {
OSErr err;
UInt32 nextBlock; UInt32 startBlock; UInt32 physNumBlocks; UInt32 numBlocks;
HFSPlusExtentKey key; UInt32 hint; HFSPlusExtentRecord extentRecord;
UInt32 extentIndex;
UInt32 extentNextBlock;
UInt32 numExtentsPerRecord;
UInt32 sectorsPerBlock;
UInt8 forkType;
Boolean extentChanged; Boolean recordDeleted;
recordDeleted = false;
sectorsPerBlock = vcb->vcbBlockSize >> kSectorShift;
if (vcb->vcbSignature == kHFSPlusSigWord)
numExtentsPerRecord = kHFSPlusExtentDensity;
else
numExtentsPerRecord = kHFSExtentDensity;
if (fcb->fcbFlags & fcbResourceMask)
forkType = kResourceForkType;
else
forkType = kDataForkType;
physNumBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
nextBlock = DivideAndRoundUp(eofSectors, sectorsPerBlock); eofSectors = nextBlock * sectorsPerBlock; if ((fcb->fcbFlags & fcbLargeFileMask) == 0 && eofSectors >= kTwoGigSectors) {
#if DEBUG_BUILD
DebugStr("\pHFS: Trying to truncate a file to 2GB or more");
#endif
err = fileBoundsErr;
goto ErrorExit;
}
fcb->fcbPhysicalSize = (UInt64)nextBlock * (UInt64)vcb->vcbBlockSize;
fcb->fcbFlags |= fcbModifiedMask;
if (eofSectors == 0) {
int i;
err = GetFCBExtentRecord(vcb, fcb, extentRecord);
if (err != noErr) goto ErrorExit;
err = DeallocateFork(vcb, fcb->fcbFileID, forkType, extentRecord, &recordDeleted);
if (err != noErr) goto ErrorExit;
if (err == noErr) {
for (i=0; i < numExtentsPerRecord; i++) {
extentRecord[i].startBlock = 0;
extentRecord[i].blockCount = 0;
}
}
err = SetFCBExtentRecord((VCB *) vcb, fcb, extentRecord);
goto Done;
}
err = SearchExtentFile(vcb, fcb, eofSectors-1, &key, extentRecord, &extentIndex, &hint, &extentNextBlock);
if (err != noErr) goto ErrorExit;
extentChanged = false;
if (!truncateToExtent) {
numBlocks = extentNextBlock - nextBlock; if (numBlocks != 0) {
startBlock = extentRecord[extentIndex].startBlock + extentRecord[extentIndex].blockCount - numBlocks;
err = BlockDeallocate(vcb, startBlock, numBlocks);
if (err != noErr) goto ErrorExit;
extentRecord[extentIndex].blockCount -= numBlocks;
if (extentRecord[extentIndex].blockCount == 0)
extentRecord[extentIndex].startBlock = 0;
extentChanged = true;
}
}
nextBlock = extentNextBlock; ++extentIndex;
while (extentIndex < numExtentsPerRecord && extentRecord[extentIndex].blockCount != 0) {
numBlocks = extentRecord[extentIndex].blockCount;
err = BlockDeallocate(vcb, extentRecord[extentIndex].startBlock, numBlocks);
if (err != noErr) goto ErrorExit;
nextBlock += numBlocks;
extentRecord[extentIndex].startBlock = 0;
extentRecord[extentIndex].blockCount = 0;
extentChanged = true;
++extentIndex;
}
if (extentChanged) {
err = UpdateExtentRecord(vcb, fcb, &key, extentRecord, hint);
if (err != noErr) goto ErrorExit;
}
if (nextBlock < physNumBlocks)
err = TruncateExtents(vcb, forkType, fcb->fcbFileID, nextBlock, &recordDeleted);
Done:
ErrorExit:
#if DEBUG_BUILD
if (err == fxRangeErr)
DebugStr("\pAbout to return fxRangeErr");
#endif
if (recordDeleted)
(void) FlushExtentFile(vcb);
return err;
}
#endif
static OSErr SearchExtentRecord(
const SVCB *vcb,
UInt32 searchFABN,
const HFSPlusExtentRecord extentData,
UInt32 extentDataStartFABN,
UInt32 *foundExtentIndex,
UInt32 *endingFABNPlusOne,
Boolean *noMoreExtents)
{
OSErr err = noErr;
UInt32 extentIndex;
UInt32 numberOfExtents;
UInt32 numAllocationBlocks;
Boolean foundExtent;
*endingFABNPlusOne = extentDataStartFABN;
*noMoreExtents = false;
foundExtent = false;
if (vcb->vcbSignature == kHFSPlusSigWord)
numberOfExtents = kHFSPlusExtentDensity;
else
numberOfExtents = kHFSExtentDensity;
for( extentIndex = 0; extentIndex < numberOfExtents; ++extentIndex )
{
numAllocationBlocks = extentData[extentIndex].blockCount;
if ( numAllocationBlocks == 0 )
{
break;
}
*endingFABNPlusOne += numAllocationBlocks;
if( searchFABN < *endingFABNPlusOne )
{
foundExtent = true;
break;
}
}
if( foundExtent )
{
*foundExtentIndex = extentIndex;
}
else
{
if( extentIndex > 0 )
{
*foundExtentIndex = extentIndex - 1;
}
else
{
*foundExtentIndex = 0;
}
if (extentIndex < numberOfExtents)
*noMoreExtents = true;
err = fxRangeErr;
}
return( err );
}
static OSErr SearchExtentFile(
const SVCB *vcb,
const SFCB *fcb,
UInt64 sectorOffset,
HFSPlusExtentKey *foundExtentKey,
HFSPlusExtentRecord foundExtentData,
UInt32 *foundExtentIndex,
UInt32 *extentBTreeHint,
UInt32 *endingFABNPlusOne )
{
OSErr err;
UInt32 filePositionBlock;
Boolean noMoreExtents = true;
filePositionBlock = sectorOffset / (vcb->vcbBlockSize >> kSectorShift);
err = GetFCBExtentRecord(vcb, fcb, foundExtentData);
if (err == noErr)
err = SearchExtentRecord( vcb, filePositionBlock, foundExtentData, 0,
foundExtentIndex, endingFABNPlusOne, &noMoreExtents );
if( err == noErr ) {
*extentBTreeHint = kNoHint; foundExtentKey->keyLength = 0;
goto Exit;
}
if ( noMoreExtents ) {
*extentBTreeHint = kNoHint; foundExtentKey->keyLength = 0; err = fxRangeErr; goto Exit;
}
err = FindExtentRecord(vcb, (fcb->fcbFlags & fcbResourceMask) ? kResourceForkType : kDataForkType,
fcb->fcbFileID, filePositionBlock, true, foundExtentKey, foundExtentData, extentBTreeHint);
if (err == btNotFound) {
*extentBTreeHint = kNoHint;
foundExtentKey->keyLength = 0;
err = GetFCBExtentRecord(vcb, fcb, foundExtentData);
return fxRangeErr;
}
if (err == noErr) {
err = SearchExtentRecord(vcb, filePositionBlock, foundExtentData, foundExtentKey->startBlock,
foundExtentIndex, endingFABNPlusOne, &noMoreExtents);
}
Exit:
return err;
}
OSErr UpdateExtentRecord (
const SVCB *vcb,
SFCB *fcb,
const HFSPlusExtentKey *extentFileKey,
HFSPlusExtentRecord extentData,
UInt32 extentBTreeHint)
{
OSErr err;
UInt32 foundHint;
UInt16 foundDataSize;
if (extentFileKey->keyLength == 0) { err = SetFCBExtentRecord(vcb, fcb, extentData);
fcb->fcbFlags |= fcbModifiedMask;
}
else {
if (vcb->vcbSignature == kHFSSigWord) {
HFSExtentKey key; HFSExtentKey foundKey; HFSExtentRecord foundData;
key.keyLength = kHFSExtentKeyMaximumLength;
key.forkType = extentFileKey->forkType;
key.fileID = extentFileKey->fileID;
key.startBlock = extentFileKey->startBlock;
err = SearchBTreeRecord(vcb->vcbExtentsFile, &key, extentBTreeHint,
&foundKey, &foundData, &foundDataSize, &foundHint);
if (err == noErr)
err = ExtentsToExtDataRec(extentData, (HFSExtentDescriptor *)&foundData);
if (err == noErr)
err = ReplaceBTreeRecord(vcb->vcbExtentsFile, &foundKey, foundHint, &foundData, foundDataSize, &foundHint);
}
else { HFSPlusExtentKey foundKey; HFSPlusExtentRecord foundData;
err = SearchBTreeRecord(vcb->vcbExtentsFile, extentFileKey, extentBTreeHint,
&foundKey, &foundData, &foundDataSize, &foundHint);
if (err == noErr)
CopyMemory(extentData, &foundData, sizeof(HFSPlusExtentRecord));
if (err == noErr)
err = ReplaceBTreeRecord(vcb->vcbExtentsFile, &foundKey, foundHint, &foundData, foundDataSize, &foundHint);
}
}
return err;
}
void ExtDataRecToExtents(
const HFSExtentRecord oldExtents,
HFSPlusExtentRecord newExtents)
{
UInt32 i;
newExtents[0].startBlock = oldExtents[0].startBlock;
newExtents[0].blockCount = oldExtents[0].blockCount;
newExtents[1].startBlock = oldExtents[1].startBlock;
newExtents[1].blockCount = oldExtents[1].blockCount;
newExtents[2].startBlock = oldExtents[2].startBlock;
newExtents[2].blockCount = oldExtents[2].blockCount;
for (i = 3; i < kHFSPlusExtentDensity; ++i)
{
newExtents[i].startBlock = 0;
newExtents[i].blockCount = 0;
}
}
static OSErr ExtentsToExtDataRec(
HFSPlusExtentRecord oldExtents,
HFSExtentRecord newExtents)
{
OSErr err;
err = noErr;
newExtents[0].startBlock = oldExtents[0].startBlock;
newExtents[0].blockCount = oldExtents[0].blockCount;
newExtents[1].startBlock = oldExtents[1].startBlock;
newExtents[1].blockCount = oldExtents[1].blockCount;
newExtents[2].startBlock = oldExtents[2].startBlock;
newExtents[2].blockCount = oldExtents[2].blockCount;
#if DEBUG_BUILD
if (oldExtents[3].startBlock || oldExtents[3].blockCount) {
DebugStr("\pExtentRecord with > 3 extents is invalid for HFS");
err = fsDSIntErr;
}
#endif
return err;
}
OSErr GetFCBExtentRecord(
const SVCB *vcb,
const SFCB *fcb,
HFSPlusExtentRecord extents)
{
if (vcb->vcbSignature == kHFSPlusSigWord)
CopyMemory(fcb->fcbExtents32, extents, sizeof(HFSPlusExtentRecord));
else
ExtDataRecToExtents(fcb->fcbExtents16, extents);
return noErr;
}
static OSErr SetFCBExtentRecord(
const SVCB *vcb,
SFCB *fcb,
HFSPlusExtentRecord extents)
{
#if DEBUG_BUILD
if (fcb->fcbVolume != vcb)
DebugStr("\pVCB does not match FCB");
#endif
if (vcb->vcbSignature == kHFSPlusSigWord)
CopyMemory(extents, fcb->fcbExtents32, sizeof(HFSPlusExtentRecord));
else
(void) ExtentsToExtDataRec(extents, fcb->fcbExtents16);
return noErr;
}
static OSErr MapFileBlockFromFCB(
const SVCB *vcb,
const SFCB *fcb,
UInt64 sectorOffset, UInt32 *firstFABN, UInt32 *firstBlock, UInt32 *nextFABN) {
UInt32 index;
UInt32 offsetBlocks;
offsetBlocks = sectorOffset / (vcb->vcbBlockSize >> kSectorShift);
if (vcb->vcbSignature == kHFSSigWord) {
const HFSExtentDescriptor *extent;
UInt32 blockCount;
UInt32 currentFABN;
extent = fcb->fcbExtents16;
currentFABN = 0;
for (index=0; index<kHFSExtentDensity; index++) {
blockCount = extent->blockCount;
if (blockCount == 0)
return fxRangeErr;
if (offsetBlocks < blockCount) {
*firstFABN = currentFABN;
*firstBlock = extent->startBlock;
currentFABN += blockCount; *nextFABN = currentFABN;
return noErr; }
offsetBlocks -= blockCount;
currentFABN += blockCount;
extent++;
}
}
else {
const HFSPlusExtentDescriptor *extent;
UInt32 blockCount;
UInt32 currentFABN;
extent = fcb->fcbExtents32;
currentFABN = 0;
for (index=0; index<kHFSPlusExtentDensity; index++) {
blockCount = extent->blockCount;
if (blockCount == 0)
return fxRangeErr;
if (offsetBlocks < blockCount) {
*firstFABN = currentFABN;
*firstBlock = extent->startBlock;
*nextFABN = currentFABN + blockCount;
return noErr; }
offsetBlocks -= blockCount;
currentFABN += blockCount;
extent++;
}
}
return fxRangeErr;
}
#define FSBufferSize 32768
OSErr ZeroFileBlocks( SVCB *vcb, SFCB *fcb, UInt32 startingSector, UInt32 numberOfSectors )
{
Ptr buffer;
OSErr err;
HIOParam iopb;
UInt32 requestedBytes;
UInt32 actualBytes; UInt32 bufferSizeSectors = FSBufferSize >> kSectorShift;
UInt64 currentPosition = startingSector << kSectorShift;
buffer = AllocateMemory(FSBufferSize);
if ( buffer == NULL )
return( fileBoundsErr );
ClearMemory( buffer, FSBufferSize ); ClearMemory( &iopb, sizeof(iopb) );
iopb.ioRefNum = ResolveFileRefNum( fcb );
iopb.ioBuffer = buffer;
iopb.ioPosMode |= noCacheMask;
do
{
if ( numberOfSectors > bufferSizeSectors )
requestedBytes = FSBufferSize;
else
requestedBytes = numberOfSectors << kSectorShift;
err = CacheWriteInPlace( vcb, iopb.ioRefNum, &iopb, currentPosition, requestedBytes, &actualBytes );
if ( err || actualBytes == 0 )
goto BAIL;
currentPosition += actualBytes;
numberOfSectors -= (actualBytes >> kSectorShift);
} while( numberOfSectors > 0 );
BAIL:
DisposeMemory(buffer);
if ( err == noErr && numberOfSectors != 0 )
err = eofErr;
return( err );
}
static Boolean ExtentsAreIntegral(
const HFSPlusExtentRecord extentRecord,
UInt32 mask,
UInt32 *blocksChecked,
Boolean *checkedLastExtent)
{
UInt32 blocks;
UInt32 extentIndex;
*blocksChecked = 0;
*checkedLastExtent = false;
for(extentIndex = 0; extentIndex < kHFSPlusExtentDensity; extentIndex++)
{
blocks = extentRecord[extentIndex].blockCount;
if ( blocks == 0 )
{
*checkedLastExtent = true;
break;
}
*blocksChecked += blocks;
if (blocks & mask)
return false;
}
return true;
}
Boolean NodesAreContiguous(
SFCB *fcb,
UInt32 nodeSize)
{
SVCB *vcb;
UInt32 mask;
UInt32 startBlock;
UInt32 blocksChecked;
UInt32 hint;
HFSPlusExtentKey key;
HFSPlusExtentRecord extents;
OSErr result;
Boolean lastExtentReached;
vcb = (SVCB *)fcb->fcbVolume;
if (vcb->vcbBlockSize >= nodeSize)
return true;
mask = (nodeSize / vcb->vcbBlockSize) - 1;
(void) GetFCBExtentRecord(vcb, fcb, extents);
if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
return false;
if (lastExtentReached || ((UInt64)blocksChecked * (UInt64)vcb->vcbBlockSize) >= fcb->fcbPhysicalSize)
return true;
startBlock = blocksChecked;
while ( !lastExtentReached )
{
result = FindExtentRecord(vcb, kDataForkType, fcb->fcbFileID, startBlock, false, &key, extents, &hint);
if (result) break;
if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
return false;
startBlock += blocksChecked;
}
return true;
}