FileExtentMapping.c [plain text]
#include "../../hfs.h"
#include "../../hfs_format.h"
#include "../../hfs_endian.h"
#include "../headers/FileMgrInternal.h"
#include "../headers/BTreesInternal.h"
#include <sys/malloc.h>
#include <sys/vnode_internal.h>
#if CONFIG_HFS_STD
static const int64_t kTwoGigabytes = 0x80000000LL;
#endif
enum
{
kDataForkType = 0,
kResourceForkType = 0xFF,
kPreviousRecord = -1
};
#if CONFIG_HFS_STD
static OSErr HFSPlusToHFSExtents(
const HFSPlusExtentRecord oldExtents,
HFSExtentRecord newExtents);
#endif
static OSErr FindExtentRecord(
const ExtendedVCB *vcb,
u_int8_t forkType,
u_int32_t fileID,
u_int32_t startBlock,
Boolean allowPrevious,
HFSPlusExtentKey *foundKey,
HFSPlusExtentRecord foundData,
u_int32_t *foundHint);
static OSErr DeleteExtentRecord(
const ExtendedVCB *vcb,
u_int8_t forkType,
u_int32_t fileID,
u_int32_t startBlock);
static OSErr CreateExtentRecord(
ExtendedVCB *vcb,
HFSPlusExtentKey *key,
HFSPlusExtentRecord extents,
u_int32_t *hint);
static OSErr GetFCBExtentRecord(
const FCB *fcb,
HFSPlusExtentRecord extents);
static OSErr SearchExtentRecord(
ExtendedVCB *vcb,
u_int32_t searchFABN,
const HFSPlusExtentRecord extentData,
u_int32_t extentDataStartFABN,
u_int32_t *foundExtentDataOffset,
u_int32_t *endingFABNPlusOne,
Boolean *noMoreExtents);
static OSErr ReleaseExtents(
ExtendedVCB *vcb,
const HFSPlusExtentRecord extentRecord,
u_int32_t *numReleasedAllocationBlocks,
Boolean *releasedLastExtent);
static OSErr DeallocateFork(
ExtendedVCB *vcb,
HFSCatalogNodeID fileID,
u_int8_t forkType,
HFSPlusExtentRecord catalogExtents,
Boolean * recordDeleted);
static OSErr TruncateExtents(
ExtendedVCB *vcb,
u_int8_t forkType,
u_int32_t fileID,
u_int32_t startBlock,
Boolean * recordDeleted);
static OSErr UpdateExtentRecord (
ExtendedVCB *vcb,
FCB *fcb,
int deleted,
const HFSPlusExtentKey *extentFileKey,
const HFSPlusExtentRecord extentData,
u_int32_t extentBTreeHint);
static Boolean ExtentsAreIntegral(
const HFSPlusExtentRecord extentRecord,
u_int32_t mask,
u_int32_t *blocksChecked,
Boolean *checkedLastExtent);
static OSErr FindExtentRecord(
const ExtendedVCB *vcb,
u_int8_t forkType,
u_int32_t fileID,
u_int32_t startBlock,
Boolean allowPrevious,
HFSPlusExtentKey *foundKey,
HFSPlusExtentRecord foundData,
u_int32_t *foundHint)
{
FCB * fcb;
struct BTreeIterator *btIterator = NULL;
FSBufferDescriptor btRecord;
OSErr err;
u_int16_t btRecordSize;
err = noErr;
if (foundHint)
*foundHint = 0;
fcb = GetFileControlBlock(vcb->extentsRefNum);
MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
if (btIterator == NULL) {
return memFullErr; }
bzero(btIterator, sizeof(*btIterator));
if (vcb->vcbSigWord != kHFSSigWord) {
HFSPlusExtentKey * extentKeyPtr;
HFSPlusExtentRecord extentData;
extentKeyPtr = (HFSPlusExtentKey*) &btIterator->key;
extentKeyPtr->keyLength = kHFSPlusExtentKeyMaximumLength;
extentKeyPtr->forkType = forkType;
extentKeyPtr->pad = 0;
extentKeyPtr->fileID = fileID;
extentKeyPtr->startBlock = startBlock;
btRecord.bufferAddress = &extentData;
btRecord.itemSize = sizeof(HFSPlusExtentRecord);
btRecord.itemCount = 1;
err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator);
if (err == btNotFound && allowPrevious) {
err = BTIterateRecord(fcb, kBTreePrevRecord, btIterator, &btRecord, &btRecordSize);
if (err == (OSErr) fsBTStartOfIterationErr) err = btNotFound;
if (err == noErr) {
if (extentKeyPtr->fileID != fileID || extentKeyPtr->forkType != forkType)
err = btNotFound;
}
}
if (err == noErr) {
if (foundKey)
BlockMoveData(extentKeyPtr, foundKey, sizeof(HFSPlusExtentKey));
BlockMoveData(&extentData, foundData, sizeof(HFSPlusExtentRecord));
}
}
#if CONFIG_HFS_STD
else {
HFSExtentKey * extentKeyPtr;
HFSExtentRecord extentData;
extentKeyPtr = (HFSExtentKey*) &btIterator->key;
extentKeyPtr->keyLength = kHFSExtentKeyMaximumLength;
extentKeyPtr->forkType = forkType;
extentKeyPtr->fileID = fileID;
extentKeyPtr->startBlock = startBlock;
btRecord.bufferAddress = &extentData;
btRecord.itemSize = sizeof(HFSExtentRecord);
btRecord.itemCount = 1;
err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator);
if (err == btNotFound && allowPrevious) {
err = BTIterateRecord(fcb, kBTreePrevRecord, btIterator, &btRecord, &btRecordSize);
if (err == (OSErr) fsBTStartOfIterationErr) err = btNotFound;
if (err == noErr) {
if (extentKeyPtr->fileID != fileID || extentKeyPtr->forkType != forkType)
err = btNotFound;
}
}
if (err == noErr) {
u_int16_t i;
if (foundKey) {
foundKey->keyLength = kHFSPlusExtentKeyMaximumLength;
foundKey->forkType = extentKeyPtr->forkType;
foundKey->pad = 0;
foundKey->fileID = extentKeyPtr->fileID;
foundKey->startBlock = extentKeyPtr->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;
}
}
}
#endif
if (foundHint)
*foundHint = btIterator->hint.nodeNum;
FREE(btIterator, M_TEMP);
return err;
}
static OSErr CreateExtentRecord(
ExtendedVCB *vcb,
HFSPlusExtentKey *key,
HFSPlusExtentRecord extents,
u_int32_t *hint)
{
struct BTreeIterator *btIterator = NULL;
FSBufferDescriptor btRecord;
u_int16_t btRecordSize;
int lockflags;
OSErr err;
err = noErr;
*hint = 0;
MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
if (btIterator == NULL) {
return memFullErr; }
bzero(btIterator, sizeof(*btIterator));
lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
if (vcb->vcbSigWord != kHFSSigWord) {
btRecordSize = sizeof(HFSPlusExtentRecord);
btRecord.bufferAddress = extents;
btRecord.itemSize = btRecordSize;
btRecord.itemCount = 1;
BlockMoveData(key, &btIterator->key, sizeof(HFSPlusExtentKey));
}
#if CONFIG_HFS_STD
else {
HFSExtentKey * keyPtr;
HFSExtentRecord data;
btRecordSize = sizeof(HFSExtentRecord);
btRecord.bufferAddress = &data;
btRecord.itemSize = btRecordSize;
btRecord.itemCount = 1;
keyPtr = (HFSExtentKey*) &btIterator->key;
keyPtr->keyLength = kHFSExtentKeyMaximumLength;
keyPtr->forkType = key->forkType;
keyPtr->fileID = key->fileID;
keyPtr->startBlock = key->startBlock;
err = HFSPlusToHFSExtents(extents, data);
}
#endif
if (err == noErr)
err = BTInsertRecord(GetFileControlBlock(vcb->extentsRefNum), btIterator, &btRecord, btRecordSize);
if (err == noErr)
*hint = btIterator->hint.nodeNum;
(void) BTFlushPath(GetFileControlBlock(vcb->extentsRefNum));
hfs_systemfile_unlock(vcb, lockflags);
FREE (btIterator, M_TEMP);
return err;
}
static OSErr DeleteExtentRecord(
const ExtendedVCB *vcb,
u_int8_t forkType,
u_int32_t fileID,
u_int32_t startBlock)
{
struct BTreeIterator *btIterator = NULL;
OSErr err;
err = noErr;
MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
if (btIterator == NULL) {
return memFullErr; }
bzero(btIterator, sizeof(*btIterator));
if (vcb->vcbSigWord != kHFSSigWord) { HFSPlusExtentKey * keyPtr;
keyPtr = (HFSPlusExtentKey*) &btIterator->key;
keyPtr->keyLength = kHFSPlusExtentKeyMaximumLength;
keyPtr->forkType = forkType;
keyPtr->pad = 0;
keyPtr->fileID = fileID;
keyPtr->startBlock = startBlock;
}
#if CONFIG_HFS_STD
else {
HFSExtentKey * keyPtr;
keyPtr = (HFSExtentKey*) &btIterator->key;
keyPtr->keyLength = kHFSExtentKeyMaximumLength;
keyPtr->forkType = forkType;
keyPtr->fileID = fileID;
keyPtr->startBlock = startBlock;
}
#endif
err = BTDeleteRecord(GetFileControlBlock(vcb->extentsRefNum), btIterator);
(void) BTFlushPath(GetFileControlBlock(vcb->extentsRefNum));
FREE(btIterator, M_TEMP);
return err;
}
OSErr MapFileBlockC (
ExtendedVCB *vcb, FCB *fcb, size_t numberOfBytes, off_t offset, daddr64_t *startSector, size_t *availableBytes) {
OSErr err;
u_int32_t allocBlockSize; u_int32_t sectorSize;
HFSPlusExtentKey foundKey;
HFSPlusExtentRecord foundData;
u_int32_t foundIndex;
u_int32_t hint;
u_int32_t firstFABN; u_int32_t nextFABN; off_t dataEnd; u_int32_t sectorsPerBlock; u_int32_t startBlock; daddr64_t temp;
off_t tmpOff;
allocBlockSize = vcb->blockSize;
sectorSize = VCBTOHFS(vcb)->hfs_logical_block_size;
err = SearchExtentFile(vcb, fcb, offset, &foundKey, foundData, &foundIndex, &hint, &nextFABN);
if (err == noErr) {
startBlock = foundData[foundIndex].startBlock;
firstFABN = nextFABN - foundData[foundIndex].blockCount;
}
if (err != noErr)
{
return err;
}
dataEnd = (off_t)((off_t)(nextFABN) * (off_t)(allocBlockSize)); if (((off_t)fcb->ff_blocks * (off_t)allocBlockSize) < dataEnd) dataEnd = (off_t)fcb->ff_blocks * (off_t)allocBlockSize;
sectorsPerBlock = allocBlockSize / sectorSize;
temp = (daddr64_t)((offset - (off_t)((off_t)(firstFABN) * (off_t)(allocBlockSize)))/sectorSize);
temp += (daddr64_t)startBlock * (daddr64_t)sectorsPerBlock;
if (vcb->vcbSigWord == kHFSPlusSigWord)
temp += vcb->hfsPlusIOPosOffset / sectorSize;
else
temp += vcb->vcbAlBlSt;
*startSector = temp;
if (availableBytes)
{
tmpOff = dataEnd - offset;
if (tmpOff <= 0) {
return EINVAL;
}
if (tmpOff > (off_t)(numberOfBytes)) {
*availableBytes = numberOfBytes; }
else {
*availableBytes = tmpOff;
}
}
return noErr;
}
static OSErr ReleaseExtents(
ExtendedVCB *vcb,
const HFSPlusExtentRecord extentRecord,
u_int32_t *numReleasedAllocationBlocks,
Boolean *releasedLastExtent)
{
u_int32_t extentIndex;
u_int32_t numberOfExtents;
OSErr err = noErr;
*numReleasedAllocationBlocks = 0;
*releasedLastExtent = false;
if (vcb->vcbSigWord == kHFSPlusSigWord)
numberOfExtents = kHFSPlusExtentDensity;
else
numberOfExtents = kHFSExtentDensity;
for( extentIndex = 0; extentIndex < numberOfExtents; extentIndex++)
{
u_int32_t numAllocationBlocks;
numAllocationBlocks = extentRecord[extentIndex].blockCount;
if ( numAllocationBlocks == 0 )
{
*releasedLastExtent = true;
break;
}
err = BlockDeallocate( vcb, extentRecord[extentIndex].startBlock, numAllocationBlocks , 0);
if ( err != noErr )
break;
*numReleasedAllocationBlocks += numAllocationBlocks; }
return( err );
}
static OSErr TruncateExtents(
ExtendedVCB *vcb,
u_int8_t forkType,
u_int32_t fileID,
u_int32_t startBlock,
Boolean * recordDeleted)
{
OSErr err;
u_int32_t numberExtentsReleased;
Boolean releasedLastExtent;
u_int32_t hint;
HFSPlusExtentKey key;
HFSPlusExtentRecord extents;
int lockflags;
lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
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;
}
hfs_systemfile_unlock(vcb, lockflags);
return err;
}
static OSErr DeallocateFork(
ExtendedVCB *vcb,
HFSCatalogNodeID fileID,
u_int8_t forkType,
HFSPlusExtentRecord catalogExtents,
Boolean * recordDeleted)
{
OSErr err;
u_int32_t numReleasedAllocationBlocks;
Boolean releasedLastExtent;
err = ReleaseExtents( vcb, catalogExtents, &numReleasedAllocationBlocks, &releasedLastExtent );
if (err == noErr && !releasedLastExtent)
err = TruncateExtents(vcb, forkType, fileID, numReleasedAllocationBlocks, recordDeleted);
return( err );
}
OSErr FlushExtentFile( ExtendedVCB *vcb )
{
FCB * fcb;
OSErr err;
int lockflags;
fcb = GetFileControlBlock(vcb->extentsRefNum);
lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
err = BTFlushPath(fcb);
hfs_systemfile_unlock(vcb, lockflags);
if ( err == noErr )
{
if (FTOC(fcb)->c_flag & C_MODIFIED)
{
MarkVCBDirty( vcb );
}
}
return( err );
}
#if CONFIG_HFS_STD
__private_extern__
int32_t CompareExtentKeys( const HFSExtentKey *searchKey, const HFSExtentKey *trialKey )
{
int32_t result;
#if DEBUG_BUILD
if (searchKey->keyLength != kHFSExtentKeyMaximumLength)
DebugStr("HFS: search Key is wrong length");
if (trialKey->keyLength != kHFSExtentKeyMaximumLength)
DebugStr("HFS: trial Key is wrong length");
#endif
result = -1;
if (searchKey->fileID == trialKey->fileID) {
if (searchKey->forkType == trialKey->forkType) {
if (searchKey->startBlock == trialKey->startBlock) {
result = 0;
}
else {
if (searchKey->startBlock > trialKey->startBlock)
result = 1;
}
}
else {
if (searchKey->forkType > trialKey->forkType)
result = 1;
}
}
else {
if (searchKey->fileID > trialKey->fileID)
result = 1;
}
return( result );
}
#endif
__private_extern__
int32_t CompareExtentKeysPlus( const HFSPlusExtentKey *searchKey, const HFSPlusExtentKey *trialKey )
{
int32_t result;
#if DEBUG_BUILD
if (searchKey->keyLength != kHFSPlusExtentKeyMaximumLength)
DebugStr("HFS: search Key is wrong length");
if (trialKey->keyLength != kHFSPlusExtentKeyMaximumLength)
DebugStr("HFS: trial Key is wrong length");
#endif
result = -1;
if (searchKey->fileID == trialKey->fileID) {
if (searchKey->forkType == trialKey->forkType) {
if (searchKey->startBlock == trialKey->startBlock) {
result = 0;
}
else {
if (searchKey->startBlock > trialKey->startBlock)
result = 1;
}
}
else {
if (searchKey->forkType > trialKey->forkType)
result = 1;
}
}
else {
if (searchKey->fileID > trialKey->fileID)
result = 1;
}
return( result );
}
static int
should_pin_blocks(hfsmount_t *hfsmp, FCB *fcb)
{
if (!ISSET(hfsmp->hfs_flags, HFS_CS_HOTFILE_PIN)
|| fcb->ff_cp == NULL || fcb->ff_cp->c_vp == NULL) {
return 0;
}
int pin_blocks;
if (vnode_issystem(fcb->ff_cp->c_vp)) {
return 1;
}
if (fcb->ff_cp->c_attr.ca_recflags & kHFSAutoCandidateMask) {
return 0;
}
if ((fcb->ff_cp->c_attr.ca_recflags & (kHFSFastDevPinnedMask|kHFSFastDevCandidateMask)) != 0) {
pin_blocks = 1;
} else {
pin_blocks = 0;
}
return pin_blocks;
}
static void
pin_blocks_if_needed(ExtendedVCB *vcb, FCB *fcb, u_int32_t startBlock, u_int32_t blockCount)
{
if (!should_pin_blocks(vcb, fcb)) {
return;
}
if (hfs_pin_block_range((struct hfsmount *)vcb, HFS_PIN_IT, startBlock, blockCount, vfs_context_kernel()) == 0) {
struct vnode *vp = fcb->ff_cp->c_vp;
hfs_hotfile_adjust_blocks(vp, -blockCount);
}
}
int
AddFileExtent(ExtendedVCB *vcb, FCB *fcb, u_int32_t startBlock, u_int32_t blockCount)
{
HFSPlusExtentKey foundKey;
HFSPlusExtentRecord foundData;
u_int32_t foundIndex;
u_int32_t hint;
u_int32_t nextBlock;
int64_t peof;
int i;
int error;
peof = (int64_t)(fcb->ff_blocks + blockCount) * (int64_t)vcb->blockSize;
error = SearchExtentFile(vcb, fcb, peof-1, &foundKey, foundData, &foundIndex, &hint, &nextBlock);
if (error != fxRangeErr)
return (EBUSY);
if (foundData[foundIndex].blockCount != 0)
++foundIndex;
if (foundIndex == kHFSPlusExtentDensity) {
foundKey.keyLength = kHFSPlusExtentKeyMaximumLength;
foundKey.forkType = kDataForkType;
foundKey.pad = 0;
foundKey.fileID = FTOC(fcb)->c_fileid;
foundKey.startBlock = nextBlock;
foundData[0].startBlock = startBlock;
foundData[0].blockCount = blockCount;
for (i = 1; i < kHFSPlusExtentDensity; ++i) {
foundData[i].startBlock = 0;
foundData[i].blockCount = 0;
}
foundIndex = 0;
error = CreateExtentRecord(vcb, &foundKey, foundData, &hint);
if (error == fxOvFlErr) {
error = dskFulErr;
} else if (error == 0) {
pin_blocks_if_needed(vcb, fcb, startBlock, blockCount);
}
} else {
foundData[foundIndex].startBlock = startBlock;
foundData[foundIndex].blockCount = blockCount;
error = UpdateExtentRecord(vcb, fcb, 0, &foundKey, foundData, hint);
if (error == 0) {
pin_blocks_if_needed(vcb, fcb, startBlock, blockCount);
}
}
(void) FlushExtentFile(vcb);
return (error);
}
OSErr ExtendFileC (
ExtendedVCB *vcb, FCB *fcb, int64_t bytesToAdd, u_int32_t blockHint, u_int32_t flags, int64_t *actualBytesAdded) {
OSErr err;
u_int32_t volumeBlockSize;
int64_t blocksToAdd;
int64_t bytesThisExtent;
HFSPlusExtentKey foundKey;
HFSPlusExtentRecord foundData;
u_int32_t foundIndex;
u_int32_t hint;
u_int32_t nextBlock;
u_int32_t startBlock;
Boolean allOrNothing;
Boolean forceContig;
Boolean wantContig;
Boolean useMetaZone;
Boolean needsFlush;
int allowFlushTxns;
u_int32_t actualStartBlock;
u_int32_t actualNumBlocks;
u_int32_t numExtentsPerRecord;
int64_t maximumBytes;
int64_t availbytes;
int64_t peof;
u_int32_t prevblocks;
uint32_t fastdev = 0;
struct hfsmount *hfsmp = (struct hfsmount*)vcb;
allowFlushTxns = 0;
needsFlush = false;
*actualBytesAdded = 0;
volumeBlockSize = vcb->blockSize;
allOrNothing = ((flags & kEFAllMask) != 0);
forceContig = ((flags & kEFContigMask) != 0);
prevblocks = fcb->ff_blocks;
if (vcb->vcbSigWord != kHFSSigWord) {
numExtentsPerRecord = kHFSPlusExtentDensity;
}
#if CONFIG_HFS_STD
else {
numExtentsPerRecord = kHFSExtentDensity;
if (bytesToAdd >= kTwoGigabytes)
goto HFS_Std_Overflow;
if ((((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd) >= kTwoGigabytes)
goto HFS_Std_Overflow;
}
#endif
blocksToAdd = howmany(bytesToAdd, volumeBlockSize);
bytesToAdd = (int64_t)((int64_t)blocksToAdd * (int64_t)volumeBlockSize);
if ((flags & kEFDeferMask)
&& (vcb->vcbSigWord == kHFSPlusSigWord)
&& (bytesToAdd < (int64_t)HFS_MAX_DEFERED_ALLOC)
&& (blocksToAdd < hfs_freeblks(VCBTOHFS(vcb), 1))) {
hfs_lock_mount (hfsmp);
vcb->loanedBlocks += blocksToAdd;
hfs_unlock_mount(hfsmp);
fcb->ff_unallocblocks += blocksToAdd;
FTOC(fcb)->c_blocks += blocksToAdd;
fcb->ff_blocks += blocksToAdd;
FTOC(fcb)->c_flag |= C_MINOR_MOD;
*actualBytesAdded = bytesToAdd;
return (0);
}
if (fcb->ff_unallocblocks > 0) {
u_int32_t loanedBlocks;
loanedBlocks = fcb->ff_unallocblocks;
blocksToAdd += loanedBlocks;
bytesToAdd = (int64_t)blocksToAdd * (int64_t)volumeBlockSize;
FTOC(fcb)->c_blocks -= loanedBlocks;
fcb->ff_blocks -= loanedBlocks;
fcb->ff_unallocblocks = 0;
hfs_lock_mount(hfsmp);
vcb->loanedBlocks -= loanedBlocks;
hfs_unlock_mount(hfsmp);
}
if ((vcb->vcbClpSiz > (int32_t)volumeBlockSize)
&& (bytesToAdd < (int64_t)HFS_MAX_DEFERED_ALLOC)
&& (flags & kEFNoClumpMask) == 0) {
maximumBytes = (int64_t)howmany(bytesToAdd, vcb->vcbClpSiz);
maximumBytes *= vcb->vcbClpSiz;
} else {
maximumBytes = bytesToAdd;
}
#if CONFIG_HFS_STD
if ( (vcb->vcbSigWord == kHFSSigWord) && ((((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd) >= kTwoGigabytes) ) {
if (allOrNothing) goto HFS_Std_Overflow; else {
--blocksToAdd; bytesToAdd -= volumeBlockSize;
}
}
#endif
if (allOrNothing &&
(blocksToAdd > hfs_freeblks(VCBTOHFS(vcb), flags & kEFReserveMask))) {
err = dskFulErr;
goto ErrorExit;
}
peof = ((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd; err = SearchExtentFile(vcb, fcb, peof-1, &foundKey, foundData, &foundIndex, &hint, &nextBlock);
if (err == noErr) {
fcb->ff_blocks = peof / volumeBlockSize;
FTOC(fcb)->c_blocks += (bytesToAdd / volumeBlockSize);
FTOC(fcb)->c_flag |= C_MODIFIED;
goto Exit;
}
if (err != fxRangeErr) goto ErrorExit;
peof = (int64_t)((int64_t)nextBlock * (int64_t)volumeBlockSize); bytesThisExtent = (int64_t)(nextBlock - fcb->ff_blocks) * (int64_t)volumeBlockSize;
if (bytesThisExtent != 0) {
fcb->ff_blocks = nextBlock;
FTOC(fcb)->c_blocks += (bytesThisExtent / volumeBlockSize);
FTOC(fcb)->c_flag |= C_MODIFIED;
bytesToAdd -= bytesThisExtent;
}
err = noErr;
if ( (vcb->hfs_flags & HFS_HAS_SPARSE_DEVICE)
&& (fcb->ff_cp->c_fileid >= kHFSFirstUserCatalogNodeID)
&& (flags & kEFMetadataMask) == 0) {
wantContig = false;
if (hfs_isrbtree_active(VCBTOHFS(vcb))) {
if (forceContig) {
wantContig = true;
}
}
else {
if ((vcb->hfs_flags & HFS_DID_CONTIG_SCAN) == 0) {
vcb->hfs_flags |= HFS_DID_CONTIG_SCAN;
wantContig = true;
}
}
}
else {
wantContig = true;
}
if (should_pin_blocks(hfsmp, fcb))
fastdev = HFS_ALLOC_FAST_DEV;
useMetaZone = flags & kEFMetadataMask;
do {
if (blockHint != 0)
startBlock = blockHint;
else
startBlock = foundData[foundIndex].startBlock + foundData[foundIndex].blockCount;
actualNumBlocks = 0;
actualStartBlock = 0;
availbytes = (int64_t)hfs_freeblks(VCBTOHFS(vcb), flags & kEFReserveMask) *
(int64_t)volumeBlockSize;
if (availbytes <= 0) {
err = dskFulErr;
} else {
if (wantContig && (availbytes < bytesToAdd)) {
err = dskFulErr;
}
else {
uint32_t ba_flags = fastdev;
if (wantContig) {
ba_flags |= HFS_ALLOC_FORCECONTIG;
}
if (useMetaZone) {
ba_flags |= HFS_ALLOC_METAZONE;
}
if (allowFlushTxns) {
ba_flags |= HFS_ALLOC_FLUSHTXN;
}
err = BlockAllocate(
vcb,
startBlock,
howmany(MIN(bytesToAdd, availbytes), volumeBlockSize),
howmany(MIN(maximumBytes, availbytes), volumeBlockSize),
ba_flags,
&actualStartBlock,
&actualNumBlocks);
}
}
if (err == dskFulErr) {
if (forceContig) {
if (allowFlushTxns == 0) {
allowFlushTxns = 1;
wantContig = 1;
err = noErr;
continue;
}
else {
break; }
}
if (wantContig) {
err = noErr;
wantContig = false;
continue;
}
if (actualNumBlocks != 0)
err = noErr;
if (useMetaZone == 0) {
err = noErr;
useMetaZone = 1;
continue;
}
if (allowFlushTxns == 0) {
allowFlushTxns = 1;
err = noErr;
continue;
}
}
if (err == noErr) {
if ((actualStartBlock == startBlock) && (blockHint == 0)) {
foundData[foundIndex].blockCount += actualNumBlocks;
err = UpdateExtentRecord(vcb, fcb, 0, &foundKey, foundData, hint);
if (err != noErr) break;
}
else {
u_int16_t i;
if (foundData[foundIndex].blockCount != 0) ++foundIndex; if (foundIndex == numExtentsPerRecord) {
if (FTOC(fcb)->c_fileid == kHFSExtentsFileID) {
(void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks, 0);
err = dskFulErr; break;
}
foundKey.keyLength = kHFSPlusExtentKeyMaximumLength;
if (FORK_IS_RSRC(fcb))
foundKey.forkType = kResourceForkType;
else
foundKey.forkType = kDataForkType;
foundKey.pad = 0;
foundKey.fileID = FTOC(fcb)->c_fileid;
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, 0);
err = dskFulErr;
}
if (err != noErr) break;
needsFlush = true; }
else {
foundData[foundIndex].startBlock = actualStartBlock;
foundData[foundIndex].blockCount = actualNumBlocks;
err = UpdateExtentRecord(vcb, fcb, 0, &foundKey, foundData, hint);
if (err != noErr) break;
}
}
nextBlock += actualNumBlocks;
bytesThisExtent = (int64_t)((int64_t)actualNumBlocks * (int64_t)volumeBlockSize);
if (bytesThisExtent > bytesToAdd) {
bytesToAdd = 0;
}
else {
bytesToAdd -= bytesThisExtent;
maximumBytes -= bytesThisExtent;
}
fcb->ff_blocks += (bytesThisExtent / volumeBlockSize);
FTOC(fcb)->c_blocks += (bytesThisExtent / volumeBlockSize);
FTOC(fcb)->c_flag |= C_MODIFIED;
if (forceContig) {
if (bytesToAdd != 0)
err = dskFulErr;
break; }
}
} while (err == noErr && bytesToAdd);
ErrorExit:
Exit:
if (VCBTOHFS(vcb)->hfs_flags & HFS_METADATA_ZONE) {
if (vcb->nextAllocation >= VCBTOHFS(vcb)->hfs_metazone_start &&
vcb->nextAllocation <= VCBTOHFS(vcb)->hfs_metazone_end) {
hfs_lock_mount (hfsmp);
HFS_UPDATE_NEXT_ALLOCATION(vcb, VCBTOHFS(vcb)->hfs_metazone_end + 1);
MarkVCBDirty(vcb);
hfs_unlock_mount(hfsmp);
}
}
if (prevblocks < fcb->ff_blocks) {
*actualBytesAdded = (int64_t)(fcb->ff_blocks - prevblocks) * (int64_t)volumeBlockSize;
} else {
*actualBytesAdded = 0;
}
if (fastdev) {
hfs_hotfile_adjust_blocks(fcb->ff_cp->c_vp,
(int64_t)prevblocks - fcb->ff_blocks);
}
if (needsFlush)
(void) FlushExtentFile(vcb);
return err;
#if CONFIG_HFS_STD
HFS_Std_Overflow:
err = fileBoundsErr;
goto ErrorExit;
#endif
}
OSErr TruncateFileC (
ExtendedVCB *vcb, FCB *fcb, int64_t peof, int deleted, int rsrc, uint32_t fileid, Boolean truncateToExtent)
{
OSErr err;
u_int32_t nextBlock; u_int32_t startBlock; u_int32_t physNumBlocks; u_int32_t numBlocks;
HFSPlusExtentKey key; u_int32_t hint; HFSPlusExtentRecord extentRecord;
u_int32_t extentIndex;
u_int32_t extentNextBlock;
u_int32_t numExtentsPerRecord;
int64_t temp64;
u_int8_t forkType;
Boolean extentChanged; Boolean recordDeleted;
recordDeleted = false;
if (vcb->vcbSigWord == kHFSPlusSigWord) {
numExtentsPerRecord = kHFSPlusExtentDensity;
}
else {
numExtentsPerRecord = kHFSExtentDensity;
}
if (rsrc) {
forkType = kResourceForkType;
}
else {
forkType = kDataForkType;
}
temp64 = fcb->ff_blocks;
physNumBlocks = (u_int32_t)temp64;
nextBlock = howmany(peof, vcb->blockSize); peof = (int64_t)((int64_t)nextBlock * (int64_t)vcb->blockSize);
#if CONFIG_HFS_STD
if ((vcb->vcbSigWord == kHFSSigWord) && (peof >= kTwoGigabytes)) {
#if DEBUG_BUILD
DebugStr("HFS: Trying to truncate a file to 2GB or more");
#endif
err = fileBoundsErr;
goto ErrorExit;
}
#endif
numBlocks = peof / vcb->blockSize;
if (!deleted) {
FTOC(fcb)->c_blocks -= (fcb->ff_blocks - numBlocks);
}
fcb->ff_blocks = numBlocks;
if (!deleted) {
FTOC(fcb)->c_flag |= C_MODIFIED;
}
if (peof == 0) {
int i;
err = DeallocateFork(vcb, fileid, forkType, fcb->fcbExtents, &recordDeleted);
if (err != noErr) goto ErrorExit;
if (err == noErr) {
for (i=0; i < kHFSPlusExtentDensity; i++) {
fcb->fcbExtents[i].startBlock = 0;
fcb->fcbExtents[i].blockCount = 0;
}
}
goto Done;
}
err = SearchExtentFile(vcb, fcb, peof-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, 0);
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, 0);
if (err != noErr) goto ErrorExit;
nextBlock += numBlocks;
extentRecord[extentIndex].startBlock = 0;
extentRecord[extentIndex].blockCount = 0;
extentChanged = true;
++extentIndex;
}
if (extentChanged) {
err = UpdateExtentRecord(vcb, fcb, deleted, &key, extentRecord, hint);
if (err != noErr) goto ErrorExit;
}
if (nextBlock < physNumBlocks)
err = TruncateExtents(vcb, forkType, fileid, nextBlock, &recordDeleted);
Done:
ErrorExit:
if (recordDeleted)
(void) FlushExtentFile(vcb);
return err;
}
OSErr HeadTruncateFile (
ExtendedVCB *vcb,
FCB *fcb,
u_int32_t headblks)
{
HFSPlusExtentRecord extents;
HFSPlusExtentRecord tailExtents;
HFSCatalogNodeID fileID;
u_int8_t forkType;
u_int32_t blkcnt;
u_int32_t startblk;
u_int32_t blksfreed;
int i, j;
int error = 0;
int lockflags;
if (vcb->vcbSigWord != kHFSPlusSigWord)
return (-1);
forkType = FORK_IS_RSRC(fcb) ? kResourceForkType : kDataForkType;
fileID = FTOC(fcb)->c_fileid;
bzero(tailExtents, sizeof(tailExtents));
blksfreed = 0;
startblk = 0;
for (i = 0, j = 0; i < kHFSPlusExtentDensity; ++i) {
blkcnt = fcb->fcbExtents[i].blockCount;
if (blkcnt == 0)
break;
if (blksfreed < headblks) {
error = BlockDeallocate(vcb, fcb->fcbExtents[i].startBlock, blkcnt, 0);
if (error ) {
if (i == 0)
goto ErrorExit;
else {
error = 0;
printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
FTOC(fcb)->c_desc.cd_nameptr ? (const char *)FTOC(fcb)->c_desc.cd_nameptr : "", error);
}
}
blksfreed += blkcnt;
fcb->fcbExtents[i].startBlock = 0;
fcb->fcbExtents[i].blockCount = 0;
} else {
tailExtents[j].startBlock = fcb->fcbExtents[i].startBlock;
tailExtents[j].blockCount = blkcnt;
++j;
}
startblk += blkcnt;
}
if (blkcnt == 0)
goto CopyExtents;
lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
for (;;) {
u_int32_t extblks;
error = FindExtentRecord(vcb, forkType, fileID, startblk, false, NULL, extents, NULL);
if (error) {
if (error != btNotFound)
printf("hfs: HeadTruncateFile: problems finding extents %s (%d)\n",
FTOC(fcb)->c_desc.cd_nameptr ? (const char *)FTOC(fcb)->c_desc.cd_nameptr : "", error);
error = 0;
break;
}
for(i = 0, extblks = 0; i < kHFSPlusExtentDensity; ++i) {
blkcnt = extents[i].blockCount;
if (blkcnt == 0)
break;
if (blksfreed < headblks) {
error = BlockDeallocate(vcb, extents[i].startBlock, blkcnt, 0);
if (error) {
printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
FTOC(fcb)->c_desc.cd_nameptr ? (const char *)FTOC(fcb)->c_desc.cd_nameptr : "", error);
error = 0;
}
blksfreed += blkcnt;
} else {
tailExtents[j].startBlock = extents[i].startBlock;
tailExtents[j].blockCount = blkcnt;
++j;
}
extblks += blkcnt;
}
error = DeleteExtentRecord(vcb, forkType, fileID, startblk);
if (error) {
printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n",
FTOC(fcb)->c_desc.cd_nameptr ? (const char *)FTOC(fcb)->c_desc.cd_nameptr : "", error);
error = 0;
}
if (blkcnt == 0)
break;
startblk += extblks;
}
hfs_systemfile_unlock(vcb, lockflags);
CopyExtents:
if (blksfreed) {
bcopy(tailExtents, fcb->fcbExtents, sizeof(tailExtents));
blkcnt = fcb->ff_blocks - headblks;
FTOC(fcb)->c_blocks -= headblks;
fcb->ff_blocks = blkcnt;
FTOC(fcb)->c_flag |= C_MODIFIED;
FTOC(fcb)->c_touch_chgtime = TRUE;
(void) FlushExtentFile(vcb);
}
ErrorExit:
return MacToVFSError(error);
}
static OSErr SearchExtentRecord(
ExtendedVCB *vcb,
u_int32_t searchFABN,
const HFSPlusExtentRecord extentData,
u_int32_t extentDataStartFABN,
u_int32_t *foundExtentIndex,
u_int32_t *endingFABNPlusOne,
Boolean *noMoreExtents)
{
OSErr err = noErr;
u_int32_t extentIndex;
u_int32_t numberOfExtents = kHFSExtentDensity;
u_int32_t numAllocationBlocks;
Boolean foundExtent;
*endingFABNPlusOne = extentDataStartFABN;
*noMoreExtents = false;
foundExtent = false;
if (vcb->vcbSigWord != kHFSSigWord) {
numberOfExtents = kHFSPlusExtentDensity;
}
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 );
}
OSErr SearchExtentFile(
ExtendedVCB *vcb,
const FCB *fcb,
int64_t filePosition,
HFSPlusExtentKey *foundExtentKey,
HFSPlusExtentRecord foundExtentData,
u_int32_t *foundExtentIndex,
u_int32_t *extentBTreeHint,
u_int32_t *endingFABNPlusOne )
{
OSErr err;
u_int32_t filePositionBlock;
int64_t temp64;
Boolean noMoreExtents;
int lockflags;
temp64 = filePosition / (int64_t)vcb->blockSize;
filePositionBlock = (u_int32_t)temp64;
bcopy ( fcb->fcbExtents, foundExtentData, sizeof(HFSPlusExtentRecord));
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;
}
lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
err = FindExtentRecord(vcb, FORK_IS_RSRC(fcb) ? kResourceForkType : kDataForkType,
FTOC(fcb)->c_fileid, filePositionBlock, true, foundExtentKey, foundExtentData, extentBTreeHint);
hfs_systemfile_unlock(vcb, lockflags);
if (err == btNotFound) {
*extentBTreeHint = kNoHint;
foundExtentKey->keyLength = 0;
err = GetFCBExtentRecord(fcb, foundExtentData);
return fxRangeErr;
}
if (err == noErr) {
err = SearchExtentRecord(vcb, filePositionBlock, foundExtentData, foundExtentKey->startBlock,
foundExtentIndex, endingFABNPlusOne, &noMoreExtents);
}
Exit:
return err;
}
static OSErr UpdateExtentRecord (ExtendedVCB *vcb, FCB *fcb, int deleted,
const HFSPlusExtentKey *extentFileKey,
const HFSPlusExtentRecord extentData,
u_int32_t extentBTreeHint)
{
OSErr err = noErr;
if (extentFileKey->keyLength == 0) { BlockMoveData(extentData, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));
if (!deleted) {
FTOC(fcb)->c_flag |= C_MODIFIED;
}
}
else {
struct BTreeIterator *btIterator = NULL;
FSBufferDescriptor btRecord;
u_int16_t btRecordSize;
FCB * btFCB;
int lockflags;
btFCB = GetFileControlBlock(vcb->extentsRefNum);
MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
if (btIterator == NULL) {
return memFullErr; }
bzero(btIterator, sizeof(*btIterator));
lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
if (vcb->vcbSigWord != kHFSSigWord) { HFSPlusExtentRecord foundData;
BlockMoveData(extentFileKey, &btIterator->key, sizeof(HFSPlusExtentKey));
btIterator->hint.index = 0;
btIterator->hint.nodeNum = extentBTreeHint;
btRecord.bufferAddress = &foundData;
btRecord.itemSize = sizeof(HFSPlusExtentRecord);
btRecord.itemCount = 1;
err = BTSearchRecord(btFCB, btIterator, &btRecord, &btRecordSize, btIterator);
if (err == noErr) {
BlockMoveData(extentData, &foundData, sizeof(HFSPlusExtentRecord));
err = BTReplaceRecord(btFCB, btIterator, &btRecord, btRecordSize);
}
(void) BTFlushPath(btFCB);
}
#if CONFIG_HFS_STD
else {
HFSExtentKey * key; HFSExtentRecord foundData;
key = (HFSExtentKey*) &btIterator->key;
key->keyLength = kHFSExtentKeyMaximumLength;
key->forkType = extentFileKey->forkType;
key->fileID = extentFileKey->fileID;
key->startBlock = extentFileKey->startBlock;
btIterator->hint.index = 0;
btIterator->hint.nodeNum = extentBTreeHint;
btRecord.bufferAddress = &foundData;
btRecord.itemSize = sizeof(HFSExtentRecord);
btRecord.itemCount = 1;
err = BTSearchRecord(btFCB, btIterator, &btRecord, &btRecordSize, btIterator);
if (err == noErr)
err = HFSPlusToHFSExtents(extentData, (HFSExtentDescriptor *)&foundData);
if (err == noErr)
err = BTReplaceRecord(btFCB, btIterator, &btRecord, btRecordSize);
(void) BTFlushPath(btFCB);
}
#endif
hfs_systemfile_unlock(vcb, lockflags);
FREE(btIterator, M_TEMP);
}
return err;
}
#if CONFIG_HFS_STD
static OSErr HFSPlusToHFSExtents(
const 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("ExtentRecord with > 3 extents is invalid for HFS");
err = fsDSIntErr;
}
#endif
return err;
}
#endif
static OSErr GetFCBExtentRecord(
const FCB *fcb,
HFSPlusExtentRecord extents)
{
BlockMoveData(fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord));
return noErr;
}
static Boolean ExtentsAreIntegral(
const HFSPlusExtentRecord extentRecord,
u_int32_t mask,
u_int32_t *blocksChecked,
Boolean *checkedLastExtent)
{
u_int32_t blocks;
u_int32_t 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(
ExtendedVCB *vcb,
FCB *fcb,
u_int32_t nodeSize)
{
u_int32_t mask;
u_int32_t startBlock;
u_int32_t blocksChecked;
u_int32_t hint;
HFSPlusExtentKey key;
HFSPlusExtentRecord extents;
OSErr result;
Boolean lastExtentReached;
int lockflags;
if (vcb->blockSize >= nodeSize)
return TRUE;
mask = (nodeSize / vcb->blockSize) - 1;
(void) GetFCBExtentRecord(fcb, extents);
if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
return FALSE;
if ( lastExtentReached ||
(int64_t)((int64_t)blocksChecked * (int64_t)vcb->blockSize) >= (int64_t)fcb->ff_size)
return TRUE;
startBlock = blocksChecked;
lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
while ( !lastExtentReached )
{
result = FindExtentRecord(vcb, kDataForkType, fcb->ff_cp->c_fileid, startBlock, FALSE, &key, extents, &hint);
if (result) break;
if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) ) {
hfs_systemfile_unlock(vcb, lockflags);
return FALSE;
}
startBlock += blocksChecked;
}
hfs_systemfile_unlock(vcb, lockflags);
return TRUE;
}