xfs: Symbolic links support

- Necessary hooks for reading symlinks are implemented

- Added support for local symlinks

- Added support for extent based symlinks

- This patch completes #17791

Change-Id: If6847974562b21f751097df357326f4643eff757
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5621
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
Reviewed-by: Adrien Destugues <pulkomandy@pulkomandy.tk>
This commit is contained in:
Mashijams 2022-09-05 18:41:41 +05:30 committed by Adrien Destugues
parent f31b83f7ef
commit 99071c0c0b
5 changed files with 224 additions and 1 deletions

View File

@ -32,6 +32,7 @@ local xfsSources =
Node.cpp
ShortAttribute.cpp
ShortDirectory.cpp
Symlink.cpp
Volume.cpp
xfs.cpp
;

View File

@ -0,0 +1,141 @@
/*
* Copyright 2022, Raghav Sharma, raghavself28@gmail.com
* Distributed under the terms of the MIT License.
*/
#include "Symlink.h"
#include "VerifyHeader.h"
Symlink::Symlink(Inode* inode)
:
fInode(inode),
fSymlinkBuffer(NULL)
{
}
Symlink::~Symlink()
{
delete fSymlinkBuffer;
}
status_t
Symlink::_FillMapEntry()
{
void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize());
uint64 firstHalf = *((uint64*)pointerToMap);
uint64 secondHalf = *((uint64*)pointerToMap + 1);
//dividing the 128 bits into 2 parts.
firstHalf = B_BENDIAN_TO_HOST_INT64(firstHalf);
secondHalf = B_BENDIAN_TO_HOST_INT64(secondHalf);
fMap.br_state = firstHalf >> 63;
fMap.br_startoff = (firstHalf & MASK(63)) >> 9;
fMap.br_startblock = ((firstHalf & MASK(9)) << 43) | (secondHalf >> 21);
fMap.br_blockcount = secondHalf & MASK(21);
TRACE("Extent::Init: startoff:(%" B_PRIu64 "), startblock:(%" B_PRIu64 "),"
"blockcount:(%" B_PRIu64 "),state:(%" B_PRIu8 ")\n", fMap.br_startoff, fMap.br_startblock,
fMap.br_blockcount, fMap.br_state);
return B_OK;
}
status_t
Symlink::_FillBuffer()
{
if (fMap.br_state != 0)
return B_BAD_VALUE;
int len = fInode->DirBlockSize();
fSymlinkBuffer = new(std::nothrow) char[len];
if (fSymlinkBuffer == NULL)
return B_NO_MEMORY;
xfs_daddr_t readPos =
fInode->FileSystemBlockToAddr(fMap.br_startblock);
if (read_pos(fInode->GetVolume()->Device(), readPos, fSymlinkBuffer, len)
!= len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
return B_OK;
}
status_t
Symlink::_ReadLocalLink(off_t pos, char* buffer, size_t* _length)
{
// All symlinks contents are inside inode itself
size_t lengthToRead = fInode->Size();
if (*_length < lengthToRead)
lengthToRead = *_length;
char* offset = (char*)(DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()));
memcpy(buffer, offset, lengthToRead);
*_length = lengthToRead;
return B_OK;
}
status_t
Symlink::_ReadExtentLink(off_t pos, char* buffer, size_t* _length)
{
status_t status;
// First fill up extent, then Symlink block buffer
status = _FillMapEntry();
if (status != B_OK)
return status;
status = _FillBuffer();
if (status != B_OK)
return status;
uint32 offset = 0;
// If it is Version 5 xfs then we have Symlink header
if (fInode->Version() == 3) {
SymlinkHeader* header = (SymlinkHeader*)fSymlinkBuffer;
if (!VerifyHeader<SymlinkHeader>(header, fSymlinkBuffer, fInode, 0, &fMap, 0)) {
ERROR("Invalid data header");
return B_BAD_VALUE;
}
offset += sizeof(SymlinkHeader);
}
size_t lengthToRead = fInode->Size();
if (*_length < lengthToRead)
lengthToRead = *_length;
memcpy(buffer, fSymlinkBuffer + offset, lengthToRead);
*_length = lengthToRead;
return B_OK;
}
status_t
Symlink::ReadLink(off_t pos, char* buffer, size_t* _length)
{
switch (fInode->Format()) {
case XFS_DINODE_FMT_LOCAL:
return _ReadLocalLink(pos, buffer, _length);
case XFS_DINODE_FMT_EXTENTS:
return _ReadExtentLink(pos, buffer, _length);
default:
return B_BAD_VALUE;
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2022, Raghav Sharma, raghavself28@gmail.com
* Distributed under the terms of the MIT License.
*/
#ifndef XFS_SYMLINK_H
#define XFS_SYMLINK_H
#include "Inode.h"
#define SYMLINK_MAGIC 0x58534c4d
// Used only on Version 5
struct SymlinkHeader {
public:
uint32 Magic()
{ return B_BENDIAN_TO_HOST_INT32(sl_magic); }
uint64 Blockno()
{ return B_BENDIAN_TO_HOST_INT64(sl_blkno); }
uuid_t* Uuid()
{ return &sl_uuid; }
uint64 Owner()
{ return B_BENDIAN_TO_HOST_INT64(sl_owner); }
static uint32 ExpectedMagic(int8 whichDirectory, Inode* inode)
{ return SYMLINK_MAGIC; }
static uint32 CRCOffset()
{ return offsetof(SymlinkHeader, sl_crc); }
private:
uint32 sl_magic;
uint32 sl_offset;
uint32 sl_bytes;
public:
uint32 sl_crc;
private:
uuid_t sl_uuid;
uint64 sl_owner;
uint64 sl_blkno;
uint64 sl_lsn;
};
// This class will handle all formats of Symlinks in xfs
class Symlink {
public:
Symlink(Inode* inode);
~Symlink();
status_t ReadLink(off_t pos, char* buffer, size_t* _length);
private:
status_t _FillMapEntry();
status_t _FillBuffer();
status_t _ReadLocalLink(off_t pos, char* buffer, size_t* _length);
status_t _ReadExtentLink(off_t pos, char* buffer, size_t* _length);
Inode* fInode;
ExtentMapEntry fMap;
char* fSymlinkBuffer;
};
#endif

View File

@ -9,6 +9,7 @@
#include "Directory.h"
#include "Inode.h"
#include "ShortAttribute.h"
#include "Symlink.h"
#include "Utility.h"
#include "Volume.h"
@ -353,7 +354,18 @@ static status_t
xfs_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
size_t *_bufferSize)
{
return B_NOT_SUPPORTED;
TRACE("XFS_READ_SYMLINK\n");
Inode* inode = (Inode*)_node->private_node;
if (!inode->IsSymLink())
return B_BAD_VALUE;
Symlink symlink(inode);
status_t result = symlink.ReadLink(0, buffer, _bufferSize);
return result;
}

View File

@ -51,6 +51,7 @@ local xfsSource =
Node.cpp
ShortAttribute.cpp
ShortDirectory.cpp
Symlink.cpp
Volume.cpp
xfs.cpp
;