* 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.
*/
#include "Attribute.h"
#include "Utility.h"
#include <stdio.h>
#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;
strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
cookie->type = type;
cookie->open_mode = openMode;
cookie->create = true;
if (Find(name) == B_OK) {
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;
strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
cookie->open_mode = openMode;
cookie->create = false;
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;
return B_ERROR;
}
status_t
Attribute::_Truncate()
{
return B_ERROR;
}
status_t
Attribute::_Find(const char* name, int32 index)
{
Put();
fName = name;
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();
}
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;
}