⛏️ index : haiku.git

// AttributeDirectory.cpp

#include <new>

#include <stdlib.h>
#include <string.h>

#include <AutoDeleter.h>
#include <Node.h>

#include "AttributeDirectory.h"

// small data size
static const int32 kSmallAttributeSize = 8;

// constructor
Attribute::Attribute(const char* name, const attr_info& info,
	const void* data)
	: fInfo(info)
{
	char* nameBuffer = fDataAndName;

	// copy data, if any
	if (data) {
		nameBuffer += info.size;
		memcpy(fDataAndName, data, info.size);

		// store a negative size to indicate we also have the data
		fInfo.size = -info.size;
	}

	// copy the name
	strcpy(nameBuffer, name);
}

// destructor
Attribute::~Attribute()
{
}

// CreateAttribute
status_t
Attribute::CreateAttribute(const char* name, const attr_info& info,
	const void* data, Attribute** attribute)
{
	if (!name || !attribute)
		return B_BAD_VALUE;

	// compute the size
	int32 nameLen = strlen(name);
	int32 size = sizeof(Attribute) + nameLen;
	if (data)
		size += info.size;

	void* buffer = malloc(size);
	if (!buffer)
		return B_NO_MEMORY;

	*attribute = new(buffer) Attribute(name, info, data);
	return B_OK;
}

// DeleteAttribute
void
Attribute::DeleteAttribute(Attribute* attribute)
{
	if (attribute) {
		attribute->~Attribute();
		free(attribute);
	}
}

// GetName
const char*
Attribute::GetName() const
{
	return (fInfo.size >= 0 ? fDataAndName : fDataAndName - fInfo.size);
}

// GetInfo
void
Attribute::GetInfo(attr_info* info) const
{
	if (info) {
		info->type = fInfo.type;
		info->size = GetSize();
	}
}

// GetType
uint32
Attribute::GetType() const
{
	return fInfo.type;
}

// GetSize
off_t
Attribute::GetSize() const
{
	return (fInfo.size >= 0 ? fInfo.size : -fInfo.size);
}

// GetData
const void*
Attribute::GetData() const
{
	return (fInfo.size >= 0 ? NULL : fDataAndName);
}


// #pragma mark -

// constructor
AttributeDirectory::AttributeDirectory()
	: fAttributes(),
	  fStatus(ATTRIBUTE_DIRECTORY_NOT_LOADED)
{
}

// destructor
AttributeDirectory::~AttributeDirectory()
{
	ClearAttrDir();
}

// GetAttrDirStatus
uint32
AttributeDirectory::GetAttrDirStatus() const
{
	return fStatus;
}

// IsAttrDirValid
bool
AttributeDirectory::IsAttrDirValid() const
{
	return (fStatus == ATTRIBUTE_DIRECTORY_VALID);
}

// LoadAttrDir
status_t
AttributeDirectory::LoadAttrDir()
{
	// nothing to do, if already loaded
	if (fStatus == ATTRIBUTE_DIRECTORY_VALID)
		return B_OK;

	// if we tried earlier and the attr dir was too big, we fail again
	if (fStatus == ATTRIBUTE_DIRECTORY_TOO_BIG)
		return B_ERROR;

	// open the node
	BNode node;
	status_t error = OpenNode(node);
	if (error != B_OK)
		return error;

	// iterate through the attribute directory
	char name[B_ATTR_NAME_LENGTH];
	while (node.GetNextAttrName(name) == B_OK) {
		// get the attribute data
		attr_info info;
		char data[kSmallAttributeSize];
		bool dataLoaded = false;
		error = _LoadAttribute(node, name, info, data, dataLoaded);
		if (error != B_OK)
			break;

		// create the attribute
		error = AddAttribute(name, info, (dataLoaded ? data : NULL));
	}

	if (error != B_OK)
		ClearAttrDir();
// TODO: Enforce maximum attribute directory size.

	return error;
}

// ClearAttrDir
void
AttributeDirectory::ClearAttrDir()
{
	while (Attribute* attribute = GetFirstAttribute())
		RemoveAttribute(attribute);
}

// AddAttribute
status_t
AttributeDirectory::AddAttribute(const char* name, const attr_info& info,
	const void* data)
{
	if (!name || GetAttribute(name))
		return B_BAD_VALUE;

	// create the attribute
	Attribute* attribute;
	status_t error = Attribute::CreateAttribute(name, info, data, &attribute);
	if (error != B_OK)
		return error;

	// add the attribute
	fAttributes.Insert(attribute);

	return B_OK;
}

