VolumeAllocation.c [plain text]
#include "../../hfs_macos_defs.h"
#include <sys/types.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include "../../hfs.h"
#include "../../hfs_dbg.h"
#include "../../hfs_format.h"
#include "../../hfs_endian.h"
#include "../headers/FileMgrInternal.h"
enum {
kBytesPerWord = 4,
kBitsPerByte = 8,
kBitsPerWord = 32,
kBitsWithinWordMask = kBitsPerWord-1
};
#define kLowBitInWordMask 0x00000001ul
#define kHighBitInWordMask 0x80000000ul
#define kAllBitsSetInWord 0xFFFFFFFFul
static OSErr ReadBitmapBlock(
ExtendedVCB *vcb,
UInt32 bit,
UInt32 **buffer,
UInt32 *blockRef);
static OSErr ReleaseBitmapBlock(
ExtendedVCB *vcb,
UInt32 blockRef,
Boolean dirty);
static OSErr BlockAllocateAny(
ExtendedVCB *vcb,
UInt32 startingBlock,
UInt32 endingBlock,
UInt32 maxBlocks,
Boolean useMetaZone,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks);
static OSErr BlockAllocateContig(
ExtendedVCB *vcb,
UInt32 startingBlock,
UInt32 minBlocks,
UInt32 maxBlocks,
Boolean useMetaZone,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks);
static OSErr BlockFindContiguous(
ExtendedVCB *vcb,
UInt32 startingBlock,
UInt32 endingBlock,
UInt32 minBlocks,
UInt32 maxBlocks,
Boolean useMetaZone,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks);
static OSErr BlockAllocateKnown(
ExtendedVCB *vcb,
UInt32 maxBlocks,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks);
__private_extern__
OSErr BlockAllocate (
ExtendedVCB *vcb,
UInt32 startingBlock,
UInt32 minBlocks,
UInt32 maxBlocks,
Boolean forceContiguous,
Boolean useMetaZone,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks)
{
UInt32 freeBlocks;
OSErr err;
Boolean updateAllocPtr = false;
*actualStartBlock = 0;
*actualNumBlocks = 0;
freeBlocks = hfs_freeblks(VCBTOHFS(vcb), 0);
if (freeBlocks == 0) {
err = dskFulErr;
goto Exit;
}
if (forceContiguous && freeBlocks < minBlocks) {
err = dskFulErr;
goto Exit;
}
if (minBlocks > freeBlocks) {
minBlocks = freeBlocks;
}
if (maxBlocks > freeBlocks) {
maxBlocks = freeBlocks;
}
if (startingBlock == 0) {
VCB_LOCK(vcb);
startingBlock = vcb->nextAllocation;
VCB_UNLOCK(vcb);
updateAllocPtr = true;
}
if (startingBlock >= vcb->totalBlocks) {
startingBlock = 0;
}
if (forceContiguous) {
err = BlockAllocateContig(vcb, startingBlock, minBlocks, maxBlocks,
useMetaZone, actualStartBlock, actualNumBlocks);
if ((err == noErr) &&
(*actualStartBlock > startingBlock) &&
((*actualStartBlock < VCBTOHFS(vcb)->hfs_metazone_start) ||
(*actualStartBlock > VCBTOHFS(vcb)->hfs_metazone_end))) {
vcb->nextAllocation = *actualStartBlock;
}
} else {
err = BlockAllocateKnown(vcb, maxBlocks, actualStartBlock, actualNumBlocks);
if (err == dskFulErr)
err = BlockAllocateAny(vcb, startingBlock, vcb->totalBlocks,
maxBlocks, useMetaZone, actualStartBlock,
actualNumBlocks);
if (err == dskFulErr)
err = BlockAllocateAny(vcb, 1, startingBlock, maxBlocks,
useMetaZone, actualStartBlock,
actualNumBlocks);
}
if (err == noErr) {
VCB_LOCK(vcb);
if (updateAllocPtr &&
((*actualStartBlock < VCBTOHFS(vcb)->hfs_metazone_start) ||
(*actualStartBlock > VCBTOHFS(vcb)->hfs_metazone_end))) {
vcb->nextAllocation = *actualStartBlock;
}
vcb->freeBlocks -= *actualNumBlocks;
hfs_generate_volume_notifications(VCBTOHFS(vcb));
VCB_UNLOCK(vcb);
MarkVCBDirty(vcb);
}
Exit:
return err;
}
__private_extern__
OSErr BlockDeallocate (
ExtendedVCB *vcb, UInt32 firstBlock, UInt32 numBlocks) {
OSErr err;
if (numBlocks == 0) {
err = noErr;
goto Exit;
}
err = BlockMarkFree(vcb, firstBlock, numBlocks);
if (err)
goto Exit;
VCB_LOCK(vcb);
vcb->freeBlocks += numBlocks;
hfs_generate_volume_notifications(VCBTOHFS(vcb));
if (vcb->nextAllocation == (firstBlock + numBlocks))
vcb->nextAllocation -= numBlocks;
VCB_UNLOCK(vcb);
MarkVCBDirty(vcb);
Exit:
return err;
}
UInt8 freebitcount[16] = {
4, 3, 3, 2, 3, 2, 2, 1,
3, 2, 2, 1, 2, 1, 1, 0,
};
__private_extern__
UInt32
MetaZoneFreeBlocks(ExtendedVCB *vcb)
{
UInt32 freeblocks;
UInt32 *currCache;
UInt32 blockRef;
UInt32 bit;
UInt32 lastbit;
int bytesleft;
int bytesperblock;
UInt8 byte;
UInt8 *buffer;
blockRef = 0;
bytesleft = freeblocks = 0;
bit = VCBTOHFS(vcb)->hfs_metazone_start;
if (bit == 1)
bit = 0;
lastbit = VCBTOHFS(vcb)->hfs_metazone_end;
bytesperblock = vcb->vcbVBMIOSize;
while (bit < lastbit) {
if (bytesleft == 0) {
if (blockRef) {
(void) ReleaseBitmapBlock(vcb, blockRef, false);
blockRef = 0;
}
if (ReadBitmapBlock(vcb, bit, &currCache, &blockRef) != 0) {
return (0);
}
buffer = (UInt8 *)currCache;
bytesleft = bytesperblock;
}
byte = *buffer++;
freeblocks += freebitcount[byte & 0x0F];
freeblocks += freebitcount[(byte >> 4) & 0x0F];
bit += kBitsPerByte;
--bytesleft;
}
if (blockRef)
(void) ReleaseBitmapBlock(vcb, blockRef, false);
return (freeblocks);
}
static UInt32 NextBitmapBlock(
ExtendedVCB *vcb,
UInt32 bit)
{
struct hfsmount *hfsmp = VCBTOHFS(vcb);
if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) == 0)
return (bit);
if ((bit >= hfsmp->hfs_metazone_start) &&
(bit <= hfsmp->hfs_metazone_end)) {
bit = hfsmp->hfs_metazone_end + 1;
}
return (bit);
}
static OSErr ReadBitmapBlock(
ExtendedVCB *vcb,
UInt32 bit,
UInt32 **buffer,
UInt32 *blockRef)
{
OSErr err;
struct buf *bp = NULL;
struct vnode *vp = NULL;
UInt32 block;
UInt32 blockSize;
REQUIRE_FILE_LOCK(vcb->extentsRefNum, false);
blockSize = (UInt32)vcb->vcbVBMIOSize;
block = bit / (blockSize * kBitsPerByte);
if (vcb->vcbSigWord == kHFSPlusSigWord) {
vp = vcb->allocationsRefNum;
} else {
vp = VCBTOHFS(vcb)->hfs_devvp;
block += vcb->vcbVBMSt;
}
err = meta_bread(vp, block, blockSize, NOCRED, &bp);
if (bp) {
if (err) {
brelse(bp);
*blockRef = NULL;
*buffer = NULL;
} else {
*blockRef = (UInt32)bp;
*buffer = (UInt32 *)bp->b_data;
}
}
return err;
}
static OSErr ReleaseBitmapBlock(
ExtendedVCB *vcb,
UInt32 blockRef,
Boolean dirty)
{
struct buf *bp = (struct buf *)blockRef;
if (blockRef == 0) {
if (dirty)
panic("ReleaseBitmapBlock: missing bp");
return (0);
}
if (bp) {
if (dirty) {
struct hfsmount *hfsmp = VCBTOHFS(vcb);
if (hfsmp->jnl) {
journal_modify_block_end(hfsmp->jnl, bp);
} else {
bdwrite(bp);
}
} else {
brelse(bp);
}
}
return (0);
}
static OSErr BlockAllocateContig(
ExtendedVCB *vcb,
UInt32 startingBlock,
UInt32 minBlocks,
UInt32 maxBlocks,
Boolean useMetaZone,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks)
{
OSErr err;
err = BlockFindContiguous(vcb, startingBlock, vcb->totalBlocks, minBlocks,
maxBlocks, useMetaZone, actualStartBlock, actualNumBlocks);
if (err == dskFulErr && startingBlock != 0) {
err = BlockFindContiguous(vcb, 1, startingBlock, minBlocks, maxBlocks,
useMetaZone, actualStartBlock, actualNumBlocks);
}
if (err != noErr) goto Exit;
if ((*actualStartBlock + *actualNumBlocks) > vcb->totalBlocks)
panic("BlockAllocateContig: allocation overflow on \"%s\"", vcb->vcbVN);
err = BlockMarkAllocated(vcb, *actualStartBlock, *actualNumBlocks);
Exit:
if (err != noErr) {
*actualStartBlock = 0;
*actualNumBlocks = 0;
}
return err;
}
static OSErr BlockAllocateAny(
ExtendedVCB *vcb,
UInt32 startingBlock,
register UInt32 endingBlock,
UInt32 maxBlocks,
Boolean useMetaZone,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks)
{
OSErr err;
register UInt32 block; register UInt32 currentWord; register UInt32 bitMask; register UInt32 wordsLeft; UInt32 *buffer = NULL;
UInt32 *currCache = NULL;
UInt32 blockRef;
UInt32 bitsPerBlock;
UInt32 wordsPerBlock;
Boolean dirty = false;
struct hfsmount *hfsmp = VCBTOHFS(vcb);
if (maxBlocks > (endingBlock - startingBlock)) {
maxBlocks = endingBlock - startingBlock;
}
if (!useMetaZone)
startingBlock = NextBitmapBlock(vcb, startingBlock);
err = ReadBitmapBlock(vcb, startingBlock, &currCache, &blockRef);
if (err != noErr) goto Exit;
buffer = currCache;
{
UInt32 wordIndexInBlock;
bitsPerBlock = vcb->vcbVBMIOSize * kBitsPerByte;
wordsPerBlock = vcb->vcbVBMIOSize / kBytesPerWord;
wordIndexInBlock = (startingBlock & (bitsPerBlock-1)) / kBitsPerWord;
buffer += wordIndexInBlock;
wordsLeft = wordsPerBlock - wordIndexInBlock;
currentWord = SWAP_BE32 (*buffer);
bitMask = kHighBitInWordMask >> (startingBlock & kBitsWithinWordMask);
}
block=startingBlock;
while (block < endingBlock) {
if ((currentWord & bitMask) == 0)
break;
++block;
bitMask >>= 1;
if (bitMask == 0) {
bitMask = kHighBitInWordMask;
++buffer;
if (--wordsLeft == 0) {
buffer = currCache = NULL;
err = ReleaseBitmapBlock(vcb, blockRef, false);
if (err != noErr) goto Exit;
if (!useMetaZone) {
block = NextBitmapBlock(vcb, block);
if (block >= endingBlock) {
err = dskFulErr;
goto Exit;
}
}
err = ReadBitmapBlock(vcb, block, &currCache, &blockRef);
if (err != noErr) goto Exit;
buffer = currCache;
wordsLeft = wordsPerBlock;
}
currentWord = SWAP_BE32 (*buffer);
}
}
if (block >= endingBlock) {
err = dskFulErr;
goto Exit;
}
*actualStartBlock = block;
dirty = true;
if (block < (endingBlock-maxBlocks)) {
endingBlock = block + maxBlocks; }
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, (struct buf *)blockRef);
}
while ((currentWord & bitMask) == 0) {
currentWord |= bitMask;
++block;
if (block == endingBlock)
break;
bitMask >>= 1;
if (bitMask == 0) {
*buffer = SWAP_BE32 (currentWord);
bitMask = kHighBitInWordMask;
++buffer;
if (--wordsLeft == 0) {
buffer = currCache = NULL;
err = ReleaseBitmapBlock(vcb, blockRef, true);
if (err != noErr) goto Exit;
if (!useMetaZone) {
UInt32 nextBlock;
nextBlock = NextBitmapBlock(vcb, block);
if (nextBlock != block) {
goto Exit;
}
}
err = ReadBitmapBlock(vcb, block, &currCache, &blockRef);
if (err != noErr) goto Exit;
buffer = currCache;
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, (struct buf *)blockRef);
}
wordsLeft = wordsPerBlock;
}
currentWord = SWAP_BE32 (*buffer);
}
}
*buffer = SWAP_BE32 (currentWord);
Exit:
if (err == noErr) {
*actualNumBlocks = block - *actualStartBlock;
if ((*actualStartBlock + *actualNumBlocks) > vcb->totalBlocks)
panic("BlockAllocateAny: allocation overflow on \"%s\"", vcb->vcbVN);
}
else {
*actualStartBlock = 0;
*actualNumBlocks = 0;
}
if (currCache)
(void) ReleaseBitmapBlock(vcb, blockRef, dirty);
return err;
}
static OSErr BlockAllocateKnown(
ExtendedVCB *vcb,
UInt32 maxBlocks,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks)
{
UInt32 i;
UInt32 foundBlocks;
UInt32 newStartBlock, newBlockCount;
if (vcb->vcbFreeExtCnt == 0)
return dskFulErr;
*actualStartBlock = vcb->vcbFreeExt[0].startBlock;
foundBlocks = vcb->vcbFreeExt[0].blockCount;
if (foundBlocks > maxBlocks)
foundBlocks = maxBlocks;
*actualNumBlocks = foundBlocks;
newStartBlock = vcb->vcbFreeExt[0].startBlock + foundBlocks;
newBlockCount = vcb->vcbFreeExt[0].blockCount - foundBlocks;
for (i=1; i<vcb->vcbFreeExtCnt; ++i)
{
if (vcb->vcbFreeExt[i].blockCount > newBlockCount)
{
vcb->vcbFreeExt[i-1].startBlock = vcb->vcbFreeExt[i].startBlock;
vcb->vcbFreeExt[i-1].blockCount = vcb->vcbFreeExt[i].blockCount;
}
else
{
break;
}
}
if (i == vcb->vcbFreeExtCnt)
{
--vcb->vcbFreeExtCnt;
}
else
{
vcb->vcbFreeExt[i-1].startBlock = newStartBlock;
vcb->vcbFreeExt[i-1].blockCount = newBlockCount;
}
if ((*actualStartBlock + *actualNumBlocks) > vcb->totalBlocks)
panic("BlockAllocateKnown: allocation overflow on \"%s\"", vcb->vcbVN);
return BlockMarkAllocated(vcb, *actualStartBlock, *actualNumBlocks);
}
__private_extern__
OSErr BlockMarkAllocated(
ExtendedVCB *vcb,
UInt32 startingBlock,
register UInt32 numBlocks)
{
OSErr err;
register UInt32 *currentWord; register UInt32 wordsLeft; register UInt32 bitMask; UInt32 firstBit; UInt32 numBits; UInt32 *buffer = NULL;
UInt32 blockRef;
UInt32 bitsPerBlock;
UInt32 wordsPerBlock;
struct hfsmount *hfsmp = VCBTOHFS(vcb);
err = ReadBitmapBlock(vcb, startingBlock, &buffer, &blockRef);
if (err != noErr) goto Exit;
{
UInt32 wordIndexInBlock;
bitsPerBlock = vcb->vcbVBMIOSize * kBitsPerByte;
wordsPerBlock = vcb->vcbVBMIOSize / kBytesPerWord;
wordIndexInBlock = (startingBlock & (bitsPerBlock-1)) / kBitsPerWord;
currentWord = buffer + wordIndexInBlock;
wordsLeft = wordsPerBlock - wordIndexInBlock;
}
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, (struct buf *)blockRef);
}
firstBit = startingBlock % kBitsPerWord;
if (firstBit != 0) {
bitMask = kAllBitsSetInWord >> firstBit; numBits = kBitsPerWord - firstBit; if (numBits > numBlocks) {
numBits = numBlocks; bitMask &= ~(kAllBitsSetInWord >> (firstBit + numBits)); }
#if DEBUG_BUILD
if ((*currentWord & SWAP_BE32 (bitMask)) != 0) {
panic("BlockMarkAllocated: blocks already allocated!");
}
#endif
*currentWord |= SWAP_BE32 (bitMask); numBlocks -= numBits;
++currentWord; --wordsLeft; }
bitMask = kAllBitsSetInWord; while (numBlocks >= kBitsPerWord) {
if (wordsLeft == 0) {
startingBlock += bitsPerBlock;
buffer = NULL;
err = ReleaseBitmapBlock(vcb, blockRef, true);
if (err != noErr) goto Exit;
err = ReadBitmapBlock(vcb, startingBlock, &buffer, &blockRef);
if (err != noErr) goto Exit;
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, (struct buf *)blockRef);
}
currentWord = buffer;
wordsLeft = wordsPerBlock;
}
#if DEBUG_BUILD
if (*currentWord != 0) {
panic("BlockMarkAllocated: blocks already allocated!");
}
#endif
*currentWord = SWAP_BE32 (bitMask);
numBlocks -= kBitsPerWord;
++currentWord; --wordsLeft; }
if (numBlocks != 0) {
bitMask = ~(kAllBitsSetInWord >> numBlocks); if (wordsLeft == 0) {
startingBlock += bitsPerBlock;
buffer = NULL;
err = ReleaseBitmapBlock(vcb, blockRef, true);
if (err != noErr) goto Exit;
err = ReadBitmapBlock(vcb, startingBlock, &buffer, &blockRef);
if (err != noErr) goto Exit;
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, (struct buf *)blockRef);
}
currentWord = buffer;
wordsLeft = wordsPerBlock;
}
#if DEBUG_BUILD
if ((*currentWord & SWAP_BE32 (bitMask)) != 0) {
panic("BlockMarkAllocated: blocks already allocated!");
}
#endif
*currentWord |= SWAP_BE32 (bitMask);
}
Exit:
if (buffer)
(void)ReleaseBitmapBlock(vcb, blockRef, true);
return err;
}
__private_extern__
OSErr BlockMarkFree(
ExtendedVCB *vcb,
UInt32 startingBlock,
register UInt32 numBlocks)
{
OSErr err;
register UInt32 *currentWord; register UInt32 wordsLeft; register UInt32 bitMask; UInt32 firstBit; UInt32 numBits; UInt32 *buffer = NULL;
UInt32 blockRef;
UInt32 bitsPerBlock;
UInt32 wordsPerBlock;
struct hfsmount *hfsmp = VCBTOHFS(vcb);
if (startingBlock + numBlocks > vcb->totalBlocks) {
panic("hfs: block mark free: trying to free non-existent blocks (%d %d %d)\n",
startingBlock, numBlocks, vcb->totalBlocks);
}
err = ReadBitmapBlock(vcb, startingBlock, &buffer, &blockRef);
if (err != noErr) goto Exit;
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, (struct buf *)blockRef);
}
{
UInt32 wordIndexInBlock;
bitsPerBlock = vcb->vcbVBMIOSize * kBitsPerByte;
wordsPerBlock = vcb->vcbVBMIOSize / kBytesPerWord;
wordIndexInBlock = (startingBlock & (bitsPerBlock-1)) / kBitsPerWord;
currentWord = buffer + wordIndexInBlock;
wordsLeft = wordsPerBlock - wordIndexInBlock;
}
firstBit = startingBlock % kBitsPerWord;
if (firstBit != 0) {
bitMask = kAllBitsSetInWord >> firstBit; numBits = kBitsPerWord - firstBit; if (numBits > numBlocks) {
numBits = numBlocks; bitMask &= ~(kAllBitsSetInWord >> (firstBit + numBits)); }
if ((*currentWord & SWAP_BE32 (bitMask)) != SWAP_BE32 (bitMask)) {
goto Corruption;
}
*currentWord &= SWAP_BE32 (~bitMask); numBlocks -= numBits;
++currentWord; --wordsLeft; }
while (numBlocks >= kBitsPerWord) {
if (wordsLeft == 0) {
startingBlock += bitsPerBlock;
buffer = NULL;
err = ReleaseBitmapBlock(vcb, blockRef, true);
if (err != noErr) goto Exit;
err = ReadBitmapBlock(vcb, startingBlock, &buffer, &blockRef);
if (err != noErr) goto Exit;
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, (struct buf *)blockRef);
}
currentWord = buffer;
wordsLeft = wordsPerBlock;
}
if (*currentWord != SWAP_BE32 (kAllBitsSetInWord)) {
goto Corruption;
}
*currentWord = 0; numBlocks -= kBitsPerWord;
++currentWord; --wordsLeft; }
if (numBlocks != 0) {
bitMask = ~(kAllBitsSetInWord >> numBlocks); if (wordsLeft == 0) {
startingBlock += bitsPerBlock;
buffer = NULL;
err = ReleaseBitmapBlock(vcb, blockRef, true);
if (err != noErr) goto Exit;
err = ReadBitmapBlock(vcb, startingBlock, &buffer, &blockRef);
if (err != noErr) goto Exit;
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, (struct buf *)blockRef);
}
currentWord = buffer;
wordsLeft = wordsPerBlock;
}
if ((*currentWord & SWAP_BE32 (bitMask)) != SWAP_BE32 (bitMask)) {
goto Corruption;
}
*currentWord &= SWAP_BE32 (~bitMask);
}
Exit:
if (buffer)
(void)ReleaseBitmapBlock(vcb, blockRef, true);
return err;
Corruption:
#if DEBUG_BUILD
panic("BlockMarkFree: blocks not allocated!");
#else
printf("hfs: WARNING - blocks on volume %s not allocated!\n", vcb->vcbVN);
vcb->vcbAtrb |= kHFSVolumeInconsistentMask;
MarkVCBDirty(vcb);
err = EIO;
goto Exit;
#endif
}
static OSErr BlockFindContiguous(
ExtendedVCB *vcb,
UInt32 startingBlock,
UInt32 endingBlock,
UInt32 minBlocks,
UInt32 maxBlocks,
Boolean useMetaZone,
UInt32 *actualStartBlock,
UInt32 *actualNumBlocks)
{
OSErr err;
register UInt32 currentBlock; UInt32 firstBlock; UInt32 stopBlock; UInt32 foundBlocks; UInt32 *buffer = NULL;
register UInt32 *currentWord;
register UInt32 bitMask;
register UInt32 wordsLeft;
register UInt32 tempWord;
UInt32 blockRef;
UInt32 wordsPerBlock;
if (!useMetaZone) {
struct hfsmount *hfsmp = VCBTOHFS(vcb);
if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) &&
(startingBlock <= hfsmp->hfs_metazone_end))
startingBlock = hfsmp->hfs_metazone_end + 1;
}
if ((endingBlock - startingBlock) < minBlocks)
{
goto DiskFull;
}
stopBlock = endingBlock - minBlocks + 1;
currentBlock = startingBlock;
firstBlock = 0;
if (!useMetaZone)
currentBlock = NextBitmapBlock(vcb, currentBlock);
err = ReadBitmapBlock(vcb, currentBlock, &buffer, &blockRef);
if ( err != noErr ) goto ErrorExit;
wordsPerBlock = vcb->vcbVBMIOSize / kBytesPerWord;
wordsLeft = (currentBlock / kBitsPerWord) & (wordsPerBlock-1); currentWord = buffer + wordsLeft;
wordsLeft = wordsPerBlock - wordsLeft;
do
{
foundBlocks = 0;
bitMask = currentBlock & kBitsWithinWordMask;
if (bitMask)
{
tempWord = SWAP_BE32(*currentWord); bitMask = kHighBitInWordMask >> bitMask;
while (tempWord & bitMask)
{
bitMask >>= 1;
++currentBlock;
}
if (bitMask)
goto FoundUnused;
++currentWord;
--wordsLeft;
}
while (currentBlock < stopBlock)
{
if (wordsLeft == 0)
{
buffer = NULL;
err = ReleaseBitmapBlock(vcb, blockRef, false);
if (err != noErr) goto ErrorExit;
if (!useMetaZone) {
currentBlock = NextBitmapBlock(vcb, currentBlock);
if (currentBlock >= stopBlock) {
goto LoopExit;
}
}
err = ReadBitmapBlock(vcb, currentBlock, &buffer, &blockRef);
if ( err != noErr ) goto ErrorExit;
currentWord = buffer;
wordsLeft = wordsPerBlock;
}
if ((tempWord = SWAP_BE32(*currentWord)) + 1) {
bitMask = kHighBitInWordMask;
while (tempWord & bitMask)
{
bitMask >>= 1;
++currentBlock;
}
break; }
currentBlock += kBitsPerWord;
++currentWord;
--wordsLeft;
}
FoundUnused:
if (currentBlock >= stopBlock)
{
break;
}
firstBlock = currentBlock;
bitMask = currentBlock & kBitsWithinWordMask;
if (bitMask)
{
tempWord = SWAP_BE32(*currentWord); bitMask = kHighBitInWordMask >> bitMask;
while (bitMask && !(tempWord & bitMask))
{
bitMask >>= 1;
++currentBlock;
}
if (bitMask)
goto FoundUsed;
++currentWord;
--wordsLeft;
}
while (currentBlock < endingBlock)
{
if (wordsLeft == 0)
{
buffer = NULL;
err = ReleaseBitmapBlock(vcb, blockRef, false);
if (err != noErr) goto ErrorExit;
if (!useMetaZone) {
UInt32 nextBlock;
nextBlock = NextBitmapBlock(vcb, currentBlock);
if (nextBlock != currentBlock) {
goto LoopExit;
}
}
err = ReadBitmapBlock(vcb, currentBlock, &buffer, &blockRef);
if ( err != noErr ) goto ErrorExit;
currentWord = buffer;
wordsLeft = wordsPerBlock;
}
if ((tempWord = SWAP_BE32(*currentWord)) != 0)
{
bitMask = kHighBitInWordMask;
while (!(tempWord & bitMask))
{
bitMask >>= 1;
++currentBlock;
}
break; }
currentBlock += kBitsPerWord;
++currentWord;
--wordsLeft;
if ((currentBlock - firstBlock) >= maxBlocks)
break;
}
FoundUsed:
if (currentBlock > endingBlock)
currentBlock = endingBlock;
foundBlocks = currentBlock - firstBlock;
if (foundBlocks > maxBlocks)
foundBlocks = maxBlocks;
if (foundBlocks >= minBlocks)
break;
tempWord = vcb->vcbFreeExtCnt;
if (tempWord == kMaxFreeExtents && vcb->vcbFreeExt[kMaxFreeExtents-1].blockCount < foundBlocks)
--tempWord;
if (tempWord < kMaxFreeExtents)
{
while (tempWord && vcb->vcbFreeExt[tempWord-1].blockCount < foundBlocks)
{
vcb->vcbFreeExt[tempWord] = vcb->vcbFreeExt[tempWord-1];
--tempWord;
}
vcb->vcbFreeExt[tempWord].startBlock = firstBlock;
vcb->vcbFreeExt[tempWord].blockCount = foundBlocks;
if (vcb->vcbFreeExtCnt < kMaxFreeExtents)
++vcb->vcbFreeExtCnt;
}
} while (currentBlock < stopBlock);
LoopExit:
if (foundBlocks < minBlocks)
{
DiskFull:
err = dskFulErr;
ErrorExit:
*actualStartBlock = 0;
*actualNumBlocks = 0;
}
else
{
err = noErr;
*actualStartBlock = firstBlock;
*actualNumBlocks = foundBlocks;
}
if (buffer)
(void) ReleaseBitmapBlock(vcb, blockRef, false);
return err;
}