* Copyright 2022, Raghav Sharma, raghavself28@gmail.com
* Distributed under the terms of the MIT License.
*/
#include "LeafAttribute.h"
#include "VerifyHeader.h"
LeafAttribute::LeafAttribute(Inode* inode)
:
fInode(inode),
fName(NULL),
fMap(NULL),
fLeafBuffer(NULL)
{
fLastEntryOffset = 0;
}
LeafAttribute::~LeafAttribute()
{
delete fMap;
delete[] fLeafBuffer;
}
status_t
LeafAttribute::Init()
{
status_t status = _FillMapEntry();
if (status != B_OK)
return status;
status = _FillLeafBuffer();
if (status != B_OK)
return status;
AttrLeafHeader* header = AttrLeafHeader::Create(fInode, fLeafBuffer);
if (header == NULL)
return B_NO_MEMORY;
if (!VerifyHeader<AttrLeafHeader>(header, fLeafBuffer, fInode, 0, fMap, ATTR_LEAF)) {
ERROR("Invalid data header");
delete header;
return B_BAD_VALUE;
}
delete header;
return B_OK;
}
status_t
LeafAttribute::_FillMapEntry()
{
fMap = new(std::nothrow) ExtentMapEntry;
if (fMap == NULL)
return B_NO_MEMORY;
void* attributeFork = DIR_AFORK_PTR(fInode->Buffer(),
fInode->CoreInodeSize(), fInode->ForkOffset());
uint64* pointerToMap = (uint64*)((char*)attributeFork);
uint64 firstHalf = pointerToMap[0];
uint64 secondHalf = 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
LeafAttribute::_FillLeafBuffer()
{
if (fMap->br_state != 0)
return B_BAD_VALUE;
int len = fInode->DirBlockSize();
fLeafBuffer = new(std::nothrow) char[len];
if (fLeafBuffer == NULL)
return B_NO_MEMORY;
xfs_daddr_t readPos = fInode->FileSystemBlockToAddr(fMap->br_startblock);
if (read_pos(fInode->GetVolume()->Device(), readPos, fLeafBuffer, len) != len) {
ERROR("Extent::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
return B_OK;
}
status_t
LeafAttribute::Open(const char* name, int openMode, attr_cookie** _cookie)
{
TRACE("LeafAttribute::Open\n");
size_t length = strlen(name);
status_t status = Lookup(name, &length);
if (status < B_OK)
return status;
attr_cookie* cookie = new(std::nothrow) attr_cookie;
if (cookie == NULL)
return B_NO_MEMORY;
fName = name;
strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
cookie->open_mode = openMode;
cookie->create = false;
*_cookie = cookie;
return B_OK;
}
status_t
LeafAttribute::Stat(attr_cookie* cookie, struct stat& stat)
{
TRACE("LeafAttribute::Stat\n");
fName = cookie->name;
size_t namelength = strlen(fName);
status_t status;
status = Lookup(fName, &namelength);
if(status != B_OK)
return status;
if (fLocalEntry != NULL) {
uint16 valuelen = B_BENDIAN_TO_HOST_INT16(fLocalEntry->valuelen);
stat.st_type = B_XATTR_TYPE;
stat.st_size = valuelen + fLocalEntry->namelen;
} else {
uint32 valuelen = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valuelen);
stat.st_type = B_XATTR_TYPE;
stat.st_size = valuelen + fRemoteEntry->namelen;
}
return B_OK;
}
status_t
LeafAttribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* length)
{
TRACE("LeafAttribute::Read\n");
if(pos < 0)
return B_BAD_VALUE;
fName = cookie->name;
size_t namelength = strlen(fName);
status_t status;
status = Lookup(fName, &namelength);
if (status != B_OK)
return status;
uint32 lengthToRead = 0;
if (fLocalEntry != NULL) {
uint16 valuelen = B_BENDIAN_TO_HOST_INT16(fLocalEntry->valuelen);
if (pos + *length > valuelen)
lengthToRead = valuelen - pos;
else
lengthToRead = *length;
char* ptrToOffset = (char*) fLocalEntry + sizeof(uint16)
+ sizeof(uint8) + fLocalEntry->namelen;
memcpy(buffer, ptrToOffset, lengthToRead);
*length = lengthToRead;
return B_OK;
} else {
uint32 valuelen = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valuelen);
if (pos + *length > valuelen)
lengthToRead = valuelen - pos;
else
lengthToRead = *length;
uint32 blkno = B_BENDIAN_TO_HOST_INT32(fRemoteEntry->valueblk);
xfs_daddr_t readPos = fInode->FileSystemBlockToAddr(blkno);
if (fInode->Version() == 3)
pos += sizeof(AttrRemoteHeader);
readPos += pos;
if (read_pos(fInode->GetVolume()->Device(), readPos, buffer, lengthToRead)
!= lengthToRead) {
ERROR("Extent::FillBlockBuffer(): IO Error");
return B_IO_ERROR;
}
*length = lengthToRead;
return B_OK;
}
}
status_t
LeafAttribute::GetNext(char* name, size_t* nameLength)
{
TRACE("LeafAttribute::GetNext\n");
AttrLeafHeader* header = AttrLeafHeader::Create(fInode,fLeafBuffer);
AttrLeafEntry* firstEntry = (AttrLeafEntry*)(fLeafBuffer + AttrLeafHeader::Size(fInode));
int totalEntries = header->Count();
delete header;
for (int i = fLastEntryOffset; i < totalEntries; i++) {
AttrLeafEntry* entry =
(AttrLeafEntry*)((char*)firstEntry + i * sizeof(AttrLeafEntry));
uint32 offset = B_BENDIAN_TO_HOST_INT16(entry->nameidx);
TRACE("offset:(%" B_PRIu16 ")\n", offset);
fLastEntryOffset = i + 1;
if (entry->flags & XFS_ATTR_LOCAL) {
AttrLeafNameLocal* local = (AttrLeafNameLocal*)(fLeafBuffer + offset);
memcpy(name, local->nameval, local->namelen);
name[local->namelen] = '\0';
*nameLength = local->namelen + 1;
TRACE("Entry found name : %s, namelength : %ld", name, *nameLength);
return B_OK;
} else {
AttrLeafNameRemote* remote = (AttrLeafNameRemote*)(fLeafBuffer + offset);
memcpy(name, remote->name, remote->namelen);
name[remote->namelen] = '\0';
*nameLength = remote->namelen + 1;
TRACE("Entry found name : %s, namelength : %ld", name, *nameLength);
return B_OK;
}
}
return B_ENTRY_NOT_FOUND;
}
status_t
LeafAttribute::Lookup(const char* name, size_t* nameLength)
{
TRACE("LeafAttribute::Lookup\n");
uint32 hashValueOfRequest = hashfunction(name, *nameLength);
TRACE("Hashval:(%" B_PRIu32 ")\n", hashValueOfRequest);
AttrLeafHeader* header = AttrLeafHeader::Create(fInode,fLeafBuffer);
AttrLeafEntry* entry = (AttrLeafEntry*)(fLeafBuffer + AttrLeafHeader::Size(fInode));
int numberOfLeafEntries = header->Count();
int left = 0;
int right = numberOfLeafEntries - 1;
delete header;
hashLowerBound<AttrLeafEntry>(entry, left, right, hashValueOfRequest);
while (B_BENDIAN_TO_HOST_INT32(entry[left].hashval) == hashValueOfRequest) {
uint32 offset = B_BENDIAN_TO_HOST_INT16(entry[left].nameidx);
TRACE("offset:(%" B_PRIu16 ")\n", offset);
int status;
if (entry[left].flags & XFS_ATTR_LOCAL) {
AttrLeafNameLocal* local = (AttrLeafNameLocal*)(fLeafBuffer + offset);
char* ptrToOffset = (char*)local + sizeof(uint8) + sizeof(uint16);
status = strncmp(name, ptrToOffset, *nameLength);
if (status == 0) {
fLocalEntry = local;
fRemoteEntry = NULL;
return B_OK;
}
} else {
AttrLeafNameRemote* remote = (AttrLeafNameRemote*)(fLeafBuffer + offset);
char* ptrToOffset = (char*)remote + sizeof(uint8) + 2 * sizeof(uint32);
status = strncmp(name, ptrToOffset, *nameLength);
if (status == 0) {
fRemoteEntry = remote;
fLocalEntry = NULL;
return B_OK;
}
}
left++;
}
return B_ENTRY_NOT_FOUND;
}
AttrLeafHeader::~AttrLeafHeader()
{
}
First see which type of directory we reading then
return magic number as per Inode Version.
*/
uint32
AttrLeafHeader::ExpectedMagic(int8 WhichDirectory, Inode* inode)
{
if (WhichDirectory == ATTR_LEAF) {
if (inode->Version() == 1 || inode->Version() == 2)
return XFS_ATTR_LEAF_MAGIC;
else
return XFS_ATTR3_LEAF_MAGIC;
} else {
return B_BAD_VALUE;
}
}
uint32
AttrLeafHeader::CRCOffset()
{
return offsetof(AttrLeafHeaderV5::OnDiskData, info.crc);
}
AttrLeafHeader*
AttrLeafHeader::Create(Inode* inode, const char* buffer)
{
if (inode->Version() == 1 || inode->Version() == 2) {
AttrLeafHeaderV4* header = new (std::nothrow) AttrLeafHeaderV4(buffer);
return header;
} else {
AttrLeafHeaderV5* header = new (std::nothrow) AttrLeafHeaderV5(buffer);
return header;
}
}
This Function returns Actual size of leaf header
in all forms of directory.
Never use sizeof() operator because we now have
vtable as well and it will give wrong results
*/
uint32
AttrLeafHeader::Size(Inode* inode)
{
if (inode->Version() == 1 || inode->Version() == 2)
return sizeof(AttrLeafHeaderV4::OnDiskData);
else
return sizeof(AttrLeafHeaderV5::OnDiskData);
}
void
AttrLeafHeaderV4::SwapEndian()
{
fData.info.forw = B_BENDIAN_TO_HOST_INT32(fData.info.forw);
fData.info.back = B_BENDIAN_TO_HOST_INT32(fData.info.back);
fData.info.magic = B_BENDIAN_TO_HOST_INT16(fData.info.magic);
fData.info.pad = B_BENDIAN_TO_HOST_INT16(fData.info.pad);
fData.count = B_BENDIAN_TO_HOST_INT16(fData.count);
fData.usedbytes = B_BENDIAN_TO_HOST_INT16(fData.usedbytes);
fData.firstused = B_BENDIAN_TO_HOST_INT16(fData.firstused);
}
AttrLeafHeaderV4::AttrLeafHeaderV4(const char* buffer)
{
memcpy(&fData, buffer, sizeof(fData));
SwapEndian();
}
AttrLeafHeaderV4::~AttrLeafHeaderV4()
{
}
uint16
AttrLeafHeaderV4::Magic()
{
return fData.info.magic;
}
uint64
AttrLeafHeaderV4::Blockno()
{
return B_BAD_VALUE;
}
uint64
AttrLeafHeaderV4::Owner()
{
return B_BAD_VALUE;
}
const uuid_t&
AttrLeafHeaderV4::Uuid()
{
static uuid_t nullUuid = {0};
return nullUuid;
}
uint16
AttrLeafHeaderV4::Count()
{
return fData.count;
}
void
AttrLeafHeaderV5::SwapEndian()
{
fData.info.forw = B_BENDIAN_TO_HOST_INT32(fData.info.forw);
fData.info.back = B_BENDIAN_TO_HOST_INT32(fData.info.back);
fData.info.magic = B_BENDIAN_TO_HOST_INT16(fData.info.magic);
fData.info.pad = B_BENDIAN_TO_HOST_INT16(fData.info.pad);
fData.info.blkno = B_BENDIAN_TO_HOST_INT64(fData.info.blkno);
fData.info.lsn = B_BENDIAN_TO_HOST_INT64(fData.info.lsn);
fData.info.owner = B_BENDIAN_TO_HOST_INT64(fData.info.owner);
fData.count = B_BENDIAN_TO_HOST_INT16(fData.count);
fData.usedbytes = B_BENDIAN_TO_HOST_INT16(fData.usedbytes);
fData.firstused = B_BENDIAN_TO_HOST_INT16(fData.firstused);
fData.pad2 = B_BENDIAN_TO_HOST_INT32(fData.pad2);
}
AttrLeafHeaderV5::AttrLeafHeaderV5(const char* buffer)
{
memcpy(&fData, buffer, sizeof(fData));
SwapEndian();
}
AttrLeafHeaderV5::~AttrLeafHeaderV5()
{
}
uint16
AttrLeafHeaderV5::Magic()
{
return fData.info.magic;
}
uint64
AttrLeafHeaderV5::Blockno()
{
return fData.info.blkno;
}
uint64
AttrLeafHeaderV5::Owner()
{
return fData.info.owner;
}
const uuid_t&
AttrLeafHeaderV5::Uuid()
{
return fData.info.uuid;
}
uint16
AttrLeafHeaderV5::Count()
{
return fData.count;
}