diff --git a/src/add-ons/kernel/file_systems/xfs/Jamfile b/src/add-ons/kernel/file_systems/xfs/Jamfile index 6f02064b01..e843550054 100644 --- a/src/add-ons/kernel/file_systems/xfs/Jamfile +++ b/src/add-ons/kernel/file_systems/xfs/Jamfile @@ -32,6 +32,7 @@ local xfsSources = Node.cpp ShortAttribute.cpp ShortDirectory.cpp + Symlink.cpp Volume.cpp xfs.cpp ; diff --git a/src/add-ons/kernel/file_systems/xfs/Symlink.cpp b/src/add-ons/kernel/file_systems/xfs/Symlink.cpp new file mode 100644 index 0000000000..ca53797bd4 --- /dev/null +++ b/src/add-ons/kernel/file_systems/xfs/Symlink.cpp @@ -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(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; + } +} \ No newline at end of file diff --git a/src/add-ons/kernel/file_systems/xfs/Symlink.h b/src/add-ons/kernel/file_systems/xfs/Symlink.h new file mode 100644 index 0000000000..fd5b55cf53 --- /dev/null +++ b/src/add-ons/kernel/file_systems/xfs/Symlink.h @@ -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 \ No newline at end of file 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 cb1b6c9965..0a7420f430 100644 --- a/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/xfs/kernel_interface.cpp @@ -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; } diff --git a/src/tests/add-ons/kernel/file_systems/xfs/xfs_shell/Jamfile b/src/tests/add-ons/kernel/file_systems/xfs/xfs_shell/Jamfile index 72e4432a65..be3342c198 100644 --- a/src/tests/add-ons/kernel/file_systems/xfs/xfs_shell/Jamfile +++ b/src/tests/add-ons/kernel/file_systems/xfs/xfs_shell/Jamfile @@ -51,6 +51,7 @@ local xfsSource = Node.cpp ShortAttribute.cpp ShortDirectory.cpp + Symlink.cpp Volume.cpp xfs.cpp ;