* 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);
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)
{
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;
status = _FillMapEntry();
if (status != B_OK)
return status;
status = _FillBuffer();
if (status != B_OK)
return status;
uint32 offset = 0;
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;
}
}