⛏️ index : haiku.git

/*
 * Copyright 2022, Raghav Sharma, raghavself28@gmail.com
 * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com
 * All rights reserved. Distributed under the terms of the MIT License.
 */


#include "Extent.h"

#include "VerifyHeader.h"


Extent::Extent(Inode* inode)
	:
	fInode(inode),
	fOffset(0)
{
}


Extent::~Extent()
{
}


void
Extent::FillMapEntry(void* pointerToMap)
{
	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);
}


status_t
Extent::FillBlockBuffer()
{
	if (fMap->br_state != 0)
		return B_BAD_VALUE;

	int len = fInode->DirBlockSize();
	fBlockBuffer = new(std::nothrow) char[len];
	if (fBlockBuffer == NULL)
		return B_NO_MEMORY;

	xfs_daddr_t readPos =
		fInode->FileSystemBlockToAddr(fMap->br_startblock);

	if (read_pos(fInode->GetVolume()->Device(), readPos, fBlockBuffer, len)
		!= len) {
		ERROR("Extent::FillBlockBuffer(): IO Error");
		return B_IO_ERROR;
	}

	return B_OK;
}


status_t
Extent::Init()
{
	fMap = new(std::nothrow) ExtentMapEntry;
	if (fMap == NULL)
		return B_NO_MEMORY;

	ASSERT(IsBlockType() == true);
	void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize());
	FillMapEntry(pointerToMap);
	ASSERT(fMap->br_blockcount == 1);
		// TODO: This is always true for block directories
		// If we use this implementation for leaf directories, this is not
		// always true
	status_t status = FillBlockBuffer();
	if (status != B_OK)
		return status;

	ExtentDataHeader* header = ExtentDataHeader::Create(fInode, fBlockBuffer);
	if (header == NULL)
		return B_NO_MEMORY;
	if (!VerifyHeader<ExtentDataHeader>(header, fBlockBuffer, fInode, 0, fMap, XFS_BLOCK)) {
		status = B_BAD_VALUE;
		ERROR("Extent:Init(): Bad Block!\n");
	}

	delete header;
	return status;
}


ExtentBlockTail*
Extent::BlockTail()
{
	return (ExtentBlockTail*)
		(fBlockBuffer + fInode->DirBlockSize() - sizeof(ExtentBlockTail));
}


uint32
Extent::GetOffsetFromAddress(uint32 address)
{
	address = address * 8;
		// block offset in eight bytes, hence multiple with 8
	return address & (fInode->DirBlockSize() - 1);
}


ExtentLeafEntry*
Extent::BlockFirstLeaf(ExtentBlockTail* tail)
{
	return (ExtentLeafEntry*)tail - B_BENDIAN_TO_HOST_INT32(tail->count);
}


bool
Extent::IsBlockType()
{
	bool status = true;
	if (fInode->BlockCount() != 1)
		status = false;
	if (fInode->Size() != fInode->DirBlockSize())
		status = false;
	void* pointerToMap = DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize());
	xfs_fileoff_t startoff = (*((uint64*)pointerToMap) & MASK(63)) >> 9;
	if (startoff != 0)
		status = false;
	return status;
}


int
Extent::EntrySize(int len) const
{
	int entrySize = sizeof(xfs_ino_t) + sizeof(uint8) + len + sizeof(uint16);
			// uint16 is for the tag
	if (fInode->HasFileTypeField())
		entrySize += sizeof(uint8);

	return (entrySize + 7) & -8;
			// rounding off to closest multiple of 8
}


status_t
Extent::GetNext(char* name, size_t* length, xfs_ino_t* ino)
{
	TRACE("Extend::GetNext\n");

	void* entry; // This could be unused entry so we should check

	entry = (void*)(fBlockBuffer + ExtentDataHeader::Size(fInode));

	int numberOfEntries = B_BENDIAN_TO_HOST_INT32(BlockTail()->count);
	int numberOfStaleEntries = B_BENDIAN_TO_HOST_INT32(BlockTail()->stale);

	// We don't read stale entries.
	numberOfEntries -= numberOfStaleEntries;
	TRACE("numberOfEntries:(%" B_PRId32 ")\n", numberOfEntries);
	uint16 currentOffset = (char*)entry - fBlockBuffer;

	for (int i = 0; i < numberOfEntries; i++) {
		ExtentUnusedEntry* unusedEntry = (ExtentUnusedEntry*)entry;

		if (B_BENDIAN_TO_HOST_INT16(unusedEntry->freetag) == DIR2_FREE_TAG) {
			TRACE("Unused entry found\n");
			currentOffset += B_BENDIAN_TO_HOST_INT16(unusedEntry->length);
			entry = (void*)
				((char*)entry + B_BENDIAN_TO_HOST_INT16(unusedEntry->length));
			i--;
			continue;
		}
		ExtentDataEntry* dataEntry = (ExtentDataEntry*) entry;

		if (fOffset >= currentOffset) {
			entry = (void*)((char*)entry + EntrySize(dataEntry->namelen));
			currentOffset += EntrySize(dataEntry->namelen);
			continue;
		}

		if ((size_t)(dataEntry->namelen) >= *length)
				return B_BUFFER_OVERFLOW;

		fOffset = currentOffset;
		memcpy(name, dataEntry->name, dataEntry->namelen);
		name[dataEntry->namelen] = '\0';
		*length = dataEntry->namelen + 1;
		*ino = B_BENDIAN_TO_HOST_INT64(dataEntry->inumber);

		TRACE("Entry found. Name: (%s), Length: (%" B_PRIuSIZE "),ino: (%" B_PRIu64 ")\n", name,
			*length, *ino);
		return B_OK;
	}

	return B_ENTRY_NOT_FOUND;
}


