⛏️ index : haiku.git

/*
 * Copyright 2010, 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 "Utility.h"

#include <stdio.h>


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


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


Attribute::Attribute(Inode* inode, attr_cookie* cookie)
	:
	fVolume(inode->GetVolume()),
	fBlock(fVolume),
	fInode(inode),
	fBodyEntry(NULL),
	fBlockEntry(NULL),
	fName(NULL)
{
	Find(cookie->name);
}


Attribute::~Attribute()
{
	Put();
}


status_t
Attribute::InitCheck()
{
	return (fBodyEntry != NULL || fBlockEntry != NULL) ? B_OK : B_NO_INIT;
}


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


status_t
Attribute::Find(const char* name)
{
	return _Find(name, -1);
}


status_t
Attribute::Find(int32 index)
{
	return _Find(NULL, index);
}


status_t
Attribute::GetName(char* name, size_t* _nameLength)
{
	if (fBodyEntry == NULL && fBlockEntry == NULL)
		return B_NO_INIT;
	if (fBodyEntry != NULL)
		return _PrefixedName(fBodyEntry, name, _nameLength);
	else
		return _PrefixedName(fBlockEntry, name, _nameLength);
}


void
Attribute::Put()
{
	if (fBodyEntry != NULL) {
		recursive_lock_unlock(&fInode->SmallDataLock());
		fBlock.Unset();
		fBodyEntry = NULL;
	}

	if (fBlockEntry != NULL) {
		fBlock.Unset();
		fBlockEntry = NULL;
	}
}


status_t
Attribute::Create(const char* name, type_code type, int openMode,
	attr_cookie** _cookie)
{
	status_t status = CheckAccess(name, openMode);
	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->type = type;
	cookie->open_mode = openMode;
	cookie->create = true;

	if (Find(name) == B_OK) {
		// attribute already exists
		if ((openMode & O_TRUNC) != 0)
			_Truncate();
	}
	*_cookie = cookie;
	return B_OK;
}


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 = Find(name);
	if (status < B_OK)
		return status;

	attr_cookie* cookie = new(std::nothrow) attr_cookie;
	if (cookie == NULL)
		return B_NO_MEMORY;

	// initialize the cookie
	strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
	cookie->open_mode = openMode;
	cookie->create = false;

	// Should we truncate the attribute?
	if ((openMode & O_TRUNC) != 0)
		_Truncate();

	*_cookie = cookie;
	return B_OK;
}


status_t
Attribute::Stat(struct stat& stat)
{
	TRACE("Stat\n");
	if (fBodyEntry == NULL && fBlockEntry == NULL)
		return B_NO_INIT;

	stat.st_type = B_XATTR_TYPE;
	
	if (fBodyEntry != NULL)
		stat.st_size = fBodyEntry->ValueSize();
	else if (fBlockEntry != NULL)
		stat.st_size = fBlockEntry->ValueSize();
	
	return B_OK;
}


status_t
Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length)
{
	if (fBodyEntry == NULL && fBlockEntry == NULL)
		return B_NO_INIT;

	if (pos < 0LL)
		return ERANGE;

	size_t length = *_length;
	const uint8* start = (uint8 *)fBlock.Block();
	if (fBlockEntry != NULL) {
		pos += fBlockEntry->ValueOffset();
		if (((uint32)pos + length) > fVolume->BlockSize()
			|| length > fBlockEntry->ValueSize())
			return ERANGE;
	} else {
		start += fVolume->InodeBlockIndex(fInode->ID()) * fVolume->InodeSize();
		const uint8* end = start + fVolume->InodeSize();
		start += EXT2_INODE_NORMAL_SIZE + fInode->Node().ExtraInodeSize()
			+ sizeof(uint32);
		pos += fBodyEntry->ValueOffset();
		if ((off_t)(pos + length) > (end - start) || length > fBodyEntry->ValueSize())
			return ERANGE;
	}
	memcpy(buffer, start + (uint32)pos, length);

	*_length = length;
	return B_OK;
}


status_t
Attribute::Write(Transaction& transaction, attr_cookie* cookie, off_t pos,
	const uint8* buffer, size_t* _length, bool* _created)
{
	if (!cookie->create && fBodyEntry == NULL && fBlockEntry == NULL)
		return B_NO_INIT;

	// TODO: Implement
	return B_ERROR;
}


status_t
Attribute::_Truncate()
{
	// TODO: Implement
	return B_ERROR;
}


status_t
Attribute::_Find(const char* name, int32 index)
{
	Put();

	fName = name;

	// try to find it in the small data region
	if (fInode->HasExtraAttributes() 
		&& recursive_lock_lock(&fInode->SmallDataLock()) == B_OK) {
		off_t blockNum;
		fVolume->GetInodeBlock(fInode->ID(), blockNum);
		
		if (blockNum != 0) {
			fBlock.SetTo(blockNum);
			const uint8* start = fBlock.Block() 
				+ fVolume->InodeBlockIndex(fInode->ID()) * fVolume->InodeSize();
			const uint8* end = start + fVolume->InodeSize();
			int32 count = 0;
			if (_FindAttributeBody(start + EXT2_INODE_NORMAL_SIZE 
				+ fInode->Node().ExtraInodeSize(), end, name, index, &count, 
				&fBodyEntry) == B_OK)
				return B_OK;
			index -= count;
		}

		recursive_lock_unlock(&fInode->SmallDataLock());
		fBlock.Unset();
	}

	// then, search in the attribute directory
	if (fInode->Node().ExtendedAttributesBlock() != 0) {
		fBlock.SetTo(fInode->Node().ExtendedAttributesBlock());
		if (_FindAttributeBlock(fBlock.Block(), 
			fBlock.Block() + fVolume->BlockSize(), name, index, NULL, 
			&fBlockEntry) == B_OK)
			return B_OK;
		fBlock.Unset();
	}

	return B_ENTRY_NOT_FOUND;
}


status_t
Attribute::_FindAttributeBody(const uint8* start, const uint8* end, 
	const char* name, int32 index, int32 *count, ext2_xattr_entry** _entry)
{
	TRACE("_FindAttributeBody %p %p %s\n", start, end, name);
	if (*((uint32*)start) != EXT2_XATTR_MAGIC)
		return B_BAD_DATA;
	return _FindAttribute(start + sizeof(uint32), end, name, index, count,
		_entry);
}


status_t
Attribute::_FindAttributeBlock(const uint8* start, const uint8* end, const char* name,
	int32 index, int32 *count, ext2_xattr_entry** _entry)
{
	TRACE("_FindAttributeBlock %p %p %s\n", start, end, name);
	ext2_xattr_header *header = (ext2_xattr_header*)start;
	if (!header->IsValid())
		return B_BAD_DATA;
	
	return _FindAttribute(start + sizeof(ext2_xattr_header), end, name, index, 
		count, _entry);
}


status_t
Attribute::_FindAttribute(const uint8* start, const uint8* end, const char* name,
	int32 index, int32 *count, ext2_xattr_entry** _entry)
{
	TRACE("_FindAttribute %p %p %s\n", start, end, name);
	char buffer[EXT2_XATTR_NAME_LENGTH];
	
	int32 i = 0;
	while (start < end) {
		ext2_xattr_entry* entry = (ext2_xattr_entry*)start;
		if (!entry->IsValid())
			break;
		
		size_t length = EXT2_XATTR_NAME_LENGTH;
		if ((name != NULL && _PrefixedName(entry, buffer, &length) == B_OK
				&& strncmp(name, buffer, length) == 0) || index == i) {
			*_entry = entry;
			return B_OK;
		}
		start += entry->Length();
		i++;
	}

	if (count != NULL)
		*count = i;
	return B_ENTRY_NOT_FOUND;
}


status_t
Attribute::_PrefixedName(ext2_xattr_entry* entry, char* _name, size_t* _nameLength)
{
	const char *indexNames[] = { "0", "user" };
	size_t l = 0;
	
	if (entry->NameIndex() < ((sizeof(indexNames) / sizeof(indexNames[0]))))
		l = snprintf(_name, *_nameLength, "%s.%s.%.*s",
			"linux", indexNames[entry->NameIndex()], entry->NameLength(),
			entry->name);
	else
		l = snprintf(_name, *_nameLength, "%s.%d.%.*s",
			"linux", entry->NameIndex(), entry->NameLength(), entry->name);
	if (l < 1 || l > *_nameLength - 1)
		return ENOBUFS;
	
	*_nameLength = l + 1;
	_name[l] = '\0';

	return B_OK;
}