⛏️ index : haiku.git

/*
 * Copyright 2017, ChαΊΏ VΕ© Gia Hy, cvghy116@gmail.com.
 * Copyright 2010-2011, JΓ©rΓ΄me Duval, korli@users.berlios.de.
 * Copyright 2010, FranΓ§ois Revol, <revol@free.fr>.
 * Copyright 2004-2008, Axel DΓΆrfler, axeld@pinc-software.de.
 * This file may be used under the terms of the MIT License.
 */

//!	connection between pure inode and kernel_interface attributes


#include "Attribute.h"
#include "BTree.h"
#include "CRCTable.h"
#include "Utility.h"


//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
#	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
#	define TRACE(x...) ;
#endif


Attribute::Attribute(Inode* inode)
	:
	fVolume(inode->GetVolume()),
	fInode(inode),
	fName(NULL)
{
}


Attribute::Attribute(Inode* inode, attr_cookie* cookie)
	:
	fVolume(inode->GetVolume()),
	fInode(inode),
	fName(cookie->name)
{
}


Attribute::~Attribute()
{
}


status_t
Attribute::CheckAccess(const char* name, int openMode)
{
	return fInode->CheckPermissions(open_mode_to_access(openMode));
}


status_t
Attribute::Open(const char* name, int openMode, attr_cookie** _cookie)
{
	TRACE("Open\n");
	status_t status = CheckAccess(name, openMode);
	if (status < B_OK)
		return status;

	status = _Lookup(name, strlen(name));
	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
Attribute::Stat(struct stat& stat)
{
	TRACE("Stat\n");

	size_t nameLength = strlen(fName);
	btrfs_dir_entry* entries;
	uint32 length;
	status_t status = _Lookup(fName, nameLength, &entries, &length);
	if (status < B_OK)
		return status;

	btrfs_dir_entry* entry;
	status = _FindEntry(entries, length, fName, nameLength, &entry);
	if (status != B_OK) {
		free(entries);
		return status;
	}

	// found an entry to stat
	stat.st_type = B_XATTR_TYPE;
	stat.st_size = entry->DataLength();
	free(entries);
	return B_OK;
}


status_t
Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length)
{
	if (pos < 0LL)
		return ERANGE;

	size_t nameLength = strlen(fName);
	btrfs_dir_entry* entries;
	uint32 length;
	status_t status = _Lookup(fName, nameLength, &entries, &length);
	if (status < B_OK)
		return status;

	btrfs_dir_entry* entry;
	status = _FindEntry(entries, length, fName, nameLength, &entry);
	if (status != B_OK) {
		free(entries);
		return status;
	}

	// found an entry to read
	if (pos + *_length > entry->DataLength())
		length = entry->DataLength() - pos;
	else
		length = *_length - pos;
	memcpy(buffer, (uint8*)entry + entry->NameLength()
		+ sizeof(btrfs_dir_entry) + (uint32)pos, length);
	*_length = length;

	free(entries);
	return B_OK;
}


status_t
Attribute::_Lookup(const char* name, size_t nameLength,
	btrfs_dir_entry** _entries, uint32* _length)
{
	uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength);
	struct btrfs_key key;
	key.SetType(BTRFS_KEY_TYPE_XATTR_ITEM);
	key.SetObjectID(fInode->ID());
	key.SetOffset(hash);
	BTree::Path path(fInode->GetVolume()->FSTree());

	btrfs_dir_entry* entries;
	uint32 length;
	status_t status = fInode->GetVolume()->FSTree()->FindExact(&path, key,
		(void**)&entries, &length);
	if (status != B_OK) {
		TRACE("AttributeIterator::Lookup(): Couldn't find entry with hash %"
			B_PRIu32 " \"%s\"\n", hash, name);
		return status;
	}

	if (_entries == NULL)
		free(entries);
	else
		*_entries = entries;

	if (_length != NULL)
		*_length = length;

	return B_OK;
}


status_t
Attribute::_FindEntry(btrfs_dir_entry* entries, size_t length,
	const char* name, size_t nameLength, btrfs_dir_entry** _entry)
{
	btrfs_dir_entry* entry = entries;
	uint16 current = 0;
	while (current < length) {
		current += entry->Length();
		break;
		// TODO there could be several entries with the same name hash
		entry = (btrfs_dir_entry*)((uint8*)entry + entry->Length());
	}

	*_entry = entry;
	return B_OK;
}