root/src/add-ons/kernel/file_systems/xfs/LeafAttribute.cpp
/*
 * Copyright 2022, Raghav Sharma, raghavself28@gmail.com
 * Distributed under the terms of the MIT License.
 */


#include "LeafAttribute.h"

#include "Utility.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];
                //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
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;

        // initialize the cookie
        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;

        // check if this attribute exists
        status = Lookup(fName, &namelength);

        if(status != B_OK)
                return status;

        // We have valid attribute entry to stat
        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;

                // For remote value blocks, value is stored in seperate file system block
                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;

                // TODO : Implement remote header checks for V5 file system
                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;

                // First check if its local or remote value
                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;

                // First check if its local or remote value
                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 {
                // currently we don't support other directories;
                return B_BAD_VALUE;
        }
}


uint32
AttrLeafHeader::CRCOffset()
{
        return offsetof(AttrLeafHeaderV5::OnDiskData, info.crc);
}


//Function to get V4 or V5 Attr leaf header instance
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;
}