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:
committed by
Adrien Destugues
parent
02a8f8b29c
commit
68c3f45dfb
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user