From 68c3f45dfb5653b45e2336d76752e10524453f83 Mon Sep 17 00:00:00 2001 From: CruxBox Date: Mon, 17 Aug 2020 22:36:02 +0530 Subject: [PATCH] xfs: Attempt at reading extent based files There are 2 issues right now. One is that I have to give cat command the entire file size for it to print the entire file. The second issue is that I am getting segmentation fault for some reason, and it doesn't even have to do with xfs_read(). For more info: https://review.haiku-os.org/c/haiku/+/3154 Change-Id: I8e3acd658730ed2339dabbd671820c409332f296 Reviewed-on: https://review.haiku-os.org/c/haiku/+/3154 Tested-by: Commit checker robot Reviewed-by: Adrien Destugues --- src/add-ons/kernel/file_systems/xfs/Debug.h | 3 +- src/add-ons/kernel/file_systems/xfs/Extent.h | 12 -- src/add-ons/kernel/file_systems/xfs/Inode.cpp | 166 ++++++++++++++++-- src/add-ons/kernel/file_systems/xfs/Inode.h | 27 ++- src/add-ons/kernel/file_systems/xfs/Utility.h | 27 +++ .../file_systems/xfs/kernel_interface.cpp | 55 +++++- src/add-ons/kernel/file_systems/xfs/xfs.h | 9 + 7 files changed, 262 insertions(+), 37 deletions(-) create mode 100644 src/add-ons/kernel/file_systems/xfs/Utility.h diff --git a/src/add-ons/kernel/file_systems/xfs/Debug.h b/src/add-ons/kernel/file_systems/xfs/Debug.h index ec9038a75c..6b0dc2aacd 100644 --- a/src/add-ons/kernel/file_systems/xfs/Debug.h +++ b/src/add-ons/kernel/file_systems/xfs/Debug.h @@ -9,7 +9,6 @@ #ifndef _DEBUG_H_ #define _DEBUG_H_ -// #define TRACE_XFS #ifdef TRACE_XFS #define TRACE(x...) dprintf("\n\33[34mxfs:\33[0m " x) #define ASSERT(x) \ @@ -20,4 +19,4 @@ #endif #define ERROR(x...) dprintf("\n\33[34mxfs:\33[0m " x) -#endif \ No newline at end of file +#endif diff --git a/src/add-ons/kernel/file_systems/xfs/Extent.h b/src/add-ons/kernel/file_systems/xfs/Extent.h index cdcb86b06d..b771485896 100644 --- a/src/add-ons/kernel/file_systems/xfs/Extent.h +++ b/src/add-ons/kernel/file_systems/xfs/Extent.h @@ -82,18 +82,6 @@ struct ExtentBlockTail { }; -struct ExtentMapEntry { - xfs_fileoff_t br_startoff; - // logical file block offset - xfs_fsblock_t br_startblock; - // absolute block number - xfs_filblks_t br_blockcount; - // # of blocks - uint8 br_state; - // state of the extent -}; - - class Extent { public: diff --git a/src/add-ons/kernel/file_systems/xfs/Inode.cpp b/src/add-ons/kernel/file_systems/xfs/Inode.cpp index ec2e9d9831..4c58378fe3 100644 --- a/src/add-ons/kernel/file_systems/xfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/xfs/Inode.cpp @@ -141,9 +141,10 @@ xfs_inode_t::DataExtentsCount() const Inode::Inode(Volume* volume, xfs_ino_t id) : fVolume(volume), - fId(id) + fId(id), + fBuffer(NULL), + fExtents(NULL) { - } @@ -174,6 +175,14 @@ Inode::Init() } +Inode::~Inode() +{ + delete fNode; + delete[] fBuffer; + delete[] fExtents; +} + + bool Inode::HasFileTypeField() const { @@ -193,6 +202,138 @@ Inode::CheckPermissions(int accessMode) const } +void +Inode::UnWrapExtentFromWrappedEntry(uint64 wrappedExtent[2], + ExtentMapEntry* entry) +{ + uint64 first = B_BENDIAN_TO_HOST_INT64(wrappedExtent[0]); + uint64 second = B_BENDIAN_TO_HOST_INT64(wrappedExtent[1]); + entry->br_state = first >> 63; + entry->br_startoff = (first & MASK(63)) >> 9; + entry->br_startblock = ((first & MASK(9)) << 43) | (second >> 21); + entry->br_blockcount = second & MASK(21); +} + + +status_t +Inode::ReadExtents() +{ + 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]; + for (int i = 0; i < DataExtentsCount(); i++) { + wrappedExtent[0] = *(uint64*)(dataStart); + wrappedExtent[1] = *(uint64*)(dataStart + sizeof(uint64)); + dataStart += 2 * sizeof(uint64); + UnWrapExtentFromWrappedEntry(wrappedExtent, &fExtents[i]); + } + return B_OK; +} + + +int +Inode::SearchMapInAllExtent(int blockNo) +{ + for (int i = 0; i < DataExtentsCount(); i++) { + if (fExtents[i].br_startoff <= blockNo + && (blockNo <= fExtents[i].br_startoff + + fExtents[i].br_blockcount - 1)) { + // Map found + return i; + } + } + return -1; +} + + +status_t +Inode::ReadAt(off_t pos, uint8* buffer, size_t* length) +{ + TRACE("Inode::ReadAt: pos:(%ld), *length:(%ld)\n", pos, *length); + status_t status; + if (fExtents == NULL) { + status = ReadExtents(); + if (status != B_OK) + return status; + } + + // set/check boundaries for pos/length + if (pos < 0) { + ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF + ", length %lu)\n", ID(), pos, length); + return B_BAD_VALUE; + } + + if (pos >= Size() || length == 0) { + TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF + ", length %lu)\n", ID(), pos, length); + *length = 0; + return B_NO_ERROR; + } + + uint32 blockNo = BLOCKNO_FROM_POSITION(pos, GetVolume()); + uint32 offsetIntoBlock = BLOCKOFFSET_FROM_POSITION(pos, this); + + size_t lengthOfBlock = BlockSize(); + char* block = new(std::nothrow) char[lengthOfBlock]; + if (block == NULL) + return B_NO_MEMORY; + + ArrayDeleter blockDeleter(block); + + size_t lengthRead = 0; + size_t lengthLeftInFile; + size_t lengthLeftInBlock; + size_t lengthToRead; + TRACE("What is blockLen:(%d)\n", lengthOfBlock); + while (*length > 0) { + TRACE("Inode::ReadAt: pos:(%ld), *length:(%ld)\n", pos, *length); + // As long as you can read full blocks, read. + lengthLeftInFile = Size() - pos; + lengthLeftInBlock = lengthOfBlock - offsetIntoBlock; + + // We could be almost at the end of the file + if (lengthLeftInFile <= lengthLeftInBlock) + lengthToRead = lengthLeftInFile; + else lengthToRead = lengthLeftInBlock; + + // But we might not be able to read all of the + // data because of less buffer length + if (lengthToRead > *length) + lengthToRead = *length; + + int indexForMap = SearchMapInAllExtent(blockNo); + if (indexForMap == -1) + return B_BAD_VALUE; + + xfs_daddr_t readPos + = FileSystemBlockToAddr(fExtents[indexForMap].br_startblock + + blockNo - fExtents[indexForMap].br_startoff); + + if (read_pos(GetVolume()->Device(), readPos, block, lengthOfBlock) + != lengthOfBlock) { + ERROR("TreeDirectory::FillBlockBuffer(): IO Error"); + return B_IO_ERROR; + } + + + memcpy((void*) (buffer + lengthRead), + (void*)(block + offsetIntoBlock), lengthToRead); + + pos += lengthToRead; + *length -= lengthToRead; + lengthRead += lengthToRead; + blockNo = BLOCKNO_FROM_POSITION(pos, GetVolume()); + offsetIntoBlock = BLOCKOFFSET_FROM_POSITION(pos, this); + } + TRACE("lengthRead:(%d)\n", lengthRead); + *length = lengthRead; + return B_OK; +} + + status_t Inode::GetFromDisk() { @@ -253,13 +394,6 @@ Inode::FileSystemBlockToAddr(uint64 block) } -Inode::~Inode() -{ - delete fBuffer; - delete fNode; -} - - /* * 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 @@ -272,22 +406,22 @@ hashfunction(const char* name, int length) int lengthCovered = 0; int index = 0; if (length >= 4) { - for (;index < length && (length - index) >= 4; index += 4) + for (; index < length && (length - index) >= 4; index += 4) { lengthCovered += 4; - hashVal = (name[index] << 21) ^ (name[index+1] << 14) - ^ (name[index+2] << 7) ^ (name[index+3] << 0) - ^ ((hashVal << 28) | (hashVal >> (4))); + hashVal = (name[index] << 21) ^ (name[index + 1] << 14) + ^ (name[index + 2] << 7) ^ (name[index + 3] << 0) + ^ ((hashVal << 28) | (hashVal >> 4)); } } int leftToCover = length - lengthCovered; if (leftToCover == 3) { - hashVal = (name[index] << 14) ^ (name[index+1] << 7) - ^ (name[index+2] << 0) ^ ((hashVal << 21) | (hashVal >> (11))); + hashVal = (name[index] << 14) ^ (name[index + 1] << 7) + ^ (name[index + 2] << 0) ^ ((hashVal << 21) | (hashVal >> 11)); } if (leftToCover == 2) { - hashVal = (name[index] << 7) ^ (name[index+1] << 0) + hashVal = (name[index] << 7) ^ (name[index + 1] << 0) ^ ((hashVal << 14) | (hashVal >> (32 - 14))); } if (leftToCover == 1) { diff --git a/src/add-ons/kernel/file_systems/xfs/Inode.h b/src/add-ons/kernel/file_systems/xfs/Inode.h index 648db9db25..448cbc04e7 100644 --- a/src/add-ons/kernel/file_systems/xfs/Inode.h +++ b/src/add-ons/kernel/file_systems/xfs/Inode.h @@ -5,6 +5,7 @@ #ifndef _INODE_H_ #define _INODE_H_ + #include "system_dependencies.h" #include "Volume.h" #include "xfs_types.h" @@ -39,6 +40,9 @@ #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())) +#define BLOCKNO_FROM_POSITION(n, volume) \ + ((n) >> (volume->BlockLog())) +#define BLOCKOFFSET_FROM_POSITION(n, inode) ((n) & (inode->BlockSize() - 1)) // xfs_da_blkinfo_t @@ -50,6 +54,18 @@ struct BlockInfo { }; +struct ExtentMapEntry { + xfs_fileoff_t br_startoff; + // logical file block offset + xfs_fsblock_t br_startblock; + // absolute block number + xfs_filblks_t br_blockcount; + // # of blocks + uint8 br_state; + // state of the extent +}; + + uint32 hashfunction(const char* name, int length); @@ -80,7 +96,6 @@ enum xfs_dinode_fmt_t { struct xfs_inode_t { void SwapEndian(); int8 Version() const; - //TODO: Check mode_t Mode() const; void GetModificationTime(struct timespec& timestamp); @@ -179,6 +194,9 @@ public: uint32 DirBlockSize() const { return fVolume->DirBlockSize(); } + uint32 BlockSize() const + { return fVolume->BlockSize(); } + void GetChangeTime(struct timespec& timestamp) const { fNode->GetChangeTime(timestamp); } @@ -198,7 +216,11 @@ public: uint64 FileSystemBlockToAddr(uint64 block); uint8 ForkOffset() const { return fNode->ForkOffset(); } - + status_t ReadExtents(); + status_t ReadAt(off_t pos, uint8* buffer, size_t* length); + int SearchMapInAllExtent(int blockNo); + void UnWrapExtentFromWrappedEntry( + uint64 wrappedExtent[2], ExtentMapEntry* entry); private: status_t GetFromDisk(); xfs_inode_t* fNode; @@ -206,6 +228,7 @@ private: Volume* fVolume; char* fBuffer; // Contains the disk inode in BE format + ExtentMapEntry* fExtents; }; #endif diff --git a/src/add-ons/kernel/file_systems/xfs/Utility.h b/src/add-ons/kernel/file_systems/xfs/Utility.h new file mode 100644 index 0000000000..fa0ccfac2e --- /dev/null +++ b/src/add-ons/kernel/file_systems/xfs/Utility.h @@ -0,0 +1,27 @@ +/* + * Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com + * This file may be used under the terms of the MIT License. + */ +#ifndef UTILITY_H +#define UTILITY_H + + +/*! Converts the open mode, the open flags given to xfs_open(), into + access modes, e.g. since O_RDONLY requires read access to the + file, it will be converted to R_OK. +*/ +inline int +open_mode_to_access(int openMode) +{ + openMode &= O_RWMASK; + if (openMode == O_RDONLY) + return R_OK; + if (openMode == O_WRONLY) + return W_OK; + + return R_OK | W_OK; +} + + +#endif // UTILITY_H diff --git a/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp index 5b7558806b..1ee159d897 100644 --- a/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp @@ -8,6 +8,7 @@ #include "system_dependencies.h" #include "Directory.h" #include "Inode.h" +#include "Utility.h" #include "Volume.h" @@ -281,7 +282,33 @@ static status_t xfs_open(fs_volume * /*_volume*/, fs_vnode *_node, int openMode, void **_cookie) { - return B_NOT_SUPPORTED; + TRACE("XFS_OPEN:\n"); + Inode* inode = (Inode*)_node->private_node; + + // opening a directory read-only is allowed, although you can't read + // any data from it. + if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) + return B_IS_A_DIRECTORY; + + status_t status = inode->CheckPermissions(open_mode_to_access(openMode) + | (openMode & O_TRUNC ? W_OK : 0)); + if (status != B_OK) + return status; + + // Prepare the cookie + file_cookie* cookie = new(std::nothrow) file_cookie; + if (cookie == NULL) + return B_NO_MEMORY; + ObjectDeleter cookieDeleter(cookie); + + cookie->open_mode = openMode & XFS_OPEN_MODE_USER_MASK; + cookie->last_size = inode->Size(); + cookie->last_notification = system_time(); + + cookieDeleter.Detach(); + *_cookie = cookie; + + return B_OK; } @@ -289,21 +316,38 @@ static status_t xfs_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, void *buffer, size_t *_length) { - return B_NOT_SUPPORTED; + TRACE("Inode::ReadAt: pos:(%ld), *length:(%ld)\n", pos, *_length); + Inode* inode = (Inode*)_node->private_node; + + if (!inode->IsFile()) { + *_length = 0; + return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; + } + + return inode->ReadAt(pos, (uint8*)buffer, _length); } static status_t xfs_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) { - return B_NOT_SUPPORTED; + return B_OK; } static status_t xfs_free_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) { - return B_NOT_SUPPORTED; + TRACE("XFS_FREE_COOKIE:\n"); + file_cookie* cookie = (file_cookie*)_cookie; + Volume* volume = (Volume*)_volume->private_volume; + Inode* inode = (Inode*)_node->private_node; + + if (inode->Size() != cookie->last_size) + notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE); + + delete cookie; + return B_OK; } @@ -423,13 +467,14 @@ static status_t xfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) { - return B_NOT_SUPPORTED; + return B_OK; } static status_t xfs_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) { + delete (DirectoryIterator*)_cookie; return B_NOT_SUPPORTED; } diff --git a/src/add-ons/kernel/file_systems/xfs/xfs.h b/src/add-ons/kernel/file_systems/xfs/xfs.h index 617e6bbcfa..4bc49e8300 100644 --- a/src/add-ons/kernel/file_systems/xfs/xfs.h +++ b/src/add-ons/kernel/file_systems/xfs/xfs.h @@ -1,4 +1,5 @@ /* + * Copyright 2001-2017, Axel Dörfler, axeld@pinc-software.de. * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com * All rights reserved. Distributed under the terms of the MIT License. */ @@ -29,6 +30,14 @@ extern fs_volume_ops gxfsVolumeOps; // Log of block size should be 9 #define BASICBLOCKSIZE (1 << BASICBLOCKLOG) // The size of a basic block should be 512 +#define XFS_OPEN_MODE_USER_MASK 0x7fffffff + + +struct file_cookie { + bigtime_t last_notification; + off_t last_size; + int open_mode; +}; /* Version 4 superblock definition */