xfs: B+Tree ExtentMap reading

We can now read all the extent maps from the leaves. This will be needed
to implement the GetNext() functionality.

Change-Id: Ie10b453c33bec6e6d3109743d695f86a90de45cd
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3119
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-02 13:09:08 +05:30 committed by Adrien Destugues
parent d9b9a94c4f
commit b4763972bd
6 changed files with 277 additions and 198 deletions

View File

@ -7,122 +7,219 @@
#include "BPlusTree.h"
void
bplustree_short_block::SwapEndian()
TreeDirectory::TreeDirectory(Inode* inode)
:
fInode(inode),
fInitStatus(B_OK),
fRoot(NULL),
fExtents(NULL),
fSingleDirBlock(NULL),
fCountOfFilledExtents(0)
{
bb_magic = B_BENDIAN_TO_HOST_INT32(bb_magic);
bb_level = B_BENDIAN_TO_HOST_INT16(bb_level);
bb_numrecs = B_BENDIAN_TO_HOST_INT16(bb_numrecs);
bb_leftsib = B_BENDIAN_TO_HOST_INT32(bb_leftsib);
bb_rightsib = B_BENDIAN_TO_HOST_INT32(bb_rightsib);
}
void
bplustree_long_block::SwapEndian()
{
bb_magic = B_BENDIAN_TO_HOST_INT32(bb_magic);
bb_level = B_BENDIAN_TO_HOST_INT16(bb_level);
bb_numrecs = B_BENDIAN_TO_HOST_INT16(bb_numrecs);
bb_leftsib = B_BENDIAN_TO_HOST_INT64(bb_leftsib);
bb_rightsib = B_BENDIAN_TO_HOST_INT64(bb_rightsib);
}
void
xfs_alloc_rec::SwapEndian()
{
ar_startblock = B_BENDIAN_TO_HOST_INT32(ar_startblock);
ar_blockcount = B_BENDIAN_TO_HOST_INT32(ar_blockcount);
}
uint32
BPlusTree::BlockSize()
{
return fVolume.SuperBlock().BlockSize();
}
int
BPlusTree::RecordSize()
{
if (fRecType == ALLOC_FLAG)
return XFS_ALLOC_REC_SIZE;
return B_BAD_VALUE;
}
int
BPlusTree::MaxRecords(bool leaf)
{
int blockLen = BlockSize();
if (fPtrType == SHORT_BLOCK_FLAG)
blockLen - XFS_BTREE_SBLOCK_SIZE;
if (leaf) {
if (fRecType == ALLOC_FLAG)
return blockLen / sizeof(xfs_alloc_rec_t);
} else {
if (fKeyType == ALLOC_FLAG) {
return blockLen / (sizeof(xfs_alloc_key_t)
+ sizeof(xfs_alloc_ptr_t));
}
fRoot = new(std::nothrow) BlockInDataFork;
if (fRoot == NULL) {
fInitStatus = B_NO_MEMORY;
return;
}
return B_BAD_VALUE;
fSingleDirBlock = new(std::nothrow) char[fInode->DirBlockSize()];
if (fSingleDirBlock == NULL) {
fInitStatus = B_NO_MEMORY;
return;
}
memcpy((void*)fRoot,
DIR_DFORK_PTR(fInode->Buffer()), sizeof(BlockInDataFork));
}
TreeDirectory::~TreeDirectory()
{
delete fRoot;
delete[] fExtents;
delete fSingleDirBlock;
}
status_t
TreeDirectory::InitCheck()
{
return fInitStatus;
}
int
BPlusTree::KeyLen()
TreeDirectory::BlockLen()
{
if (fKeyType == ALLOC_FLAG)
return XFS_ALLOC_REC_SIZE;
return B_BAD_VALUE;
return XFS_BTREE_LBLOCK_SIZE;
}
int
BPlusTree::BlockLen()
TreeKey
TreeDirectory::GetKey(int pos)
{
if (fPtrType == LONG_BLOCK_FLAG)
return XFS_BTREE_LBLOCK_SIZE;
if (fPtrType == SHORT_BLOCK_FLAG)
return XFS_BTREE_SBLOCK_SIZE;
return B_BAD_VALUE;
return BlockLen() + (pos - 1) * XFS_KEY_SIZE;
}
int
BPlusTree::PtrLen()
size_t
TreeDirectory::KeySize()
{
if (fPtrType == LONG_BLOCK_FLAG)
return sizeof(uint64);
return XFS_KEY_SIZE;
}
size_t
TreeDirectory::PtrSize()
{
return XFS_PTR_SIZE;
}
TreePointer*
TreeDirectory::GetPtr(int pos, LongBlock* curLongBlock)
{
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
TreeDirectory::MaxRecordsPossible(size_t len)
{
len -= sizeof(BlockInDataFork);
return len / (KeySize() + PtrSize());
}
status_t
TreeDirectory::GetAllExtents()
{
xfs_extnum_t noOfExtents = fInode->DataExtentsCount();
ExtentMapUnwrap* extentsWrapped
= new(std::nothrow) ExtentMapUnwrap[noOfExtents];
if (extentsWrapped == NULL)
return B_NO_MEMORY;
Volume* volume = fInode->GetVolume();
uint16 levelsInTree = fRoot->Levels();
size_t lengthOfDataFork;
if (fInode->ForkOffset() != 0)
lengthOfDataFork = fInode->ForkOffset() << 3;
else
return sizeof(uint32);
lengthOfDataFork = volume->InodeSize() - INODE_CORE_UNLINKED_SIZE;
TRACE("Length Of Data Fork: (%d)\n", lengthOfDataFork);
size_t maxRecords = MaxRecordsPossible(lengthOfDataFork);
TRACE("Maxrecords: (%d)\n", maxRecords);
TreePointer* ptrToNode = (TreePointer*)
((char*)DIR_DFORK_PTR(fInode->Buffer())
+ sizeof(BlockInDataFork) + maxRecords*KeySize());
size_t len = volume->BlockSize();
char node[len];
// This isn't for a directory block but for one of the tree nodes
TRACE("levels:(%d)\n", levelsInTree);
TRACE("Numrecs:(%d)\n", fRoot->NumRecords());
// Go down the tree by taking the leftest pointer to go to the first leaf
uint64 fileSystemBlockNo = B_BENDIAN_TO_HOST_INT64(*ptrToNode);
uint64 readPos = fInode->FileSystemBlockToAddr(fileSystemBlockNo);
while (levelsInTree != 1) {
fileSystemBlockNo = B_BENDIAN_TO_HOST_INT64(*ptrToNode);
// The fs block that contains node at next lower level. Now read.
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);
// Get's the first pointer. This points to next node.
levelsInTree--;
}
// Next level wil contain leaf nodes. Now Read Directory Buffer
len = fInode->DirBlockSize();
if (read_pos(volume->Device(), readPos, fSingleDirBlock, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
delete[] extentsWrapped;
extentsWrapped = NULL;
return B_IO_ERROR;
}
levelsInTree--;
ASSERT(levelsInTree == 0);
// We should be at the left most leaf node.
// This could be a multilevel node type directory
while (1) {
// Run till you have leaf blocks to checkout
char* leafBuffer = fSingleDirBlock;
ASSERT(((LongBlock*)leafBuffer)->Magic() == XFS_BMAP_MAGIC);
uint32 offset = sizeof(LongBlock);
int numRecs = ((LongBlock*)leafBuffer)->NumRecs();
for (int i = 0; i < numRecs; i++) {
extentsWrapped[fCountOfFilledExtents].first =
*(uint64*)(leafBuffer + offset);
extentsWrapped[fCountOfFilledExtents].second =
*(uint64*)(leafBuffer + offset + sizeof(uint64));
offset += sizeof(ExtentMapUnwrap);
fCountOfFilledExtents++;
}
fileSystemBlockNo = ((LongBlock*)leafBuffer)->Right();
TRACE("Next leaf is at: (%d)\n", fileSystemBlockNo);
if (fileSystemBlockNo == -1)
break;
uint64 readPos = fInode->FileSystemBlockToAddr(fileSystemBlockNo);
if (read_pos(volume->Device(), readPos, fSingleDirBlock, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
delete[] extentsWrapped;
extentsWrapped = NULL;
return B_IO_ERROR;
}
}
TRACE("Total covered: (%d)\n", fCountOfFilledExtents);
status_t status = UnWrapExtents(extentsWrapped);
delete[] extentsWrapped;
extentsWrapped = NULL;
return status;
}
int
BPlusTree::RecordOffset(int pos)
status_t
TreeDirectory::UnWrapExtents(ExtentMapUnwrap* extentsWrapped)
{
return BlockLen() + (pos - 1) * RecordSize();
}
int
BPlusTree::KeyOffset(int pos)
{
return BlockLen() + (pos - 1) * KeyLen();
}
int
BPlusTree::PtrOffset(int pos, int level)
{
return BlockLen() + MaxRecords(level > 0) * KeyLen()
+ (pos - 1) * PtrLen();
fExtents = new(std::nothrow) ExtentMapEntry[fCountOfFilledExtents];
if (fExtents == NULL)
return B_NO_MEMORY;
uint64 first, second;
for (int i = 0; i < fCountOfFilledExtents; i++) {
first = B_BENDIAN_TO_HOST_INT64(extentsWrapped[i].first);
second = B_BENDIAN_TO_HOST_INT64(extentsWrapped[i].second);
fExtents[i].br_state = first >> 63;
fExtents[i].br_startoff = (first & MASK(63)) >> 9;
fExtents[i].br_startblock = ((first & MASK(9)) << 43) | (second >> 21);
fExtents[i].br_blockcount = second & MASK(21);
}
return B_OK;
}

View File

@ -5,136 +5,115 @@
#ifndef _BPLUS_TREE_H_
#define _BPLUS_TREE_H_
#include "Extent.h"
#include "Inode.h"
#include "LeafDirectory.h"
#include "Node.h"
#include "system_dependencies.h"
/* Allocation B+ Tree Format */
#define XFS_ABTB_MAGICNUM 0x41425442
// For block offset B+Tree
#define XFS_ABTC_MAGICNUM 0x41425443
// For block count B+ Tree
#define XFS_BTREE_SBLOCK_SIZE 18
// Header for Short Format btree
#define XFS_BTREE_LBLOCK_SIZE 24
// Header for Long Format btree
#define XFS_KEY_SIZE sizeof(xfs_fileoff_t)
#define XFS_PTR_SIZE sizeof(xfs_fsblock_t)
#define XFS_BMAP_MAGIC 0x424d4150
typedef xfs_fileoff_t TreeKey;
typedef xfs_fsblock_t TreePointer;
/*
* Headers are the "nodes" really and are called "blocks". The records, keys
* and ptrs are calculated using helpers
* Headers(here, the LongBlock) are the "nodes" really and are called "blocks".
* The records, keys and ptrs are calculated using helpers
*/
struct bplustree_short_block {
void SwapEndian();
struct LongBlock {
uint32 Magic()
{ return bb_magic; }
{ return B_BENDIAN_TO_HOST_INT32(bb_magic); }
uint16 Level()
{ return bb_level; }
{ return B_BENDIAN_TO_HOST_INT16(bb_level); }
uint16 NumRecs()
{ return bb_numrecs; }
{ return B_BENDIAN_TO_HOST_INT16(bb_numrecs); }
xfs_alloc_ptr_t Left()
{ return bb_leftsib; }
TreePointer Left()
{ return B_BENDIAN_TO_HOST_INT64(bb_leftsib); }
xfs_alloc_ptr_t Right()
{ return bb_rightsib;}
uint32 bb_magic;
uint16 bb_level;
uint16 bb_numrecs;
uint32 bb_leftsib;
uint32 bb_rightsib;
}
struct bplustree_long_block {
void SwapEndian();
uint32 Magic()
{ return bb_magic; }
uint16 Level()
{ return bb_level; }
uint16 NumRecs()
{ return bb_numrecs; }
xfs_alloc_ptr_t Left()
{ return bb_leftsib; }
xfs_alloc_ptr_t Right()
{ return bb_rightsib;}
TreePointer Right()
{ return B_BENDIAN_TO_HOST_INT64(bb_rightsib); }
uint32 bb_magic;
uint16 bb_level;
uint16 bb_numrecs;
uint64 bb_leftsib;
uint64 bb_rightsib;
}
};
/* Array of these records in the leaf node along with above headers */
#define XFS_ALLOC_REC_SIZE 8
typedef struct xfs_alloc_rec {
void SwapEndian();
uint32 StartBlock()
{ return ar_startblock; }
uint32 BlockCount()
{ return ar_blockcount; }
uint32 ar_startblock;
uint32 ar_blockcount;
} xfs_alloc_rec_t, xfs_alloc_key_t;
/* We have an array of extent records in
* the leaf node along with above headers
* The behaviour is very much like node directories.
*/
typedef uint32 xfs_alloc_ptr_t;
// Node pointers, AG relative block pointer
#define ALLOC_FLAG 0x1
#define LONG_BLOCK_FLAG 0x1
#define SHORT_BLOCK_FLAG 0x2
union btree_ptr {
bplustree_long_block fLongBlock;
bplustree_short_block fShortBlock;
}
//xfs_bmdr_block
struct BlockInDataFork {
uint16 Levels()
{ return
B_BENDIAN_TO_HOST_INT16(bb_level); }
uint16 NumRecords()
{ return
B_BENDIAN_TO_HOST_INT16(bb_numrecs); }
uint16 bb_level;
uint16 bb_numrecs;
};
union btree_key {
xfs_alloc_key_t fAlloc;
}
struct ExtentMapUnwrap {
uint64 first;
uint64 second;
};
union btree_rec {
xfs_alloc_rec_t fAlloc;
}
class BPlusTree {
/*
* This class should handle B+Tree based directories
*/
class TreeDirectory {
public:
uint32 BlockSize();
int RecordSize();
int MaxRecords(bool leaf);
int KeyLen();
TreeDirectory(Inode* inode);
~TreeDirectory();
status_t InitCheck();
status_t GetNext(char* name, size_t* length,
xfs_ino_t* ino);
status_t Lookup(const char* name, size_t length,
xfs_ino_t* id);
int EntrySize(int len) const;
int BlockLen();
int PtrLen();
int RecordOffset(int pos); // get the pos'th record
int KeyOffset(int pos); // get the pos'th key
int PtrOffset(int pos); // get the pos'th ptr
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
status_t GetAllExtents();
size_t MaxRecordsPossible(size_t len);
private:
Volume* fVolume;
xfs_agnumber_t fAgnumber;
btree_ptr* fRoot;
int fRecType;
int fKeyType;
int fPtrType;
}
inline status_t UnWrapExtents(ExtentMapUnwrap* extentsWrapped);
private:
Inode* fInode;
status_t fInitStatus;
BlockInDataFork* fRoot;
ExtentMapEntry* fExtents;
uint32 fCountOfFilledExtents;
char* fSingleDirBlock;
};
#endif

View File

@ -13,7 +13,8 @@ DirectoryIterator::DirectoryIterator(Inode* inode)
fShortDir(NULL),
fExtentDir(NULL),
fLeafDir(NULL),
fNodeDir(NULL)
fNodeDir(NULL),
fTreeDir(NULL)
{
}
@ -24,6 +25,7 @@ DirectoryIterator::~DirectoryIterator()
delete fLeafDir;
delete fExtentDir;
delete fNodeDir;
delete fTreeDir;
}
@ -75,16 +77,13 @@ DirectoryIterator::Init()
* we can atleast still list the shortform directory
*/
//TODO: Reading from extent based directories
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
TRACE("Iterator:GetNext: EXTENTS");
return B_OK;
}
//TODO: Reading from B+Trees based directories
if (fInode->Format() == XFS_DINODE_FMT_BTREE) {
TRACE("Iterator:GetNext: B+TREE");
return B_OK;
fTreeDir = new(std::nothrow) TreeDirectory(fInode);
if (fTreeDir == NULL)
return B_NO_MEMORY;
return fTreeDir->InitCheck();
}
return B_BAD_VALUE;

View File

@ -6,6 +6,7 @@
#define _DIRECTORY_H_
#include "BPlusTree.h"
#include "Extent.h"
#include "Inode.h"
#include "LeafDirectory.h"
@ -38,6 +39,7 @@ private:
LeafDirectory* fLeafDir;
// Extent based leaf directory
NodeDirectory* fNodeDir;
TreeDirectory* fTreeDir;
};

View File

@ -20,6 +20,7 @@ DEFINES += DEBUG_APP="\\\"xfs\\\"" ;
UseHeaders [ FDirName $(HAIKU_TOP) src libs uuid ] : true ;
local xfsSources =
BPlusTree.cpp
DeviceOpener.cpp
Directory.cpp
Extent.cpp

View File

@ -39,6 +39,7 @@ UseHeaders [ FDirName $(HAIKU_TOP) headers private ] : true ;
UseHeaders [ FDirName $(HAIKU_TOP) src tools fs_shell ] ;
local xfsSource =
BPlusTree.cpp
DeviceOpener.cpp
Directory.cpp
Extent.cpp