xfs: B+Tree GetNext() works

The GetNext() works well for B+Trees and that wraps up the work needed
for all kinds of GetNext().
Change-Id: Ie965d3da273364f8fdbdb8faee5cb3c214881130
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3124
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
CruxBox 2020-08-03 12:08:38 +05:30 committed by Adrien Destugues
parent 7766b5a136
commit 1dcb6c252c
9 changed files with 713 additions and 65 deletions

View File

@ -6,7 +6,6 @@
#include "BPlusTree.h"
TreeDirectory::TreeDirectory(Inode* inode)
:
fInode(inode),
@ -14,7 +13,11 @@ TreeDirectory::TreeDirectory(Inode* inode)
fRoot(NULL),
fExtents(NULL),
fSingleDirBlock(NULL),
fCountOfFilledExtents(0)
fOffsetOfSingleDirBlock(-1),
fCountOfFilledExtents(0),
fCurMapIndex(0),
fOffset(0),
fCurBlockNumber(0)
{
fRoot = new(std::nothrow) BlockInDataFork;
if (fRoot == NULL) {
@ -30,6 +33,12 @@ TreeDirectory::TreeDirectory(Inode* inode)
memcpy((void*)fRoot,
DIR_DFORK_PTR(fInode->Buffer()), sizeof(BlockInDataFork));
for (int i = 0; i < MAX_TREE_DEPTH; i++) {
fPathForLeaves[i].blockData = NULL;
fPathForData[i].blockData = NULL;
}
}
@ -55,13 +64,6 @@ TreeDirectory::BlockLen()
}
TreeKey
TreeDirectory::GetKey(int pos)
{
return BlockLen() + (pos - 1) * XFS_KEY_SIZE;
}
size_t
TreeDirectory::KeySize()
{
@ -76,22 +78,198 @@ TreeDirectory::PtrSize()
}
TreePointer*
TreeDirectory::GetPtr(int pos, LongBlock* curLongBlock)
size_t
TreeDirectory::MaxRecordsPossibleRoot()
{
size_t availableSpace = fInode->GetVolume()->BlockSize() - BlockLen();
size_t maxRecords = availableSpace / (KeySize() + PtrSize());
size_t offsetIntoNode =
BlockLen() + maxRecords * KeySize() + (pos - 1) * PtrSize();
return (TreePointer*)((char*)curLongBlock + offsetIntoNode);
size_t lengthOfDataFork;
if (fInode->ForkOffset() != 0)
lengthOfDataFork = fInode->ForkOffset() << 3;
if (fInode->ForkOffset() == 0) {
lengthOfDataFork = fInode->GetVolume()->InodeSize()
- INODE_CORE_UNLINKED_SIZE;
}
lengthOfDataFork -= sizeof(BlockInDataFork);
return lengthOfDataFork / (KeySize() + PtrSize());
}
size_t
TreeDirectory::MaxRecordsPossible(size_t len)
TreeDirectory::GetPtrOffsetIntoRoot(int pos)
{
len -= sizeof(BlockInDataFork);
return len / (KeySize() + PtrSize());
size_t maxRecords = MaxRecordsPossibleRoot();
return (sizeof(BlockInDataFork)
+ maxRecords * KeySize() + (pos - 1) * PtrSize());
}
size_t
TreeDirectory::MaxRecordsPossibleNode()
{
size_t availableSpace = fInode->GetVolume()->BlockSize() - BlockLen();
return availableSpace / (KeySize() + PtrSize());
}
size_t
TreeDirectory::GetPtrOffsetIntoNode(int pos)
{
size_t maxRecords = MaxRecordsPossibleNode();
return BlockLen() + maxRecords * KeySize() + (pos - 1) * PtrSize();
}
TreePointer*
TreeDirectory::GetPtrFromRoot(int pos)
{
return (TreePointer*)
((char*)DIR_DFORK_PTR(fInode->Buffer()) + GetPtrOffsetIntoRoot(pos));
}
TreePointer*
TreeDirectory::GetPtrFromNode(int pos, void* buffer)
{
size_t offsetIntoNode = GetPtrOffsetIntoNode(pos);
return (TreePointer*)((char*)buffer + offsetIntoNode);
}
TreeKey*
TreeDirectory::GetKeyFromNode(int pos, void* buffer)
{
return (TreeKey*)
((char*)buffer + BlockLen() + (pos - 1) * KeySize());
}
TreeKey*
TreeDirectory::GetKeyFromRoot(int pos)
{
off_t offset = (pos - 1) * KeySize();
char* base = (char*)DIR_DFORK_PTR(fInode->Buffer())
+ sizeof(BlockInDataFork);
return (TreeKey*) (base + offset);
}
status_t
TreeDirectory::SearchOffsetInTreeNode(uint32 offset,
TreePointer** pointer, int pathIndex)
{
// This is O(MaxRecords). Next is to implement this using upper bound
// binary search and get O(log(MaxRecords))
*pointer = NULL;
TreeKey* offsetKey = NULL;
size_t maxRecords = MaxRecordsPossibleNode();
for (int i = maxRecords - 1; i >= 0; i--) {
offsetKey
= GetKeyFromNode(i + 1, (void*)fPathForLeaves[pathIndex].blockData);
if (B_BENDIAN_TO_HOST_INT64(*offsetKey) <= offset) {
*pointer = GetPtrFromNode(i + 1, (void*)
fPathForLeaves[pathIndex].blockData);
break;
}
}
if (offsetKey == NULL || *pointer == NULL)
return B_BAD_VALUE;
return B_OK;
}
status_t
TreeDirectory::SearchAndFillPath(uint32 offset, int type)
{
TRACE("SearchAndFillPath:\n");
PathNode* path;
if (type == DATA)
path = fPathForData;
else if (type == LEAF)
path = fPathForLeaves;
else
return B_BAD_VALUE;
TreePointer* ptrToNode = NULL;
TreeKey* offsetKey = NULL;
// Go down the root of the tree first
for (int i = fRoot->NumRecords() - 1; i >= 0; i--) {
offsetKey = GetKeyFromRoot(i + 1);
if (B_BENDIAN_TO_HOST_INT64(*offsetKey) <= offset) {
ptrToNode = GetPtrFromRoot(i + 1);
break;
}
}
if (ptrToNode == NULL || offsetKey == NULL) {
//Corrupt tree
return B_BAD_VALUE;
}
// We now have gone down the root and save path if not saved.
int level = fRoot->Levels() - 1;
Volume* volume = fInode->GetVolume();
status_t status;
for (int i = 0; i < MAX_TREE_DEPTH && level >= 0; i++, level--) {
uint64 requiredBlock = B_BENDIAN_TO_HOST_INT64(*ptrToNode);
TRACE("requiredBlock:(%d)\n", requiredBlock);
if (path[i].blockNumber == requiredBlock) {
// This block already has what we need
if (path[i].type == 2)
break;
status = SearchOffsetInTreeNode(offset, &ptrToNode, i);
if (status != B_OK)
return status;
continue;
}
// We do not have the block we need
size_t len;
if (level == 0) {
// The size of buffer should be the directory block size
len = fInode->DirBlockSize();
TRACE("path node type:(%d)\n", path[i].type);
if (path[i].type != 2) {
// Size is not directory block size.
delete path[i].blockData;
path[i].type = 0;
path[i].blockData = new(std::nothrow) char[len];
if (path[i].blockData == NULL)
return B_NO_MEMORY;
path[i].type = 2;
}
uint64 readPos = fInode->FileSystemBlockToAddr(requiredBlock);
if (read_pos(volume->Device(), readPos, path[i].blockData, len)
!= len) {
ERROR("FillPath::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
path[i].blockNumber = requiredBlock;
break;
}
// The size of buffer should be the block size
len = volume->BlockSize();
if (path[i].type != 1) {
delete path[i].blockData;
path[i].type = 0;
path[i].blockData = new(std::nothrow) char[len];
if (path[i].blockData == NULL)
return B_NO_MEMORY;
path[i].type = 1;
}
uint64 readPos = fInode->FileSystemBlockToAddr(requiredBlock);
if (read_pos(volume->Device(), readPos, path[i].blockData, len)
!= len) {
ERROR("FillPath::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
path[i].blockNumber = requiredBlock;
status = SearchOffsetInTreeNode(offset, &ptrToNode, i);
if (status != B_OK)
return status;
}
return B_OK;
}
@ -99,26 +277,20 @@ status_t
TreeDirectory::GetAllExtents()
{
xfs_extnum_t noOfExtents = fInode->DataExtentsCount();
ExtentMapUnwrap* extentsWrapped
= new(std::nothrow) ExtentMapUnwrap[noOfExtents];
if (extentsWrapped == NULL)
return B_NO_MEMORY;
ArrayDeleter<ExtentMapUnwrap> extentsWrappedDeleter(extentsWrapped);
Volume* volume = fInode->GetVolume();
uint16 levelsInTree = fRoot->Levels();
size_t lengthOfDataFork;
if (fInode->ForkOffset() != 0)
lengthOfDataFork = fInode->ForkOffset() << 3;
else
lengthOfDataFork = volume->InodeSize() - INODE_CORE_UNLINKED_SIZE;
TRACE("Length Of Data Fork: (%d)\n", lengthOfDataFork);
size_t maxRecords = MaxRecordsPossible(lengthOfDataFork);
size_t maxRecords = MaxRecordsPossibleRoot();
TRACE("Maxrecords: (%d)\n", maxRecords);
TreePointer* ptrToNode = (TreePointer*)
((char*)DIR_DFORK_PTR(fInode->Buffer())
+ sizeof(BlockInDataFork) + maxRecords*KeySize());
TreePointer* ptrToNode = GetPtrFromRoot(1);
size_t len = volume->BlockSize();
char node[len];
@ -136,14 +308,11 @@ TreeDirectory::GetAllExtents()
readPos = fInode->FileSystemBlockToAddr(fileSystemBlockNo);
if (read_pos(volume->Device(), readPos, node, len) != len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
delete[] extentsWrapped;
extentsWrapped = NULL;
return B_IO_ERROR;
}
LongBlock* curLongBlock = (LongBlock*)node;
ASSERT(curLongBlock->Magic() == XFS_BMAP_MAGIC);
ptrToNode = GetPtr(1, curLongBlock);
ptrToNode = GetPtrFromNode(1, (void*)curLongBlock);
// Get's the first pointer. This points to next node.
levelsInTree--;
}
@ -153,9 +322,6 @@ TreeDirectory::GetAllExtents()
if (read_pos(volume->Device(), readPos, fSingleDirBlock, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
delete[] extentsWrapped;
extentsWrapped = NULL;
return B_IO_ERROR;
}
levelsInTree--;
@ -187,9 +353,6 @@ TreeDirectory::GetAllExtents()
if (read_pos(volume->Device(), readPos, fSingleDirBlock, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
delete[] extentsWrapped;
extentsWrapped = NULL;
return B_IO_ERROR;
}
}
@ -197,13 +360,200 @@ TreeDirectory::GetAllExtents()
status_t status = UnWrapExtents(extentsWrapped);
delete[] extentsWrapped;
extentsWrapped = NULL;
return status;
}
status_t
TreeDirectory::FillBuffer(char* blockBuffer, int howManyBlocksFurther,
ExtentMapEntry* targetMap)
{
TRACE("FILLBUFFER\n");
ExtentMapEntry map;
if (targetMap == NULL)
map = fExtents[fCurMapIndex];
if (targetMap != NULL)
map = *targetMap;
if (map.br_state != 0)
return B_BAD_VALUE;
size_t len = fInode->DirBlockSize();
if (blockBuffer == NULL) {
blockBuffer = new(std::nothrow) char[len];
if (blockBuffer == NULL)
return B_NO_MEMORY;
}
xfs_daddr_t readPos = fInode->FileSystemBlockToAddr(
map.br_startblock + howManyBlocksFurther);
if (read_pos(fInode->GetVolume()->Device(), readPos, blockBuffer, len)
!= len) {
ERROR("TreeDirectory::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
if (targetMap == NULL) {
fSingleDirBlock = blockBuffer;
ExtentDataHeader* header = (ExtentDataHeader*) fSingleDirBlock;
if (B_BENDIAN_TO_HOST_INT32(header->magic) == DATA_HEADER_MAGIC) {
TRACE("DATA BLOCK VALID\n");
} else {
TRACE("DATA BLOCK INVALID\n");
return B_BAD_VALUE;
}
}
if (targetMap != NULL) {
fSingleDirBlock = blockBuffer;
ExtentLeafHeader* header = (ExtentLeafHeader*) fSingleDirBlock;
if (B_BENDIAN_TO_HOST_INT16(header->info.magic) == XFS_DA_NODE_MAGIC
|| B_BENDIAN_TO_HOST_INT16(header->info.magic)
== XFS_DIR2_LEAFN_MAGIC) {
TRACE("LEAF/NODE VALID\n");
} else {
TRACE("LEAF/NODE INVALID\n");
return B_BAD_VALUE;
}
}
return B_OK;
}
int
TreeDirectory::EntrySize(int len) const
{
int entrySize = sizeof(xfs_ino_t) + sizeof(uint8) + len + sizeof(uint16);
// uint16 is for the tag
if (fInode->HasFileTypeField())
entrySize += sizeof(uint8);
return ROUNDUP(entrySize, 8);
// rounding off to next greatest multiple of 8
}
/*
* Throw in the desired block number and get the index of it
*/
status_t
TreeDirectory::SearchMapInAllExtent(int blockNo, uint32& mapIndex)
{
ExtentMapEntry map;
for (uint32 i = 0; i < fCountOfFilledExtents; i++) {
map = fExtents[i];
if (map.br_startoff <= blockNo
&& (blockNo <= map.br_startoff + map.br_blockcount - 1)) {
// Map found
mapIndex = i;
return B_OK;
}
}
return B_ENTRY_NOT_FOUND;
}
status_t
TreeDirectory::GetNext(char* name, size_t* length, xfs_ino_t* ino)
{
TRACE("TreeDirectory::GetNext\n");
status_t status;
if (fExtents == NULL) {
status = GetAllExtents();
if (status != B_OK)
return status;
}
status = FillBuffer(fSingleDirBlock, 0);
if (status != B_OK)
return status;
Volume* volume = fInode->GetVolume();
void* entry = (void*)((ExtentDataHeader*)fSingleDirBlock + 1);
// This could be an unused entry so we should check
uint32 blockNoFromAddress = BLOCKNO_FROM_ADDRESS(fOffset, volume);
if (fOffset != 0 && blockNoFromAddress == fCurBlockNumber) {
entry = (void*)(fSingleDirBlock
+ BLOCKOFFSET_FROM_ADDRESS(fOffset, fInode));
// This gets us a little faster to the next entry
}
uint32 curDirectorySize = fInode->Size();
ExtentMapEntry& map = fExtents[fCurMapIndex];
while (fOffset != curDirectorySize) {
blockNoFromAddress = BLOCKNO_FROM_ADDRESS(fOffset, volume);
TRACE("fOffset:(%d), blockNoFromAddress:(%d)\n",
fOffset, blockNoFromAddress);
if (fCurBlockNumber != blockNoFromAddress
&& blockNoFromAddress > map.br_startoff
&& blockNoFromAddress
<= map.br_startoff + map.br_blockcount - 1) {
// When the block is mapped in the same data
// map entry but is not the first block
status = FillBuffer(fSingleDirBlock,
blockNoFromAddress - map.br_startoff);
if (status != B_OK)
return status;
entry = (void*)((ExtentDataHeader*)fSingleDirBlock + 1);
fOffset = fOffset + sizeof(ExtentDataHeader);
fCurBlockNumber = blockNoFromAddress;
} else if (fCurBlockNumber != blockNoFromAddress) {
// When the block isn't mapped in the current data map entry
uint32 curMapIndex;
status = SearchMapInAllExtent(blockNoFromAddress, curMapIndex);
if (status != B_OK)
return status;
fCurMapIndex = curMapIndex;
map = fExtents[fCurMapIndex];
status = FillBuffer(fSingleDirBlock,
blockNoFromAddress - map.br_startoff);
if (status != B_OK)
return status;
entry = (void*)((ExtentDataHeader*)fSingleDirBlock + 1);
fOffset = fOffset + sizeof(ExtentDataHeader);
fCurBlockNumber = blockNoFromAddress;
}
ExtentUnusedEntry* unusedEntry = (ExtentUnusedEntry*)entry;
if (B_BENDIAN_TO_HOST_INT16(unusedEntry->freetag) == DIR2_FREE_TAG) {
TRACE("Unused entry found\n");
fOffset = fOffset + B_BENDIAN_TO_HOST_INT16(unusedEntry->length);
entry = (void*)
((char*)entry + B_BENDIAN_TO_HOST_INT16(unusedEntry->length));
continue;
}
ExtentDataEntry* dataEntry = (ExtentDataEntry*) entry;
uint16 currentOffset = (char*)dataEntry - fSingleDirBlock;
TRACE("GetNext: fOffset:(%d), currentOffset:(%d)\n",
BLOCKOFFSET_FROM_ADDRESS(fOffset, fInode), currentOffset);
if (BLOCKOFFSET_FROM_ADDRESS(fOffset, fInode) > currentOffset) {
entry = (void*)((char*)entry + EntrySize(dataEntry->namelen));
continue;
}
if (dataEntry->namelen + 1 > *length)
return B_BUFFER_OVERFLOW;
fOffset = fOffset + EntrySize(dataEntry->namelen);
memcpy(name, dataEntry->name, dataEntry->namelen);
name[dataEntry->namelen] = '\0';
*length = dataEntry->namelen + 1;
*ino = B_BENDIAN_TO_HOST_INT64(dataEntry->inumber);
TRACE("Entry found. Name: (%s), Length: (%ld), ino: (%ld)\n", name,
*length, *ino);
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
status_t
TreeDirectory::UnWrapExtents(ExtentMapUnwrap* extentsWrapped)
{
@ -223,3 +573,253 @@ TreeDirectory::UnWrapExtents(ExtentMapUnwrap* extentsWrapped)
return B_OK;
}
void
TreeDirectory::FillMapEntry(int num, ExtentMapEntry** fMap,
int type, int pathIndex)
{
void* pointerToMap;
if (type == DATA) {
char* base = fPathForData[pathIndex].blockData + BlockLen();
off_t offset = num * EXTENT_SIZE;
pointerToMap = (void*)(base + offset);
} else {
char* base = fPathForLeaves[pathIndex].blockData + BlockLen();
off_t offset = num * EXTENT_SIZE;
pointerToMap = (void*)(base + offset);
}
uint64 firstHalf = *((uint64*)pointerToMap);
uint64 secondHalf = *((uint64*)pointerToMap + 1);
//dividing the 128 bits into 2 parts.
firstHalf = B_BENDIAN_TO_HOST_INT64(firstHalf);
secondHalf = B_BENDIAN_TO_HOST_INT64(secondHalf);
(*fMap)->br_state = firstHalf >> 63;
(*fMap)->br_startoff = (firstHalf & MASK(63)) >> 9;
(*fMap)->br_startblock = ((firstHalf & MASK(9)) << 43) | (secondHalf >> 21);
(*fMap)->br_blockcount = secondHalf & MASK(21);
TRACE("FillMapEntry: startoff:(%ld), startblock:(%ld), blockcount:(%ld),"
"state:(%d)\n", (*fMap)->br_startoff, (*fMap)->br_startblock,
(*fMap)->br_blockcount, (*fMap)->br_state);
}
void
TreeDirectory::SearchForMapInDirectoryBlock(int blockNo,
int entries, ExtentMapEntry** map, int type, int pathIndex)
{
TRACE("SearchForMapInDirectoryBlock: blockNo:(%d)\n", blockNo);
for (int i = 0; i < entries; i++) {
FillMapEntry(i, map, type, pathIndex);
if ((*map)->br_startoff <= blockNo
&& (blockNo <= (*map)->br_startoff + (*map)->br_blockcount - 1)) {
// Map found
return;
}
}
// Map wasn't found. Some kind of corruption. This is checked by caller.
*map = NULL;
}
uint32
TreeDirectory::SearchForHashInNodeBlock(uint32 hashVal)
{
NodeHeader* header = (NodeHeader*)(fSingleDirBlock);
NodeEntry* entry = (NodeEntry*)(fSingleDirBlock + sizeof(NodeHeader));
int count = B_BENDIAN_TO_HOST_INT16(header->count);
for (int i = 0; i < count; i++) {
if (hashVal <= B_BENDIAN_TO_HOST_INT32(entry[i].hashval))
return B_BENDIAN_TO_HOST_INT32(entry[i].before);
}
return 0;
}
status_t
TreeDirectory::Lookup(const char* name, size_t length, xfs_ino_t* ino)
{
TRACE("TreeDirectory: Lookup\n");
TRACE("Name: %s\n", name);
uint32 hashValueOfRequest = hashfunction(name, length);
TRACE("Hashval:(%ld)\n", hashValueOfRequest);
Volume* volume = fInode->GetVolume();
status_t status;
ExtentMapEntry* targetMap = new(std::nothrow) ExtentMapEntry;
if (targetMap == NULL)
return B_NO_MEMORY;
int pathIndex = -1;
uint32 rightOffset = LEAF_STARTOFFSET(volume->BlockLog());
// In node directories, the "node blocks" had a single level
// Here we could have multiple levels. With each iteration of
// the loop we go a level lower.
while (rightOffset != fOffsetOfSingleDirBlock && 1) {
status = SearchAndFillPath(rightOffset, LEAF);
if (status != B_OK)
return status;
// The path should now have the Tree Leaf at appropriate level
// Find the directory block in the path
for (int i = 0; i < MAX_TREE_DEPTH; i++) {
if (fPathForLeaves[i].type == 2) {
pathIndex = i;
break;
}
}
if (pathIndex == -1) {
// corrupt tree
return B_BAD_VALUE;
}
// Get the node block from directory block
// If level is non-zero, reiterate with new "rightOffset"
// Else, we are at leaf block, then break
LongBlock* curDirBlock
= (LongBlock*)fPathForLeaves[pathIndex].blockData;
if (curDirBlock->Magic() != XFS_BMAP_MAGIC)
return B_BAD_VALUE;
SearchForMapInDirectoryBlock(rightOffset, curDirBlock->NumRecs(),
&targetMap, LEAF, pathIndex);
if (targetMap == NULL)
return B_BAD_VALUE;
FillBuffer(fSingleDirBlock, rightOffset - targetMap->br_startoff,
targetMap);
fOffsetOfSingleDirBlock = rightOffset;
ExtentLeafHeader* dirBlock = (ExtentLeafHeader*)fSingleDirBlock;
if (B_BENDIAN_TO_HOST_INT16(dirBlock->info.magic)
== XFS_DIR2_LEAFN_MAGIC) {
// Got the potential leaf. Break.
break;
}
if (B_BENDIAN_TO_HOST_INT16(dirBlock->info.magic)
== XFS_DA_NODE_MAGIC) {
rightOffset = SearchForHashInNodeBlock(hashValueOfRequest);
if (rightOffset == 0)
return B_ENTRY_NOT_FOUND;
continue;
}
}
// We now have the leaf block that might contain the entry we need.
// Else go to the right subling if it might contain it. Else break.
while (1) {
ExtentLeafHeader* leafHeader
= (ExtentLeafHeader*)fSingleDirBlock;
ExtentLeafEntry* leafEntry
= (ExtentLeafEntry*)(fSingleDirBlock + sizeof(ExtentLeafHeader));
int numberOfLeafEntries = B_BENDIAN_TO_HOST_INT16(leafHeader->count);
TRACE("numberOfLeafEntries:(%d)\n", numberOfLeafEntries);
int left = 0;
int mid;
int right = numberOfLeafEntries - 1;
// Trying to find the lowerbound of hashValueOfRequest
// This is slightly different from bsearch(), as we want the first
// instance of hashValueOfRequest and not any instance.
while (left < right) {
mid = (left + right) / 2;
uint32 hashval = B_BENDIAN_TO_HOST_INT32(leafEntry[mid].hashval);
if (hashval >= hashValueOfRequest) {
right = mid;
continue;
}
if (hashval < hashValueOfRequest) {
left = mid + 1;
}
}
TRACE("left:(%d), right:(%d)\n", left, right);
uint32 nextLeaf = B_BENDIAN_TO_HOST_INT32(leafHeader->info.forw);
uint32 lastHashVal = B_BENDIAN_TO_HOST_INT32(
leafEntry[numberOfLeafEntries - 1].hashval);
while (B_BENDIAN_TO_HOST_INT32(leafEntry[left].hashval)
== hashValueOfRequest) {
uint32 address = B_BENDIAN_TO_HOST_INT32(leafEntry[left].address);
if (address == 0) {
left++;
continue;
}
uint32 dataBlockNumber = BLOCKNO_FROM_ADDRESS(address * 8, volume);
uint32 offset = BLOCKOFFSET_FROM_ADDRESS(address * 8, fInode);
TRACE("BlockNumber:(%d), offset:(%d)\n", dataBlockNumber, offset);
status = SearchAndFillPath(dataBlockNumber, DATA);
int pathIndex = -1;
for (int i = 0; i < MAX_TREE_DEPTH; i++) {
if (fPathForData[i].type == 2) {
pathIndex = i;
break;
}
}
if (pathIndex == -1)
return B_BAD_VALUE;
LongBlock* curDirBlock
= (LongBlock*)fPathForData[pathIndex].blockData;
SearchForMapInDirectoryBlock(dataBlockNumber,
curDirBlock->NumRecs(), &targetMap, DATA, pathIndex);
if (targetMap == NULL)
return B_BAD_VALUE;
FillBuffer(fSingleDirBlock,
dataBlockNumber - targetMap->br_startoff, targetMap);
fOffsetOfSingleDirBlock = dataBlockNumber;
TRACE("offset:(%d)\n", offset);
ExtentDataEntry* entry
= (ExtentDataEntry*)(fSingleDirBlock + offset);
int retVal = strncmp(name, (char*)entry->name, entry->namelen);
if (retVal == 0) {
*ino = B_BENDIAN_TO_HOST_INT64(entry->inumber);
TRACE("ino:(%d)\n", *ino);
return B_OK;
}
left++;
}
if (lastHashVal == hashValueOfRequest && nextLeaf != -1) {
// Go to forward neighbor. We might find an entry there.
status = SearchAndFillPath(nextLeaf, LEAF);
if (status != B_OK)
return status;
pathIndex = -1;
for (int i = 0; i < MAX_TREE_DEPTH; i++) {
if (fPathForLeaves[i].type == 2) {
pathIndex = i;
break;
}
}
if (pathIndex == -1)
return B_BAD_VALUE;
LongBlock* curDirBlock
= (LongBlock*)fPathForLeaves[pathIndex].blockData;
SearchForMapInDirectoryBlock(nextLeaf, curDirBlock->NumRecs(),
&targetMap, LEAF, pathIndex);
if (targetMap == NULL)
return B_BAD_VALUE;
FillBuffer(fSingleDirBlock,
nextLeaf - targetMap->br_startoff, targetMap);
fOffsetOfSingleDirBlock = nextLeaf;
continue;
} else {
break;
}
}
return B_ENTRY_NOT_FOUND;
}

View File

@ -20,6 +20,7 @@
#define XFS_KEY_SIZE sizeof(xfs_fileoff_t)
#define XFS_PTR_SIZE sizeof(xfs_fsblock_t)
#define XFS_BMAP_MAGIC 0x424d4150
#define MAX_TREE_DEPTH 5
typedef xfs_fileoff_t TreeKey;
@ -61,7 +62,7 @@ struct LongBlock {
*/
//xfs_bmdr_block
// xfs_bmdr_block
struct BlockInDataFork {
uint16 Levels()
{ return
@ -80,6 +81,22 @@ struct ExtentMapUnwrap {
};
/*
* Using the structure to prevent re-reading of already read blocks during
* a traversal of tree.
*
* type:
* 0, if its an unused node, 1 if blockData size is a single block,
* 2 if blockData size is directory block size.
*/
struct PathNode {
int type;
char* blockData;
uint32 blockNumber;
// This is the file system block number
};
/*
* This class should handle B+Tree based directories
*/
@ -96,13 +113,29 @@ public:
int BlockLen();
size_t PtrSize();
size_t KeySize();
TreeKey GetKey(int pos);
// get the pos'th key
TreePointer* GetPtr(int pos, LongBlock* pointer);
// get the pos'th pointer
TreeKey* GetKeyFromNode(int pos, void* buffer);
TreePointer* GetPtrFromNode(int pos, void* buffer);
TreeKey* GetKeyFromRoot(int pos);
TreePointer* GetPtrFromRoot(int pos);
status_t SearchMapInAllExtent(int blockNo,
uint32& mapIndex);
status_t GetAllExtents();
size_t MaxRecordsPossible(size_t len);
size_t MaxRecordsPossibleRoot();
size_t MaxRecordsPossibleNode();
void FillMapEntry(int num, ExtentMapEntry** map,
int type, int pathIndex);
status_t FillBuffer(char* blockBuffer,
int howManyBlocksFurther,
ExtentMapEntry* targetMap = NULL);
size_t GetPtrOffsetIntoNode(int pos);
size_t GetPtrOffsetIntoRoot(int pos);
status_t SearchAndFillPath(uint32 offset, int type);
status_t SearchOffsetInTreeNode (uint32 offset,
TreePointer** pointer, int pathIndex);
void SearchForMapInDirectoryBlock (int blockNo,
int entries, ExtentMapEntry** map,
int type, int pathIndex);
uint32 SearchForHashInNodeBlock(uint32 hashVal);
private:
inline status_t UnWrapExtents(ExtentMapUnwrap* extentsWrapped);
@ -113,6 +146,12 @@ private:
ExtentMapEntry* fExtents;
uint32 fCountOfFilledExtents;
char* fSingleDirBlock;
uint32 fOffsetOfSingleDirBlock;
uint32 fCurMapIndex;
uint64 fOffset;
uint32 fCurBlockNumber;
PathNode fPathForLeaves[MAX_TREE_DEPTH];
PathNode fPathForData[MAX_TREE_DEPTH];
};

View File

@ -34,13 +34,14 @@ DirectoryIterator::Init()
{
if (fInode->Format() == XFS_DINODE_FMT_LOCAL)
{
TRACE("Iterator:Init: LOCAL");
fShortDir = new(std::nothrow) ShortDirectory(fInode);
if (fShortDir == NULL)
return B_NO_MEMORY;
return B_OK;
}
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
// TODO: Only working with Block directories, not leaf.
TRACE("Iterator:Init: EXTENTS");
fExtentDir = new(std::nothrow) Extent(fInode);
if (fExtentDir == NULL)
return B_NO_MEMORY;
@ -76,10 +77,8 @@ DirectoryIterator::Init()
/* Return B_OK so even if the shortform directory has an extent directory
* we can atleast still list the shortform directory
*/
//TODO: Reading from B+Trees based directories
if (fInode->Format() == XFS_DINODE_FMT_BTREE) {
TRACE("Iterator:GetNext: B+TREE");
TRACE("Iterator:Init(): B+TREE");
fTreeDir = new(std::nothrow) TreeDirectory(fInode);
if (fTreeDir == NULL)
return B_NO_MEMORY;
@ -95,11 +94,11 @@ DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
{
status_t status;
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
TRACE("Iterator:GetNext: LOCAL");
status = fShortDir->GetNext(name, length, ino);
return status;
}
//TODO: Reading from extent based directories
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
TRACE("Iterator:GetNext: EXTENTS");
if (fExtentDir != NULL)
@ -111,9 +110,10 @@ DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
return status;
}
//TODO: Reading from B+Trees based directories
if (fInode->Format() == XFS_DINODE_FMT_BTREE) {
TRACE("Iterator:GetNext: B+TREE");
if (fTreeDir != NULL)
return status = fTreeDir->GetNext(name, length, ino);
return B_NOT_SUPPORTED;
}
@ -127,13 +127,14 @@ DirectoryIterator::Lookup(const char* name, size_t length, xfs_ino_t* ino)
{
status_t status;
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
TRACE("Iterator:Lookup: LOCAL\n");
status = fShortDir->Lookup(name, length, ino);
return status;
}
//TODO: Reading from extent based dirs
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
TRACE("Iterator:Lookup: EXTENTS");
TRACE("Iterator:Lookup: EXTENTS\n");
if (fExtentDir != NULL)
status = fExtentDir->Lookup(name, length, ino);
else if (fLeafDir != NULL)
@ -145,7 +146,9 @@ DirectoryIterator::Lookup(const char* name, size_t length, xfs_ino_t* ino)
//TODO: Reading from B+Tree based dirs
if (fInode->Format() == XFS_DINODE_FMT_BTREE) {
TRACE("Iterator:Lookup: B+TREE");
TRACE("Iterator:Lookup: B+TREE\n");
if (fTreeDir != NULL)
return fTreeDir->Lookup(name, length, ino);
return B_NOT_SUPPORTED;
}

View File

@ -149,6 +149,10 @@ Extent::GetNext(char* name, size_t* length, xfs_ino_t* ino)
// This could be an unused entry so we should check
int numberOfEntries = B_BENDIAN_TO_HOST_INT32(BlockTail()->count);
int numberOfStaleEntries = B_BENDIAN_TO_HOST_INT32(BlockTail()->stale);
// We don't read stale entries.
numberOfEntries -= numberOfStaleEntries;
TRACE("numberOfEntries:(%d)\n", numberOfEntries);
uint16 currentOffset = (char*)entry - fBlockBuffer;
@ -161,6 +165,7 @@ Extent::GetNext(char* name, size_t* length, xfs_ino_t* ino)
currentOffset += B_BENDIAN_TO_HOST_INT16(unusedEntry->length);
entry = (void*)
((char*)entry + B_BENDIAN_TO_HOST_INT16(unusedEntry->length));
i--;
continue;
}
ExtentDataEntry* dataEntry = (ExtentDataEntry*) entry;

View File

@ -120,14 +120,14 @@ LeafDirectory::FillBuffer(int type, char* blockBuffer, int howManyBlocksFurthur)
if (read_pos(fInode->GetVolume()->Device(), readPos, blockBuffer, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
ERROR("LeafDirectory::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
if (type == DATA) {
fDataBuffer = blockBuffer;
ExtentDataHeader* header = (ExtentDataHeader*) fDataBuffer;
if (B_BENDIAN_TO_HOST_INT32(header->magic) == HEADER_MAGIC)
if (B_BENDIAN_TO_HOST_INT32(header->magic) == DATA_HEADER_MAGIC)
TRACE("DATA BLOCK VALID\n");
else {
TRACE("DATA BLOCK INVALID\n");

View File

@ -11,7 +11,7 @@
#include "system_dependencies.h"
#define HEADER_MAGIC 0x58443244
#define DATA_HEADER_MAGIC 0x58443244
enum ContentType { DATA, LEAF };

View File

@ -106,14 +106,14 @@ NodeDirectory::FillBuffer(int type, char* blockBuffer, int howManyBlocksFurthur)
if (read_pos(fInode->GetVolume()->Device(), readPos, blockBuffer, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
ERROR("NodeDirectory::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
if (type == DATA) {
fDataBuffer = blockBuffer;
ExtentDataHeader* header = (ExtentDataHeader*) fDataBuffer;
if (B_BENDIAN_TO_HOST_INT32(header->magic) == HEADER_MAGIC) {
if (B_BENDIAN_TO_HOST_INT32(header->magic) == DATA_HEADER_MAGIC) {
TRACE("DATA BLOCK VALID\n");
} else {
TRACE("DATA BLOCK INVALID\n");

View File

@ -37,7 +37,7 @@ public:
bool IsNodeType();
void FillMapEntry(int num, ExtentMapEntry* map);
status_t FillBuffer(int type, char* buffer,
int howManyBlocksFurthur);
int howManyBlocksFurther);
void SearchAndFillDataMap(int blockNo);
uint32 FindHashInNode(uint32 hashVal);
uint32 GetOffsetFromAddress(uint32 address);

View File

@ -12,8 +12,8 @@
#include "fssh_api_wrapper.h"
#include "fssh_auto_deleter.h"
#include "fssh_kernel_priv.h"
#include "Debug.h"
#ifdef __cplusplus
extern "C"
{
@ -47,6 +47,7 @@ extern "C"
#include <fs_volume.h>
#include "Debug.h"
#include <Drivers.h>
#include <kernel.h>
#include <KernelExport.h>
#include <NodeMonitor.h>
#include <SupportDefs.h>