xfs: Implemented reading of B+Tree based files

This patch adds the functionality to read B+Trees based files.
It shares similarity with extent based file reading functionality. Just
the way the extents are read is different.

Change-Id: I9b7ebe20171a8860fdc35024f7018540248aed61
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3170
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
CruxBox
2021-07-03 09:10:35 +00:00
committed by Adrien Destugues
parent 3334d6fba1
commit bd76cde0fa
6 changed files with 233 additions and 72 deletions
@@ -38,7 +38,6 @@ TreeDirectory::TreeDirectory(Inode* inode)
fPathForLeaves[i].blockData = NULL;
fPathForData[i].blockData = NULL;
}
}
@@ -46,7 +45,7 @@ TreeDirectory::~TreeDirectory()
{
delete fRoot;
delete[] fExtents;
delete fSingleDirBlock;
delete[] fSingleDirBlock;
}
@@ -285,50 +284,21 @@ TreeDirectory::GetAllExtents()
ArrayDeleter<ExtentMapUnwrap> extentsWrappedDeleter(extentsWrapped);
Volume* volume = fInode->GetVolume();
uint16 levelsInTree = fRoot->Levels();
size_t maxRecords = MaxRecordsPossibleRoot();
TRACE("Maxrecords: (%d)\n", maxRecords);
TreePointer* ptrToNode = GetPtrFromRoot(1);
Volume* volume = fInode->GetVolume();
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");
return B_IO_ERROR;
}
LongBlock* curLongBlock = (LongBlock*)node;
ASSERT(curLongBlock->Magic() == XFS_BMAP_MAGIC);
ptrToNode = GetPtrFromNode(1, (void*)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");
return B_IO_ERROR;
}
levelsInTree--;
ASSERT(levelsInTree == 0);
uint16 levelsInTree = fRoot->Levels();
status_t status = fInode->GetNodefromTree(levelsInTree, volume, len,
fInode->DirBlockSize(), fSingleDirBlock);
if (status != B_OK)
return status;
// We should be at the left most leaf node.
// This could be a multilevel node type directory
uint64 fileSystemBlockNo;
while (1) {
// Run till you have leaf blocks to checkout
char* leafBuffer = fSingleDirBlock;
@@ -358,7 +328,7 @@ TreeDirectory::GetAllExtents()
}
TRACE("Total covered: (%d)\n", fCountOfFilledExtents);
status_t status = UnWrapExtents(extentsWrapped);
status = UnWrapExtents(extentsWrapped);
return status;
}
@@ -13,20 +13,6 @@
#include "system_dependencies.h"
#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
#define MAX_TREE_DEPTH 5
typedef xfs_fileoff_t TreeKey;
typedef xfs_fsblock_t TreePointer;
/*
* Headers(here, the LongBlock) are the "nodes" really and are called "blocks".
* The records, keys and ptrs are calculated using helpers
@@ -62,19 +48,6 @@ struct LongBlock {
*/
// 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;
};
struct ExtentMapUnwrap {
uint64 first;
uint64 second;
+186 -4
View File
@@ -5,7 +5,7 @@
#include "Inode.h"
#include "BPlusTree.h"
void
xfs_inode_t::SwapEndian()
@@ -216,10 +216,8 @@ Inode::UnWrapExtentFromWrappedEntry(uint64 wrappedExtent[2],
status_t
Inode::ReadExtents()
Inode::ReadExtentsFromExtentBasedInode()
{
if (Format() != XFS_DINODE_FMT_EXTENTS)
return B_NOT_SUPPORTED;
fExtents = new(std::nothrow) ExtentMapEntry[DataExtentsCount()];
char* dataStart = (char*) DIR_DFORK_PTR(Buffer());
uint64 wrappedExtent[2];
@@ -233,6 +231,189 @@ Inode::ReadExtents()
}
size_t
Inode::MaxRecordsPossibleInTreeRoot()
{
size_t lengthOfDataFork;
if (ForkOffset() != 0)
lengthOfDataFork = ForkOffset() << 3;
else if(ForkOffset() == 0) {
lengthOfDataFork = GetVolume()->InodeSize()
- INODE_CORE_UNLINKED_SIZE;
}
lengthOfDataFork -= sizeof(BlockInDataFork);
return lengthOfDataFork / (XFS_KEY_SIZE + XFS_PTR_SIZE);
}
size_t
Inode::GetPtrOffsetIntoRoot(int pos)
{
size_t maxRecords = MaxRecordsPossibleInTreeRoot();
return (sizeof(BlockInDataFork)
+ maxRecords * XFS_KEY_SIZE + (pos - 1) * XFS_PTR_SIZE);
}
TreePointer*
Inode::GetPtrFromRoot(int pos)
{
return (TreePointer*)
((char*)DIR_DFORK_PTR(Buffer()) + GetPtrOffsetIntoRoot(pos));
}
size_t
Inode::MaxRecordsPossibleNode()
{
size_t availableSpace = GetVolume()->BlockSize() - XFS_BTREE_LBLOCK_SIZE;
return availableSpace / (XFS_KEY_SIZE + XFS_PTR_SIZE);
}
size_t
Inode::GetPtrOffsetIntoNode(int pos)
{
size_t maxRecords = MaxRecordsPossibleNode();
return XFS_BTREE_LBLOCK_SIZE + maxRecords * XFS_KEY_SIZE
+ (pos - 1) * XFS_PTR_SIZE;
}
TreePointer*
Inode::GetPtrFromNode(int pos, void* buffer)
{
size_t offsetIntoNode = GetPtrOffsetIntoNode(pos);
return (TreePointer*)((char*)buffer + offsetIntoNode);
}
status_t
Inode::GetNodefromTree(uint16& levelsInTree, Volume* volume,
size_t& len, size_t DirBlockSize, char* block) {
char* node = new(std::nothrow) char[len];
if (node == NULL)
return B_NO_MEMORY;
// This isn't for a directory block but for one of the tree nodes
ArrayDeleter<char> nodeDeleter(node);
TRACE("levels:(%d)\n", levelsInTree);
TRACE("Numrecs:(%d)\n", fRoot->NumRecords());
TreePointer* ptrToNode = GetPtrFromRoot(1);
uint64 fileSystemBlockNo = B_BENDIAN_TO_HOST_INT64(*ptrToNode);
uint64 readPos = FileSystemBlockToAddr(fileSystemBlockNo);
// Go down the tree by taking the leftmost pointer to the first leaf
while (levelsInTree != 1) {
fileSystemBlockNo = B_BENDIAN_TO_HOST_INT64(*ptrToNode);
// The fs block that contains node at next lower level. Now read.
readPos = FileSystemBlockToAddr(fileSystemBlockNo);
if (read_pos(volume->Device(), readPos, node, len) != len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
LongBlock* curLongBlock = (LongBlock*)node;
ASSERT(curLongBlock->Magic() == XFS_BMAP_MAGIC);
ptrToNode = GetPtrFromNode(1, (void*)curLongBlock);
// Get's the first pointer. This points to next node.
levelsInTree--;
}
// Next level wil contain leaf nodes. Now Read Directory Buffer
len = DirBlockSize;
if (read_pos(volume->Device(), readPos, block, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
levelsInTree--;
if (levelsInTree != 0)
return B_BAD_VALUE;
return B_OK;
}
status_t
Inode::ReadExtentsFromTreeInode()
{
fExtents = new(std::nothrow) ExtentMapEntry[DataExtentsCount()];
BlockInDataFork* root = new(std::nothrow) BlockInDataFork;
if (root == NULL)
return B_NO_MEMORY;
memcpy((void*)root,
DIR_DFORK_PTR(Buffer()), sizeof(BlockInDataFork));
size_t maxRecords = MaxRecordsPossibleInTreeRoot();
TRACE("Maxrecords: (%d)\n", maxRecords);
uint16 levelsInTree = root->Levels();
delete root;
Volume* volume = GetVolume();
size_t len = volume->BlockSize();
len = DirBlockSize();
char* block = new(std::nothrow) char[len];
if (block == NULL)
return B_NO_MEMORY;
ArrayDeleter<char> blockDeleter(block);
GetNodefromTree(levelsInTree, volume, len, DirBlockSize(), block);
uint64 fileSystemBlockNo;
uint64 wrappedExtent[2];
int indexIntoExtents = 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 = block;
ASSERT(((LongBlock*)leafBuffer)->Magic() == XFS_BMAP_MAGIC);
uint32 offset = sizeof(LongBlock);
int numRecs = ((LongBlock*)leafBuffer)->NumRecs();
for (int i = 0; i < numRecs; i++) {
wrappedExtent[0] = *(uint64*)(leafBuffer + offset);
wrappedExtent[1]
= *(uint64*)(leafBuffer + offset + sizeof(uint64));
offset += sizeof(ExtentMapUnwrap);
UnWrapExtentFromWrappedEntry(wrappedExtent,
&fExtents[indexIntoExtents]);
indexIntoExtents++;
}
fileSystemBlockNo = ((LongBlock*)leafBuffer)->Right();
TRACE("Next leaf is at: (%d)\n", fileSystemBlockNo);
if (fileSystemBlockNo == -1)
break;
uint64 readPos = FileSystemBlockToAddr(fileSystemBlockNo);
if (read_pos(volume->Device(), readPos, block, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
}
TRACE("Total covered: (%d)\n", indexIntoExtents);
return B_OK;
}
status_t
Inode::ReadExtents()
{
if (fExtents != NULL)
return B_OK;
if (Format() == XFS_DINODE_FMT_BTREE)
return ReadExtentsFromTreeInode();
if (Format() == XFS_DINODE_FMT_EXTENTS)
return ReadExtentsFromExtentBasedInode();
return B_BAD_VALUE;
}
int
Inode::SearchMapInAllExtent(int blockNo)
{
@@ -328,6 +509,7 @@ Inode::ReadAt(off_t pos, uint8* buffer, size_t* length)
blockNo = BLOCKNO_FROM_POSITION(pos, GetVolume());
offsetIntoBlock = BLOCKOFFSET_FROM_POSITION(pos, this);
}
TRACE("lengthRead:(%d)\n", lengthRead);
*length = lengthRead;
return B_OK;
+26 -2
View File
@@ -45,6 +45,19 @@
#define BLOCKOFFSET_FROM_POSITION(n, inode) ((n) & (inode->BlockSize() - 1))
// 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;
};
// xfs_da_blkinfo_t
struct BlockInfo {
uint32 forw;
@@ -146,7 +159,6 @@ struct xfs_inode_t {
uint16 di_dmstate;
uint16 di_flags;
uint32 di_gen;
uint32 di_next_unlinked;
};
@@ -218,9 +230,21 @@ public:
{ return fNode->ForkOffset(); }
status_t ReadExtents();
status_t ReadAt(off_t pos, uint8* buffer, size_t* length);
status_t GetNodefromTree(uint16& levelsInTree,
Volume* volume, size_t& len,
size_t DirBlockSize, char* block);
int SearchMapInAllExtent(int blockNo);
void UnWrapExtentFromWrappedEntry(
uint64 wrappedExtent[2], ExtentMapEntry* entry);
uint64 wrappedExtent[2],
ExtentMapEntry* entry);
status_t ReadExtentsFromExtentBasedInode();
status_t ReadExtentsFromTreeInode();
size_t MaxRecordsPossibleInTreeRoot();
size_t MaxRecordsPossibleNode();
TreePointer* GetPtrFromRoot(int pos);
TreePointer* GetPtrFromNode(int pos, void* buffer);
size_t GetPtrOffsetIntoRoot(int pos);
size_t GetPtrOffsetIntoNode(int pos);
private:
status_t GetFromDisk();
xfs_inode_t* fNode;
+10
View File
@@ -32,6 +32,16 @@ extern fs_volume_ops gxfsVolumeOps;
// The size of a basic block should be 512
#define XFS_OPEN_MODE_USER_MASK 0x7fffffff
/* B+Tree related macros
*/
#define XFS_BTREE_SBLOCK_SIZE 18
// Header for Short Format btree
#define XFS_BTREE_LBLOCK_SIZE 24
// Header for Long Format btree
#define XFS_BMAP_MAGIC 0x424d4150
#define MAX_TREE_DEPTH 5
#define XFS_KEY_SIZE sizeof(xfs_fileoff_t)
#define XFS_PTR_SIZE sizeof(xfs_fsblock_t)
struct file_cookie {
bigtime_t last_notification;
@@ -34,6 +34,8 @@ typedef uint64 xfs_rtblock_t; // extent number in the real-time sub-volume
typedef uint64 xfs_fileoff_t; // block offset into a file
typedef uint64 xfs_filblks_t; // block count for a file
typedef int64 xfs_fsize_t; // byte size of a file
typedef xfs_fileoff_t TreeKey;
typedef xfs_fsblock_t TreePointer;
// typedef unsigned char uuid_t[16];