diff --git a/src/add-ons/kernel/file_systems/xfs/Debug.h b/src/add-ons/kernel/file_systems/xfs/Debug.h index ac7413cce3..ec5a36b935 100644 --- a/src/add-ons/kernel/file_systems/xfs/Debug.h +++ b/src/add-ons/kernel/file_systems/xfs/Debug.h @@ -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) \ diff --git a/src/add-ons/kernel/file_systems/xfs/Directory.cpp b/src/add-ons/kernel/file_systems/xfs/Directory.cpp index 1f8a9643a1..5d763710d2 100644 --- a/src/add-ons/kernel/file_systems/xfs/Directory.cpp +++ b/src/add-ons/kernel/file_systems/xfs/Directory.cpp @@ -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 diff --git a/src/add-ons/kernel/file_systems/xfs/Extent.cpp b/src/add-ons/kernel/file_systems/xfs/Extent.cpp index 106c73756c..ed499e8860 100644 --- a/src/add-ons/kernel/file_systems/xfs/Extent.cpp +++ b/src/add-ons/kernel/file_systems/xfs/Extent.cpp @@ -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; } diff --git a/src/add-ons/kernel/file_systems/xfs/Extent.h b/src/add-ons/kernel/file_systems/xfs/Extent.h index 39b4d28025..3f3d6012c0 100644 --- a/src/add-ons/kernel/file_systems/xfs/Extent.h +++ b/src/add-ons/kernel/file_systems/xfs/Extent.h @@ -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. }; diff --git a/src/add-ons/kernel/file_systems/xfs/Inode.cpp b/src/add-ons/kernel/file_systems/xfs/Inode.cpp index f855443e77..78d4a6ebea 100644 --- a/src/add-ons/kernel/file_systems/xfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/xfs/Inode.cpp @@ -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))); diff --git a/src/add-ons/kernel/file_systems/xfs/Inode.h b/src/add-ons/kernel/file_systems/xfs/Inode.h index 7a1f16ff66..6bf4c9f8e1 100644 --- a/src/add-ons/kernel/file_systems/xfs/Inode.h +++ b/src/add-ons/kernel/file_systems/xfs/Inode.h @@ -178,6 +178,7 @@ public: uint32 UserId() const { return fNode->UserId(); } uint32 GroupId() const { return fNode->GroupId(); } + bool HasFileTypeField() const; private: status_t GetFromDisk(); diff --git a/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp b/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp index 3e34062c6e..ff3f6619e5 100644 --- a/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp +++ b/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp @@ -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)); } diff --git a/src/add-ons/kernel/file_systems/xfs/ShortDirectory.h b/src/add-ons/kernel/file_systems/xfs/ShortDirectory.h index 7507d3878b..316a3bda71 100644 --- a/src/add-ons/kernel/file_systems/xfs/ShortDirectory.h +++ b/src/add-ons/kernel/file_systems/xfs/ShortDirectory.h @@ -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);