// RemoveAttribute
bool
AttributeDirectory::RemoveAttribute(const char* name)
{
	if (!name)
		return false;

	for (SLList<Attribute>::Iterator it = fAttributes.GetIterator();
		 it.HasNext();) {
		Attribute* attribute = it.Next();
		if (strcmp(attribute->GetName(), name) == 0) {
			it.Remove();
			Attribute::DeleteAttribute(attribute);
			return true;
		}
	}

	return false;
}

// RemoveAttribute
void
AttributeDirectory::RemoveAttribute(Attribute* attribute)
{
	if (!attribute)
		return;

	fAttributes.Remove(attribute);
	Attribute::DeleteAttribute(attribute);
}

// UpdateAttribute
status_t
AttributeDirectory::UpdateAttribute(const char* name, bool* removed,
	attr_info* _info, const void** _data)
{
	if (!name || !removed)
		return B_BAD_VALUE;

	// open the node
	BNode node;
	status_t error = OpenNode(node);
	if (error != B_OK) {
		ClearAttrDir();
		if (fStatus == ATTRIBUTE_DIRECTORY_VALID)
			fStatus = ATTRIBUTE_DIRECTORY_NOT_LOADED;
		return error;
	}

	// get the attribute data
	attr_info info;
	char data[kSmallAttributeSize];
	bool dataLoaded = false;
	error = _LoadAttribute(node, name, info,
		(fStatus == ATTRIBUTE_DIRECTORY_VALID ? data : NULL), dataLoaded);
	if (error == B_OK) {
		if (fStatus == ATTRIBUTE_DIRECTORY_VALID) {
			// remove the attribute
			Attribute* previous = NULL;
			for (SLList<Attribute>::Iterator it = fAttributes.GetIterator();
				 it.HasNext();) {
				Attribute* attribute = it.Next();
				if (strcmp(attribute->GetName(), name) == 0) {
					it.Remove();
					Attribute::DeleteAttribute(attribute);
					break;
				}
				previous = attribute;
			}

// TODO: Enforce the maximum attr dir size.
			// re-create the attribute
			Attribute* attribute;
			error = Attribute::CreateAttribute(name, info, data,
				&attribute);
			if (error == B_OK) {
				// add the attribute
				fAttributes.InsertAfter(previous, attribute);

				// return the desired info
				if (_info)
					attribute->GetInfo(_info);
				if (_data)
					*_data = attribute->GetData();
				*removed = false;
			}
		} else if (error == B_OK) {
			if (_info)
				*_info = info;
			if (_data)
				*_data = NULL;
			*removed = false;
		}
	} else {
		*removed = true;
		RemoveAttribute(name);
		error = B_OK;
	}

	// clean up on error
	if (error != B_OK) {
		ClearAttrDir();
		if (fStatus == ATTRIBUTE_DIRECTORY_VALID)
			fStatus = ATTRIBUTE_DIRECTORY_NOT_LOADED;
	}

	return error;
}

// GetAttribute
Attribute*
AttributeDirectory::GetAttribute(const char* name) const
{
	if (!name)
		return NULL;

	for (SLList<Attribute>::ConstIterator it = fAttributes.GetIterator();
		 it.HasNext();) {
		Attribute* attribute = it.Next();
		if (strcmp(attribute->GetName(), name) == 0)
			return attribute;
	}

	return NULL;
}

// GetFirstAttribute
Attribute*
AttributeDirectory::GetFirstAttribute() const
{
	return fAttributes.GetFirst();
}

// GetNextAttribute
Attribute*
AttributeDirectory::GetNextAttribute(Attribute* attribute) const
{
	return (attribute ? fAttributes.GetNext(attribute) : NULL);
}

// _LoadAttribute
status_t
AttributeDirectory::_LoadAttribute(BNode& node, const char* name,
	attr_info& info, void* data, bool& dataLoaded)
{
	// stat the attribute
	status_t error = node.GetAttrInfo(name, &info);
	if (error != B_OK)
		return error;

	// if the data are small enough, read them
	if (data && info.size <= kSmallAttributeSize) {
		ssize_t bytesRead = node.ReadAttr(name, info.type, 0, data,
			info.size);
		dataLoaded = (bytesRead == info.size);
	}

	return B_OK;
}