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_
|
#ifndef _DEBUG_H_
|
||||||
#define _DEBUG_H_
|
#define _DEBUG_H_
|
||||||
|
|
||||||
#define TRACE_XFS
|
// #define TRACE_XFS
|
||||||
#ifdef TRACE_XFS
|
#ifdef TRACE_XFS
|
||||||
#define TRACE(x...) dprintf("\n\33[34mxfs:\33[0m " x)
|
#define TRACE(x...) dprintf("\n\33[34mxfs:\33[0m " x)
|
||||||
#define ASSERT(x) \
|
#define ASSERT(x) \
|
||||||
|
|||||||
@@ -62,15 +62,17 @@ DirectoryIterator::Init()
|
|||||||
status_t
|
status_t
|
||||||
DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
|
DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
|
||||||
{
|
{
|
||||||
|
status_t status;
|
||||||
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
|
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
|
||||||
status_t status = fShortDir->GetNext(name, length, ino);
|
status = fShortDir->GetNext(name, length, ino);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Reading from extent based directories
|
//TODO: Reading from extent based directories
|
||||||
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
|
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
|
||||||
TRACE("Iterator:GetNext: EXTENTS");
|
TRACE("Iterator:GetNext: EXTENTS");
|
||||||
return B_NOT_SUPPORTED;
|
status = fExtentDir->GetNext(name, length, ino);
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Reading from B+Trees based directories
|
//TODO: Reading from B+Trees based directories
|
||||||
@@ -87,19 +89,17 @@ DirectoryIterator::GetNext(char* name, size_t* length, xfs_ino_t* ino)
|
|||||||
status_t
|
status_t
|
||||||
DirectoryIterator::Lookup(const char* name, size_t length, xfs_ino_t* ino)
|
DirectoryIterator::Lookup(const char* name, size_t length, xfs_ino_t* ino)
|
||||||
{
|
{
|
||||||
|
status_t status;
|
||||||
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
|
if (fInode->Format() == XFS_DINODE_FMT_LOCAL) {
|
||||||
status_t status = fShortDir->Lookup(name, length, ino);
|
status = fShortDir->Lookup(name, length, ino);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Reading from extent based dirs
|
//TODO: Reading from extent based dirs
|
||||||
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
|
if (fInode->Format() == XFS_DINODE_FMT_EXTENTS) {
|
||||||
TRACE("Iterator:Lookup: EXTENTS");
|
TRACE("Iterator:Lookup: EXTENTS");
|
||||||
#if 0
|
status = fExtentDir->Lookup(name, length, ino);
|
||||||
status_t status = fExtentDir->Lookup(name, length, ino);
|
return status;
|
||||||
return status;
|
|
||||||
#endif
|
|
||||||
return B_NOT_SUPPORTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Reading from B+Tree based dirs
|
//TODO: Reading from B+Tree based dirs
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
Extent::Extent(Inode* inode)
|
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
|
status_t
|
||||||
Extent::Init()
|
Extent::Init()
|
||||||
{
|
{
|
||||||
@@ -46,16 +78,38 @@ Extent::Init()
|
|||||||
ASSERT(BlockType() == true);
|
ASSERT(BlockType() == true);
|
||||||
void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer());
|
void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer());
|
||||||
FillMapEntry(pointerToMap);
|
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*
|
ExtentBlockTail*
|
||||||
Extent::BlockTail(ExtentDataHeader* header)
|
Extent::BlockTail()
|
||||||
{
|
{
|
||||||
return (ExtentBlockTail*)
|
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())
|
if (fInode->Size() != fInode->DirBlockSize())
|
||||||
status = false;
|
status = false;
|
||||||
return status;
|
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
|
#define DIR2_BLOCK_HEADER_MAGIC 0x58443242
|
||||||
// for v4 system
|
// for v4 system
|
||||||
|
#define DIR2_FREE_TAG 0xffff
|
||||||
#define XFS_DIR2_DATA_FD_COUNT 3
|
#define XFS_DIR2_DATA_FD_COUNT 3
|
||||||
#define EXTENT_REC_SIZE 128
|
#define EXTENT_REC_SIZE 128
|
||||||
#define MASK(n) ((1UL << n) - 1)
|
#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
|
// xfs_exntst_t
|
||||||
@@ -97,10 +101,12 @@ public:
|
|||||||
status_t Init();
|
status_t Init();
|
||||||
bool BlockType();
|
bool BlockType();
|
||||||
void FillMapEntry(void* pointerToMap);
|
void FillMapEntry(void* pointerToMap);
|
||||||
ExtentDataHeader* BlockHeader();
|
status_t FillBlockBuffer();
|
||||||
ExtentBlockTail* BlockTail(ExtentDataHeader* header);
|
ExtentBlockTail* BlockTail();
|
||||||
ExtentLeafEntry* BlockFirstLeaf(ExtentBlockTail* tail);
|
ExtentLeafEntry* BlockFirstLeaf(ExtentBlockTail* tail);
|
||||||
xfs_ino_t GetIno();
|
xfs_ino_t GetIno();
|
||||||
|
uint32 GetOffsetFromAddress(uint32 address);
|
||||||
|
int EntrySize(int len) const;
|
||||||
status_t GetNext(char* name, size_t* length,
|
status_t GetNext(char* name, size_t* length,
|
||||||
xfs_ino_t* ino);
|
xfs_ino_t* ino);
|
||||||
status_t Lookup(const char* name, size_t length,
|
status_t Lookup(const char* name, size_t length,
|
||||||
@@ -108,6 +114,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
Inode* fInode;
|
Inode* fInode;
|
||||||
ExtentMapEntry* fMap;
|
ExtentMapEntry* fMap;
|
||||||
|
uint32 fOffset;
|
||||||
char* fBlockBuffer;
|
char* fBlockBuffer;
|
||||||
// This isn't inode data. It holds the directory block.
|
// 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
|
status_t
|
||||||
Inode::GetFromDisk()
|
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
|
uint32
|
||||||
hashfunction(const char* name, int length)
|
hashfunction(const char* name, int length)
|
||||||
{
|
{
|
||||||
uint32 hashVal = 0;
|
uint32 hashVal = 0;
|
||||||
|
|
||||||
int lengthCovered = 0;
|
int lengthCovered = 0;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (length >= 4) {
|
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)
|
hashVal = (name[index] << 21) ^ (name[index+1] << 14)
|
||||||
^ (name[index+2] << 7) ^ (name[index+3] << 0)
|
^ (name[index+2] << 7) ^ (name[index+3] << 0)
|
||||||
^ ((hashVal << 28) | (hashVal >> (4)));
|
^ ((hashVal << 28) | (hashVal >> (4)));
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ public:
|
|||||||
|
|
||||||
uint32 UserId() const { return fNode->UserId(); }
|
uint32 UserId() const { return fNode->UserId(); }
|
||||||
uint32 GroupId() const { return fNode->GroupId(); }
|
uint32 GroupId() const { return fNode->GroupId(); }
|
||||||
|
bool HasFileTypeField() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t GetFromDisk();
|
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
|
uint8
|
||||||
ShortDirectory::GetFileType(ShortFormEntry* entry)
|
ShortDirectory::GetFileType(ShortFormEntry* entry)
|
||||||
{
|
{
|
||||||
ASSERT(HasFileTypeField() == true);
|
ASSERT(fInode->HasFileTypeField() == true);
|
||||||
return entry->name[entry->namelen];
|
return entry->name[entry->namelen];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +60,7 @@ ShortDirectory::GetIno(ShortFormInodeUnion* inum)
|
|||||||
xfs_ino_t
|
xfs_ino_t
|
||||||
ShortDirectory::GetEntryIno(ShortFormEntry* entry)
|
ShortDirectory::GetEntryIno(ShortFormEntry* entry)
|
||||||
{
|
{
|
||||||
if (HasFileTypeField())
|
if (fInode->HasFileTypeField())
|
||||||
return GetIno((ShortFormInodeUnion*)(entry->name
|
return GetIno((ShortFormInodeUnion*)(entry->name
|
||||||
+ entry->namelen + sizeof(uint8)));
|
+ entry->namelen + sizeof(uint8)));
|
||||||
else
|
else
|
||||||
@@ -82,7 +72,7 @@ size_t
|
|||||||
ShortDirectory::EntrySize(int namelen)
|
ShortDirectory::EntrySize(int namelen)
|
||||||
{
|
{
|
||||||
return sizeof(ShortFormEntry) + namelen
|
return sizeof(ShortFormEntry) + namelen
|
||||||
+ (HasFileTypeField()? sizeof(uint8) : 0)
|
+ (fInode->HasFileTypeField()? sizeof(uint8) : 0)
|
||||||
+ (fHeader->i8count? sizeof(uint64):sizeof(uint32));
|
+ (fHeader->i8count? sizeof(uint64):sizeof(uint32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ public:
|
|||||||
ShortDirectory(Inode* inode);
|
ShortDirectory(Inode* inode);
|
||||||
~ShortDirectory();
|
~ShortDirectory();
|
||||||
size_t HeaderSize();
|
size_t HeaderSize();
|
||||||
bool HasFileTypeField();
|
|
||||||
uint8 GetFileType(ShortFormEntry* entry);
|
uint8 GetFileType(ShortFormEntry* entry);
|
||||||
ShortFormEntry* FirstEntry();
|
ShortFormEntry* FirstEntry();
|
||||||
xfs_ino_t GetIno(ShortFormInodeUnion* inum);
|
xfs_ino_t GetIno(ShortFormInodeUnion* inum);
|
||||||
|
|||||||
Reference in New Issue
Block a user