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:
committed by
Adrien Destugues
parent
3334d6fba1
commit
bd76cde0fa
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user