status_t
Extent::Lookup(const char* name, size_t length, xfs_ino_t* ino)
{
	TRACE("Extent: Lookup\n");
	TRACE("Name: %s\n", name);
	uint32 hashValueOfRequest = hashfunction(name, length);
	TRACE("Hashval:(%" B_PRIu32 ")\n", hashValueOfRequest);
	ExtentBlockTail* blockTail = BlockTail();
	ExtentLeafEntry* leafEntry = BlockFirstLeaf(blockTail);

	int numberOfLeafEntries = B_BENDIAN_TO_HOST_INT32(blockTail->count);
	int left = 0;
	int right = numberOfLeafEntries - 1;

	hashLowerBound<ExtentLeafEntry>(leafEntry, left, right, hashValueOfRequest);

	while (B_BENDIAN_TO_HOST_INT32(leafEntry[left].hashval)
			== hashValueOfRequest) {

		uint32 address = B_BENDIAN_TO_HOST_INT32(leafEntry[left].address);
		if (address == 0) {
			left++;
			continue;
		}

		uint32 offset = GetOffsetFromAddress(address);
		TRACE("offset:(%" B_PRIu32 ")\n", offset);
		ExtentDataEntry* entry = (ExtentDataEntry*)(fBlockBuffer + offset);

		int retVal = strncmp(name, (char*)entry->name, entry->namelen);
		if (retVal == 0) {
			*ino = B_BENDIAN_TO_HOST_INT64(entry->inumber);
			TRACE("ino:(%" B_PRIu64 ")\n", *ino);
			return B_OK;
		}
		left++;
	}

	return B_ENTRY_NOT_FOUND;
}


ExtentDataHeader::~ExtentDataHeader()
{
}


/*
	First see which type of directory we reading then
	return magic number as per Inode Version.
*/
uint32
ExtentDataHeader::ExpectedMagic(int8 WhichDirectory, Inode* inode)
{
	if (WhichDirectory == XFS_BLOCK) {
		if (inode->Version() == 1 || inode->Version() == 2)
			return DIR2_BLOCK_HEADER_MAGIC;
		else
			return DIR3_BLOCK_HEADER_MAGIC;
	} else {
		if (inode->Version() == 1 || inode->Version() == 2)
			return V4_DATA_HEADER_MAGIC;
		else
			return V5_DATA_HEADER_MAGIC;
	}
}


uint32
ExtentDataHeader::CRCOffset()
{
	return offsetof(ExtentDataHeaderV5::OnDiskData, crc);
}


ExtentDataHeader*
ExtentDataHeader::Create(Inode* inode, const char* buffer)
{
	if (inode->Version() == 1 || inode->Version() == 2) {
		ExtentDataHeaderV4* header = new (std::nothrow) ExtentDataHeaderV4(buffer);
		return header;
	} else {
		ExtentDataHeaderV5* header = new (std::nothrow) ExtentDataHeaderV5(buffer);
		return header;
	}
}


/*
	This Function returns Actual size of data header
	in all forms of directory.
	Never use sizeof() operator because we now have
	vtable as well and it will give wrong results
*/
uint32
ExtentDataHeader::Size(Inode* inode)
{
	if (inode->Version() == 1 || inode->Version() == 2)
		return sizeof(ExtentDataHeaderV4::OnDiskData);
	else
		return sizeof(ExtentDataHeaderV5::OnDiskData);
}


void
ExtentDataHeaderV4::_SwapEndian()
{
	fData.magic = (B_BENDIAN_TO_HOST_INT32(fData.magic));
}


ExtentDataHeaderV4::ExtentDataHeaderV4(const char* buffer)
{
	memcpy(&fData, buffer, sizeof(fData));
	_SwapEndian();
}


ExtentDataHeaderV4::~ExtentDataHeaderV4()
{
}


uint32
ExtentDataHeaderV4::Magic()
{
	return fData.magic;
}


uint64
ExtentDataHeaderV4::Blockno()
{
	return B_BAD_VALUE;
}


uint64
ExtentDataHeaderV4::Lsn()
{
	return B_BAD_VALUE;
}


uint64
ExtentDataHeaderV4::Owner()
{
	return B_BAD_VALUE;
}


const uuid_t&
ExtentDataHeaderV4::Uuid()
{
	static uuid_t nullUuid;
	return nullUuid;
}


void
ExtentDataHeaderV5::_SwapEndian()
{
	fData.magic	=	B_BENDIAN_TO_HOST_INT32(fData.magic);
	fData.blkno	=	B_BENDIAN_TO_HOST_INT64(fData.blkno);
	fData.lsn		=	B_BENDIAN_TO_HOST_INT64(fData.lsn);
	fData.owner	=	B_BENDIAN_TO_HOST_INT64(fData.owner);
	fData.pad		=	B_BENDIAN_TO_HOST_INT32(fData.pad);
}


ExtentDataHeaderV5::ExtentDataHeaderV5(const char* buffer)
{
	memcpy(&fData, buffer, sizeof(fData));
	_SwapEndian();
}


ExtentDataHeaderV5::~ExtentDataHeaderV5()
{
}


uint32
ExtentDataHeaderV5::Magic()
{
	return fData.magic;
}


uint64
ExtentDataHeaderV5::Blockno()
{
	return fData.blkno;
}


uint64
ExtentDataHeaderV5::Lsn()
{
	return fData.lsn;
}


uint64
ExtentDataHeaderV5::Owner()
{
	return fData.owner;
}


const uuid_t&
ExtentDataHeaderV5::Uuid()
{
	return fData.uuid;
}