xfs: Read Block directories

Block directories can now be read.
Change-Id: I9aa898638e6ac39cb903f2dd58cd184785e1a06b
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2992
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
CruxBox
2020-10-11 16:00:26 +00:00
committed by waddlesplash
parent 3255108dde
commit fc87b4a284
8 changed files with 217 additions and 32 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
#ifndef _DEBUG_H_
#define _DEBUG_H_
#define TRACE_XFS
// #define TRACE_XFS
#ifdef TRACE_XFS
#define TRACE(x...) dprintf("\n\33[34mxfs:\33[0m " x)
#define ASSERT(x) \
@@ -62,15 +62,17 @@ DirectoryIterator::Init()
status_t
DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
{
status_t status;
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
status_t status = fShortDir->GetNext(name, length, ino);
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");
return B_NOT_SUPPORTED;
status = fExtentDir->GetNext(name, length, ino);
return status;
}
//TODO: Reading from B+Trees based directories
@@ -87,19 +89,17 @@ DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
status_t
DirectoryIterator::Lookup(const char* name, size_t length, xfs_ino_t* ino)
{
status_t status;
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
status_t status = fShortDir->Lookup(name, length, ino);
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");
#if 0
status_t status = fExtentDir->Lookup(name, length, ino);
return status;
#endif
return B_NOT_SUPPORTED;
status = fExtentDir->Lookup(name, length, ino);
return status;
}
//TODO: Reading from B+Tree based dirs
+181 -4
View File
@@ -9,7 +9,8 @@
Extent::Extent(Inode* inode)
:
fInode(inode)
fInode(inode),
fOffset(0)
{
}
@@ -36,6 +37,37 @@ Extent::FillMapEntry(void* pointerToMap)
}
status_t
Extent::FillBlockBuffer()
{
if (fMap->br_state != 0)
return B_BAD_VALUE;
int len = fInode->DirBlockSize();
fBlockBuffer = new(std::nothrow) char[len];
if (fBlockBuffer == NULL)
return B_NO_MEMORY;
Volume* volume = fInode->GetVolume();
xfs_agblock_t numberOfBlocksInAg = volume->AgBlocks();
uint64 agNo = FSBLOCKS_TO_AGNO(fMap->br_startblock, volume);
uint64 agBlockNo = FSBLOCKS_TO_AGBLOCKNO(fMap->br_startblock, volume);
xfs_fsblock_t blockToRead = FSBLOCKS_TO_BASICBLOCKS(volume->BlockLog(),
((uint64)(agNo * numberOfBlocksInAg) + agBlockNo));
xfs_daddr_t readPos = blockToRead * (BASICBLOCKSIZE) + fMap->br_startoff;
TRACE("blockToRead: (%ld), readPos: (%ld)\n", blockToRead, readPos);
if (read_pos(volume->Device(), readPos, fBlockBuffer, len) != len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
return B_OK;
}
status_t
Extent::Init()
{
@@ -46,16 +78,38 @@ Extent::Init()
ASSERT(BlockType() == true);
void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer());
FillMapEntry(pointerToMap);
ASSERT(fMap->br_blockcount == 1);
//TODO: This is always true for block directories
//If we use this implementation for leaf directories, this is not
//always true
status_t status = FillBlockBuffer();
ExtentDataHeader* header = (ExtentDataHeader*)fBlockBuffer;
if (B_BENDIAN_TO_HOST_INT32(header->magic) == DIR2_BLOCK_HEADER_MAGIC) {
status = B_OK;
TRACE("Extent:Init(): Block read successfully\n");
} else {
status = B_BAD_VALUE;
TRACE("Extent:Init(): Bad Block!\n");
}
return B_NOT_SUPPORTED;
return status;
}
ExtentBlockTail*
Extent::BlockTail(ExtentDataHeader* header)
Extent::BlockTail()
{
return (ExtentBlockTail*)
((char*)header + fInode->DirBlockSize() - sizeof(ExtentBlockTail));
(fBlockBuffer + fInode->DirBlockSize() - sizeof(ExtentBlockTail));
}
uint32
Extent::GetOffsetFromAddress(uint32 address)
{
address = address * 8;
// block offset in eight bytes, hence multiple with 8
return address & (fInode->DirBlockSize() - 1);
}
@@ -75,5 +129,128 @@ Extent::BlockType()
if (fInode->Size() != fInode->DirBlockSize())
status = false;
return status;
//TODO: Checks: Fileoffset must be 0 and
//length = directory block size / filesystem block size
}
int
Extent::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 (entrySize + 7) & -8;
// rounding off to closest multiple of 8
}
status_t
Extent::GetNext(char* name, size_t* length, xfs_ino_t* ino)
{
TRACE("Extend::GetNext\n");
void* entry = (void*)((ExtentDataHeader*)fBlockBuffer + 1);
// This could be an unused entry so we should check
int numberOfEntries = B_BENDIAN_TO_HOST_INT32(BlockTail()->count);
TRACE("numberOfEntries:(%d)\n", numberOfEntries);
for (int i = 0; i < numberOfEntries; i++) {
TRACE("i:(%d)\n", i);
ExtentUnusedEntry* unusedEntry = (ExtentUnusedEntry*)entry;
if (B_BENDIAN_TO_HOST_INT16(unusedEntry->freetag) == DIR2_FREE_TAG) {
TRACE("Unused entry found\n");
entry = (void*)
((char*)entry + B_BENDIAN_TO_HOST_INT16(unusedEntry->length));
continue;
}
ExtentDataEntry* dataEntry = (ExtentDataEntry*) entry;
uint16 currentOffset = (char*)dataEntry - fBlockBuffer;
TRACE("GetNext: fOffset:(%d), currentOffset:(%d)\n",
fOffset, currentOffset);
if (fOffset >= currentOffset) {
entry = (void*)((char*)entry + EntrySize(dataEntry->namelen));
continue;
}
if (dataEntry->namelen > *length)
return B_BUFFER_OVERFLOW;
fOffset = currentOffset;
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
Extent::Lookup(const char* name, size_t length, xfs_ino_t* ino)
{
TRACE("Extent: Lookup\n");
TRACE("Name: %s\n", name);
uint32 hashValueOfRequest = hashfunction(name, length);
TRACE("Hashval:(%ld)\n", hashValueOfRequest);
ExtentBlockTail* blockTail = BlockTail();
ExtentLeafEntry* leafEntry = BlockFirstLeaf(blockTail);
int numberOfLeafEntries = B_BENDIAN_TO_HOST_INT32(blockTail->count);
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);
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 offset = GetOffsetFromAddress(address);
TRACE("offset:(%d)\n", offset);
ExtentDataEntry* entry = (ExtentDataEntry*)(fBlockBuffer + 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++;
}
return B_ENTRY_NOT_FOUND;
}
+9 -2
View File
@@ -12,9 +12,13 @@
#define DIR2_BLOCK_HEADER_MAGIC 0x58443242
// for v4 system
#define DIR2_FREE_TAG 0xffff
#define XFS_DIR2_DATA_FD_COUNT 3
#define EXTENT_REC_SIZE 128
#define MASK(n) ((1UL << n) - 1)
#define FSBLOCKS_TO_AGNO(n, volume) (n >> volume->AgBlocksLog())
#define FSBLOCKS_TO_AGBLOCKNO(n, volume) (n & MASK(volume->AgBlocksLog()))
// xfs_exntst_t
@@ -97,10 +101,12 @@ public:
status_t Init();
bool BlockType();
void FillMapEntry(void* pointerToMap);
ExtentDataHeader* BlockHeader();
ExtentBlockTail* BlockTail(ExtentDataHeader* header);
status_t FillBlockBuffer();
ExtentBlockTail* BlockTail();
ExtentLeafEntry* BlockFirstLeaf(ExtentBlockTail* tail);
xfs_ino_t GetIno();
uint32 GetOffsetFromAddress(uint32 address);
int EntrySize(int len) const;
status_t GetNext(char* name, size_t* length,
xfs_ino_t* ino);
status_t Lookup(const char* name, size_t length,
@@ -108,6 +114,7 @@ public:
private:
Inode* fInode;
ExtentMapEntry* fMap;
uint32 fOffset;
char* fBlockBuffer;
// This isn't inode data. It holds the directory block.
};
+14 -3
View File
@@ -160,6 +160,13 @@ Inode::Init()
}
bool
Inode::HasFileTypeField() const
{
return fVolume->SuperBlockFeatures2() & XFS_SB_VERSION2_FTYPE;
}
status_t
Inode::GetFromDisk()
{
@@ -209,17 +216,21 @@ Inode::~Inode()
}
/*
* Basically take 4 characters at a time as long as you can, and xor with
* previous hashVal after rotating 4 bits of hashVal. Likewise, continue
* xor and rotating. This is quite a generic hash function.
*/
uint32
hashfunction(const char* name, int length)
{
uint32 hashVal = 0;
int lengthCovered = 0;
int index = 0;
if (length >= 4) {
for (; index <= length; index+=4)
for (;index < length && (length - index) >= 4; index += 4)
{
lengthCovered = index;
lengthCovered += 4;
hashVal = (name[index] << 21) ^ (name[index+1] << 14)
^ (name[index+2] << 7) ^ (name[index+3] << 0)
^ ((hashVal << 28) | (hashVal >> (4)));
@@ -178,6 +178,7 @@ public:
uint32 UserId() const { return fNode->UserId(); }
uint32 GroupId() const { return fNode->GroupId(); }
bool HasFileTypeField() const;
private:
status_t GetFromDisk();
@@ -22,20 +22,10 @@ ShortDirectory::~ShortDirectory()
}
bool
ShortDirectory::HasFileTypeField()
{
if (fInode->GetVolume()->SuperBlockFeatures2() & XFS_SB_VERSION2_FTYPE)
return true;
else
return false;
}
uint8
ShortDirectory::GetFileType(ShortFormEntry* entry)
{
ASSERT(HasFileTypeField() == true);
ASSERT(fInode->HasFileTypeField() == true);
return entry->name[entry->namelen];
}
@@ -70,7 +60,7 @@ ShortDirectory::GetIno(ShortFormInodeUnion* inum)
xfs_ino_t
ShortDirectory::GetEntryIno(ShortFormEntry* entry)
{
if (HasFileTypeField())
if (fInode->HasFileTypeField())
return GetIno((ShortFormInodeUnion*)(entry->name
+ entry->namelen + sizeof(uint8)));
else
@@ -82,7 +72,7 @@ size_t
ShortDirectory::EntrySize(int namelen)
{
return sizeof(ShortFormEntry) + namelen
+ (HasFileTypeField()? sizeof(uint8) : 0)
+ (fInode->HasFileTypeField()? sizeof(uint8) : 0)
+ (fHeader->i8count? sizeof(uint64):sizeof(uint32));
}
@@ -56,7 +56,6 @@ public:
ShortDirectory(Inode* inode);
~ShortDirectory();
size_t HeaderSize();
bool HasFileTypeField();
uint8 GetFileType(ShortFormEntry* entry);
ShortFormEntry* FirstEntry();
xfs_ino_t GetIno(ShortFormInodeUnion* inum);