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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user