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 <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
CruxBox
2021-06-23 17:17:40 +00:00
committed by Adrien Destugues
parent 02a8f8b29c
commit 68c3f45dfb
7 changed files with 262 additions and 37 deletions
+1 -2
View File
@@ -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
#endif
@@ -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:
+150 -16
View File
@@ -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<char> 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) {
+25 -2
View File
@@ -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
@@ -0,0 +1,27 @@
/*
* Copyright 2001-2009, Axel Dörfler, [email protected].
* Copyright 2020, Shubham Bhagat, [email protected]
* 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
@@ -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<file_cookie> 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;
}
@@ -1,4 +1,5 @@
/*
* Copyright 2001-2017, Axel Dörfler, [email protected].
* Copyright 2020, Shubham Bhagat, [email protected]
* 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 */