⛏️ index : haiku.git

/*
 * 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];
		//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;
}