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 <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
CruxBox
2021-06-04 12:50:56 +00:00
committed by Adrien Destugues
parent d9b9a94c4f
commit b4763972bd
6 changed files with 277 additions and 198 deletions
+194 -97
View File
@@ -7,122 +7,219 @@
#include "BPlusTree.h" #include "BPlusTree.h"
void TreeDirectory::TreeDirectory(Inode* inode)
bplustree_short_block::SwapEndian() :
fInode(inode),
fInitStatus(B_OK),
fRoot(NULL),
fExtents(NULL),
fSingleDirBlock(NULL),
fCountOfFilledExtents(0)
{ {
bb_magic = B_BENDIAN_TO_HOST_INT32(bb_magic); fRoot = new(std::nothrow) BlockInDataFork;
bb_level = B_BENDIAN_TO_HOST_INT16(bb_level); if (fRoot == NULL) {
bb_numrecs = B_BENDIAN_TO_HOST_INT16(bb_numrecs); fInitStatus = B_NO_MEMORY;
bb_leftsib = B_BENDIAN_TO_HOST_INT32(bb_leftsib); return;
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));
}
} }
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 int
BPlusTree::KeyLen() TreeDirectory::BlockLen()
{ {
if (fKeyType == ALLOC_FLAG) return XFS_BTREE_LBLOCK_SIZE;
return XFS_ALLOC_REC_SIZE;
return B_BAD_VALUE;
} }
int TreeKey
BPlusTree::BlockLen() TreeDirectory::GetKey(int pos)
{ {
if (fPtrType == LONG_BLOCK_FLAG) return BlockLen() + (pos - 1) * XFS_KEY_SIZE;
return XFS_BTREE_LBLOCK_SIZE;
if (fPtrType == SHORT_BLOCK_FLAG)
return XFS_BTREE_SBLOCK_SIZE;
return B_BAD_VALUE;
} }
int size_t
BPlusTree::PtrLen() TreeDirectory::KeySize()
{ {
if (fPtrType == LONG_BLOCK_FLAG) return XFS_KEY_SIZE;
return sizeof(uint64); }
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 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 status_t
BPlusTree::RecordOffset(int pos) TreeDirectory::UnWrapExtents(ExtentMapUnwrap* extentsWrapped)
{ {
return BlockLen() + (pos - 1) * RecordSize(); fExtents = new(std::nothrow) ExtentMapEntry[fCountOfFilledExtents];
} if (fExtents == NULL)
return B_NO_MEMORY;
uint64 first, second;
int
BPlusTree::KeyOffset(int pos) for (int i = 0; i < fCountOfFilledExtents; i++) {
{ first = B_BENDIAN_TO_HOST_INT64(extentsWrapped[i].first);
return BlockLen() + (pos - 1) * KeyLen(); 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);
int fExtents[i].br_blockcount = second & MASK(21);
BPlusTree::PtrOffset(int pos, int level) }
{
return BlockLen() + MaxRecords(level > 0) * KeyLen() return B_OK;
+ (pos - 1) * PtrLen();
} }
+72 -93
View File
@@ -5,136 +5,115 @@
#ifndef _BPLUS_TREE_H_ #ifndef _BPLUS_TREE_H_
#define _BPLUS_TREE_H_ #define _BPLUS_TREE_H_
#include "Extent.h"
#include "Inode.h"
#include "LeafDirectory.h"
#include "Node.h"
#include "system_dependencies.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 #define XFS_BTREE_SBLOCK_SIZE 18
// Header for Short Format btree // Header for Short Format btree
#define XFS_BTREE_LBLOCK_SIZE 24 #define XFS_BTREE_LBLOCK_SIZE 24
// Header for Long Format btree // 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 * Headers(here, the LongBlock) are the "nodes" really and are called "blocks".
* and ptrs are calculated using helpers * The records, keys and ptrs are calculated using helpers
*/ */
struct bplustree_short_block { struct LongBlock {
void SwapEndian();
uint32 Magic() uint32 Magic()
{ return bb_magic; } { return B_BENDIAN_TO_HOST_INT32(bb_magic); }
uint16 Level() uint16 Level()
{ return bb_level; } { return B_BENDIAN_TO_HOST_INT16(bb_level); }
uint16 NumRecs() uint16 NumRecs()
{ return bb_numrecs; } { return B_BENDIAN_TO_HOST_INT16(bb_numrecs); }
xfs_alloc_ptr_t Left() TreePointer Left()
{ return bb_leftsib; } { return B_BENDIAN_TO_HOST_INT64(bb_leftsib); }
xfs_alloc_ptr_t Right() TreePointer Right()
{ return bb_rightsib;} { return B_BENDIAN_TO_HOST_INT64(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;}
uint32 bb_magic; uint32 bb_magic;
uint16 bb_level; uint16 bb_level;
uint16 bb_numrecs; uint16 bb_numrecs;
uint64 bb_leftsib; uint64 bb_leftsib;
uint64 bb_rightsib; uint64 bb_rightsib;
} };
/* Array of these records in the leaf node along with above headers */ /* We have an array of extent records in
#define XFS_ALLOC_REC_SIZE 8 * the leaf node along with above headers
typedef struct xfs_alloc_rec { * The behaviour is very much like node directories.
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;
typedef uint32 xfs_alloc_ptr_t; //xfs_bmdr_block
// Node pointers, AG relative block pointer struct BlockInDataFork {
uint16 Levels()
#define ALLOC_FLAG 0x1 { return
#define LONG_BLOCK_FLAG 0x1 B_BENDIAN_TO_HOST_INT16(bb_level); }
#define SHORT_BLOCK_FLAG 0x2 uint16 NumRecords()
{ return
union btree_ptr { B_BENDIAN_TO_HOST_INT16(bb_numrecs); }
bplustree_long_block fLongBlock; uint16 bb_level;
bplustree_short_block fShortBlock; uint16 bb_numrecs;
} };
union btree_key { struct ExtentMapUnwrap {
xfs_alloc_key_t fAlloc; uint64 first;
} uint64 second;
};
union btree_rec { /*
xfs_alloc_rec_t fAlloc; * This class should handle B+Tree based directories
} */
class TreeDirectory {
class BPlusTree {
public: public:
uint32 BlockSize(); TreeDirectory(Inode* inode);
int RecordSize(); ~TreeDirectory();
int MaxRecords(bool leaf); status_t InitCheck();
int KeyLen(); 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 BlockLen();
int PtrLen(); size_t PtrSize();
int RecordOffset(int pos); // get the pos'th record size_t KeySize();
int KeyOffset(int pos); // get the pos'th key TreeKey GetKey(int pos);
int PtrOffset(int pos); // get the pos'th ptr // 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: private:
Volume* fVolume; inline status_t UnWrapExtents(ExtentMapUnwrap* extentsWrapped);
xfs_agnumber_t fAgnumber;
btree_ptr* fRoot; private:
int fRecType; Inode* fInode;
int fKeyType; status_t fInitStatus;
int fPtrType; BlockInDataFork* fRoot;
} ExtentMapEntry* fExtents;
uint32 fCountOfFilledExtents;
char* fSingleDirBlock;
};
#endif #endif
@@ -13,7 +13,8 @@ DirectoryIterator::DirectoryIterator(Inode* inode)
fShortDir(NULL), fShortDir(NULL),
fExtentDir(NULL), fExtentDir(NULL),
fLeafDir(NULL), fLeafDir(NULL),
fNodeDir(NULL) fNodeDir(NULL),
fTreeDir(NULL)
{ {
} }
@@ -24,6 +25,7 @@ DirectoryIterator::~DirectoryIterator()
delete fLeafDir; delete fLeafDir;
delete fExtentDir; delete fExtentDir;
delete fNodeDir; delete fNodeDir;
delete fTreeDir;
} }
@@ -75,16 +77,13 @@ DirectoryIterator::Init()
* we can atleast still list the shortform directory * 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 //TODO: Reading from B+Trees based directories
if (fInode->Format() == XFS_DINODE_FMT_BTREE) { if (fInode->Format() == XFS_DINODE_FMT_BTREE) {
TRACE("Iterator:GetNext: B+TREE"); 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; return B_BAD_VALUE;
@@ -6,6 +6,7 @@
#define _DIRECTORY_H_ #define _DIRECTORY_H_
#include "BPlusTree.h"
#include "Extent.h" #include "Extent.h"
#include "Inode.h" #include "Inode.h"
#include "LeafDirectory.h" #include "LeafDirectory.h"
@@ -38,6 +39,7 @@ private:
LeafDirectory* fLeafDir; LeafDirectory* fLeafDir;
// Extent based leaf directory // Extent based leaf directory
NodeDirectory* fNodeDir; NodeDirectory* fNodeDir;
TreeDirectory* fTreeDir;
}; };
@@ -20,6 +20,7 @@ DEFINES += DEBUG_APP="\\\"xfs\\\"" ;
UseHeaders [ FDirName $(HAIKU_TOP) src libs uuid ] : true ; UseHeaders [ FDirName $(HAIKU_TOP) src libs uuid ] : true ;
local xfsSources = local xfsSources =
BPlusTree.cpp
DeviceOpener.cpp DeviceOpener.cpp
Directory.cpp Directory.cpp
Extent.cpp Extent.cpp
@@ -39,6 +39,7 @@ UseHeaders [ FDirName $(HAIKU_TOP) headers private ] : true ;
UseHeaders [ FDirName $(HAIKU_TOP) src tools fs_shell ] ; UseHeaders [ FDirName $(HAIKU_TOP) src tools fs_shell ] ;
local xfsSource = local xfsSource =
BPlusTree.cpp
DeviceOpener.cpp DeviceOpener.cpp
Directory.cpp Directory.cpp
Extent.cpp Extent.cpp