lf_hfs_btree_allocate.c [plain text]
#include "lf_hfs_btrees_io.h"
#include "lf_hfs_endian.h"
#include "lf_hfs_btrees_private.h"
#include "lf_hfs_vfsutils.h"
#include "lf_hfs_generic_buf.h"
static OSStatus GetMapNode (BTreeControlBlockPtr btreePtr,
BlockDescriptor *nodePtr,
u_int16_t **mapPtr,
u_int16_t *mapSize );
OSStatus AllocateNode (BTreeControlBlockPtr btreePtr, u_int32_t *nodeNum)
{
OSStatus err;
BlockDescriptor node;
u_int16_t *mapPtr, *pos;
u_int16_t mapSize, size;
u_int16_t freeWord;
u_int16_t mask;
u_int16_t bitOffset;
u_int32_t nodeNumber;
nodeNumber = 0; node.buffer = nil; node.blockHeader = nil;
while (true)
{
err = GetMapNode (btreePtr, &node, &mapPtr, &mapSize);
M_ExitOnError (err);
ModifyBlockStart(btreePtr->fileRefNum, &node);
pos = mapPtr;
size = mapSize;
size >>= 1;
while ( size-- )
{
if ( *pos++ != 0xFFFF ) break;
}
--pos;
if (*pos != 0xFFFF) break;
nodeNumber += mapSize << 3; }
freeWord = SWAP_BE16 (*pos);
bitOffset = 15;
mask = 0x8000;
do {
if ( (freeWord & mask) == 0)
break;
mask >>= 1;
} while (--bitOffset);
nodeNumber += ((pos - mapPtr) << 4) + (15 - bitOffset);
if (nodeNumber >= btreePtr->totalNodes)
{
err = fsBTFullErr;
goto ErrorExit;
}
*pos |= SWAP_BE16 (mask);
err = UpdateNode (btreePtr, &node, 0, kLockTransaction);
M_ExitOnError (err);
--btreePtr->freeNodes;
M_BTreeHeaderDirty(btreePtr);
BTUpdateReserve(btreePtr, 1);
*nodeNum = nodeNumber;
return noErr;
ErrorExit:
(void) ReleaseNode (btreePtr, &node);
*nodeNum = 0;
return err;
}
OSStatus FreeNode (BTreeControlBlockPtr btreePtr, u_int32_t nodeNum)
{
OSStatus err;
BlockDescriptor node;
u_int32_t nodeIndex;
u_int16_t mapSize = 0;
u_int16_t *mapPos = NULL;
u_int16_t bitOffset;
nodeIndex = 0; node.buffer = nil; node.blockHeader = nil;
while (nodeNum >= nodeIndex)
{
err = GetMapNode (btreePtr, &node, &mapPos, &mapSize);
M_ExitOnError (err);
nodeIndex += mapSize << 3; }
ModifyBlockStart(btreePtr->fileRefNum, &node);
nodeNum -= (nodeIndex - (mapSize << 3)); bitOffset = 15 - (nodeNum & 0x0000000F); mapPos += nodeNum >> 4;
M_SWAP_BE16_ClearBitNum (*mapPos, bitOffset);
err = UpdateNode (btreePtr, &node, 0, kLockTransaction);
M_ExitOnError (err);
++btreePtr->freeNodes;
M_BTreeHeaderDirty(btreePtr);
return noErr;
ErrorExit:
(void) ReleaseNode (btreePtr, &node);
return err;
}
OSStatus ExtendBTree (BTreeControlBlockPtr btreePtr,
u_int32_t newTotalNodes )
{
OSStatus err;
FCB *filePtr;
FSSize minEOF, maxEOF;
u_int16_t nodeSize;
u_int32_t oldTotalNodes;
u_int32_t newMapNodes;
u_int32_t mapBits, totalMapBits;
u_int32_t recStartBit;
u_int32_t nodeNum, nextNodeNum;
u_int32_t firstNewMapNodeNum, lastNewMapNodeNum;
BlockDescriptor mapNode, newNode;
u_int16_t *mapPos;
u_int16_t *mapStart;
u_int16_t mapSize;
u_int16_t mapNodeRecSize;
u_int32_t bitInWord, bitInRecord;
u_int16_t mapIndex;
oldTotalNodes = btreePtr->totalNodes;
if (newTotalNodes <= oldTotalNodes) return noErr;
nodeSize = btreePtr->nodeSize;
filePtr = GetFileControlBlock(btreePtr->fileRefNum);
mapNode.buffer = nil;
mapNode.blockHeader = nil;
newNode.buffer = nil;
newNode.blockHeader = nil;
mapNodeRecSize = nodeSize - sizeof(BTNodeDescriptor) - 6;
totalMapBits = 0;
do {
err = GetMapNode (btreePtr, &mapNode, &mapStart, &mapSize);
M_ExitOnError (err);
mapBits = mapSize << 3; recStartBit = totalMapBits; totalMapBits += mapBits;
} while ( ((BTNodeDescriptor*)mapNode.buffer)->fLink != 0 );
#if DEBUG
if (totalMapBits != CalcMapBits (btreePtr))
LFHFS_LOG(LEVEL_ERROR, "ExtendBTree: totalMapBits != CalcMapBits");
#endif
minEOF = (u_int64_t)newTotalNodes * (u_int64_t)nodeSize;
if ( (u_int64_t)filePtr->fcbEOF < minEOF )
{
maxEOF = (u_int64_t)0x7fffffffLL * (u_int64_t)nodeSize;
err = btreePtr->setEndOfForkProc (btreePtr->fileRefNum, minEOF, maxEOF);
M_ExitOnError (err);
}
newTotalNodes = (uint32_t)(filePtr->fcbEOF / nodeSize);
btreePtr->totalNodes = newTotalNodes;
newMapNodes = 0;
if (newTotalNodes > totalMapBits)
{
newMapNodes = (((newTotalNodes - totalMapBits) >> 3) / mapNodeRecSize) + 1;
firstNewMapNodeNum = oldTotalNodes;
lastNewMapNodeNum = firstNewMapNodeNum + newMapNodes - 1;
}
else
{
err = ReleaseNode (btreePtr, &mapNode);
M_ExitOnError (err);
goto Success;
}
ModifyBlockStart(btreePtr->fileRefNum, &mapNode);
((BTNodeDescriptor*)mapNode.buffer)->fLink = firstNewMapNodeNum;
nodeNum = firstNewMapNodeNum;
while (true)
{
err = GetNewNode (btreePtr, nodeNum, &newNode);
M_ExitOnError (err);
ModifyBlockStart(btreePtr->fileRefNum, &newNode);
((NodeDescPtr)newNode.buffer)->numRecords = 1;
((NodeDescPtr)newNode.buffer)->kind = kBTMapNode;
*(u_int16_t *)((Ptr)newNode.buffer + nodeSize - 4) = nodeSize - 6;
if (nodeNum++ == lastNewMapNodeNum)
break;
((BTNodeDescriptor*)newNode.buffer)->fLink = nodeNum;
err = UpdateNode (btreePtr, &newNode, 0, kLockTransaction);
M_ExitOnError (err);
}
err = UpdateNode (btreePtr, &newNode, 0, kLockTransaction);
M_ExitOnError (err);
nodeNum = firstNewMapNodeNum;
do {
bitInRecord = nodeNum - recStartBit;
while (bitInRecord >= mapBits)
{
nextNodeNum = ((NodeDescPtr)mapNode.buffer)->fLink;
if ( nextNodeNum == 0)
{
err = fsBTNoMoreMapNodesErr;
goto ErrorExit;
}
err = UpdateNode (btreePtr, &mapNode, 0, kLockTransaction);
M_ExitOnError (err);
err = GetNode (btreePtr, nextNodeNum, 0, &mapNode);
M_ExitOnError (err);
ModifyBlockStart(btreePtr->fileRefNum, &mapNode);
mapIndex = 0;
mapStart = (u_int16_t *) GetRecordAddress (btreePtr, mapNode.buffer, mapIndex);
mapSize = GetRecordSize (btreePtr, mapNode.buffer, mapIndex);
#if DEBUG
if (mapSize != M_MapRecordSize (btreePtr->nodeSize) )
{
LFHFS_LOG(LEVEL_ERROR, "ExtendBTree: mapSize != M_MapRecordSize");
}
#endif
mapBits = mapSize << 3; recStartBit = totalMapBits; totalMapBits += mapBits;
bitInRecord = nodeNum - recStartBit;
}
mapPos = mapStart + ((nodeNum - recStartBit) >> 4);
bitInWord = 15 - ((nodeNum - recStartBit) & 0x0000000F);
M_SWAP_BE16_SetBitNum (*mapPos, bitInWord);
++nodeNum;
} while (nodeNum <= lastNewMapNodeNum);
err = UpdateNode (btreePtr, &mapNode, 0, kLockTransaction);
M_ExitOnError (err);
Success:
btreePtr->totalNodes = newTotalNodes;
btreePtr->freeNodes += (newTotalNodes - oldTotalNodes) - newMapNodes;
M_BTreeHeaderDirty(btreePtr);
(void) UpdateHeader (btreePtr, true);
return noErr;
ErrorExit:
(void) ReleaseNode (btreePtr, &mapNode);
(void) ReleaseNode (btreePtr, &newNode);
return err;
}
static
OSStatus GetMapNode (BTreeControlBlockPtr btreePtr,
BlockDescriptor *nodePtr,
u_int16_t **mapPtr,
u_int16_t *mapSize )
{
OSStatus err;
u_int16_t mapIndex;
u_int32_t nextNodeNum;
if (nodePtr->buffer != nil) {
nextNodeNum = ((NodeDescPtr)nodePtr->buffer)->fLink;
if (nextNodeNum == 0)
{
err = fsBTNoMoreMapNodesErr;
goto ErrorExit;
}
err = ReleaseNode (btreePtr, nodePtr);
M_ExitOnError (err);
err = GetNode (btreePtr, nextNodeNum, 0, nodePtr);
M_ExitOnError (err);
if ( ((NodeDescPtr)nodePtr->buffer)->kind != kBTMapNode)
{
err = fsBTBadNodeType;
goto ErrorExit;
}
++btreePtr->numMapNodesRead;
mapIndex = 0;
} else {
err = GetNode (btreePtr, kHeaderNodeNum, 0, nodePtr);
M_ExitOnError (err);
if ( ((NodeDescPtr)nodePtr->buffer)->kind != kBTHeaderNode)
{
err = fsBTInvalidHeaderErr; goto ErrorExit;
}
mapIndex = 2;
}
*mapPtr = (u_int16_t *) GetRecordAddress (btreePtr, nodePtr->buffer, mapIndex);
*mapSize = GetRecordSize (btreePtr, nodePtr->buffer, mapIndex);
return noErr;
ErrorExit:
(void) ReleaseNode (btreePtr, nodePtr);
*mapPtr = nil;
*mapSize = 0;
return err;
}
u_int32_t CalcMapBits (BTreeControlBlockPtr btreePtr)
{
u_int32_t mapBits;
mapBits = (u_int32_t)(M_HeaderMapRecordSize (btreePtr->nodeSize) << 3);
while (mapBits < btreePtr->totalNodes)
mapBits += M_MapRecordSize (btreePtr->nodeSize) << 3;
return mapBits;
}
int
BTZeroUnusedNodes(FCB *filePtr)
{
int err=0;
u_int16_t *mapPtr, *pos;
u_int16_t mapSize, size;
u_int16_t mask;
u_int16_t bitNumber;
u_int16_t word;
vnode_t vp = FTOV(filePtr);
BTreeControlBlockPtr btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
GenericLFBufPtr bp = NULL;
u_int32_t nodeNumber = 0;
BlockDescriptor mapNode = {0};
mapNode.buffer = nil;
mapNode.blockHeader = nil;
while (true)
{
err = GetMapNode (btreePtr, &mapNode, &mapPtr, &mapSize);
if (err)
{
err = MacToVFSError(err);
goto ErrorExit;
}
pos = mapPtr;
size = mapSize;
size >>= 1;
while (size--)
{
if (*pos != 0xFFFF)
{
word = SWAP_BE16(*pos);
for (bitNumber = 0, mask = 0x8000;
bitNumber < 16;
++bitNumber, mask >>= 1)
{
if (word & mask)
continue;
if (nodeNumber + bitNumber >= btreePtr->totalNodes)
{
goto done;
}
bp = lf_hfs_generic_buf_allocate(vp, nodeNumber + bitNumber, btreePtr->nodeSize, 0); if (bp == NULL)
{
LFHFS_LOG(LEVEL_ERROR , "BTZeroUnusedNodes: unable to read node %u\n", nodeNumber + bitNumber);
err = EIO;
goto ErrorExit;
}
if (bp->uCacheFlags & GEN_BUF_WRITE_LOCK) {
lf_hfs_generic_buf_release(bp);
continue;
}
lf_hfs_generic_buf_clear(bp);
err = lf_hfs_generic_buf_write(bp);
if (err) {
goto ErrorExit;
}
lf_hfs_generic_buf_release(bp);
}
}
++pos;
nodeNumber += 16;
}
}
ErrorExit:
done:
(void) ReleaseNode(btreePtr, &mapNode);
return err;
}