1/*
2 * Copyright 2010-2011, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2010, François Revol, <revol@free.fr>.
4 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
5 * This file may be used under the terms of the MIT License.
6 */
7
8//!	connection between pure inode and kernel_interface attributes
9
10
11#include "Attribute.h"
12
13#include <new>
14#include <stdio.h>
15#include <stdlib.h>
16
17#include "BPlusTree.h"
18#include "CRCTable.h"
19#include "Utility.h"
20
21
22//#define TRACE_BTRFS
23#ifdef TRACE_BTRFS
24#	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
25#else
26#	define TRACE(x...) ;
27#endif
28
29
30Attribute::Attribute(Inode* inode)
31	:
32	fVolume(inode->GetVolume()),
33	fInode(inode),
34	fName(NULL)
35{
36}
37
38
39Attribute::Attribute(Inode* inode, attr_cookie* cookie)
40	:
41	fVolume(inode->GetVolume()),
42	fInode(inode),
43	fName(cookie->name)
44{
45}
46
47
48Attribute::~Attribute()
49{
50}
51
52
53status_t
54Attribute::CheckAccess(const char* name, int openMode)
55{
56	return fInode->CheckPermissions(open_mode_to_access(openMode)
57		| (openMode & O_TRUNC ? W_OK : 0));
58}
59
60
61status_t
62Attribute::Open(const char* name, int openMode, attr_cookie** _cookie)
63{
64	TRACE("Open\n");
65	status_t status = CheckAccess(name, openMode);
66	if (status < B_OK)
67		return status;
68
69	status = _Lookup(name, strlen(name));
70	if (status < B_OK)
71		return status;
72
73	attr_cookie* cookie = new(std::nothrow) attr_cookie;
74	if (cookie == NULL)
75		return B_NO_MEMORY;
76
77	fName = name;
78
79	// initialize the cookie
80	strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
81	cookie->open_mode = openMode;
82	cookie->create = false;
83
84	*_cookie = cookie;
85	return B_OK;
86}
87
88
89status_t
90Attribute::Stat(struct stat& stat)
91{
92	TRACE("Stat\n");
93
94	size_t nameLength = strlen(fName);
95	btrfs_dir_entry *entries;
96	size_t length;
97	status_t status = _Lookup(fName, nameLength, &entries, &length);
98	if (status < B_OK)
99		return status;
100
101	btrfs_dir_entry *entry;
102	status = _FindEntry(entries, length, fName, nameLength, &entry);
103	if (status != B_OK) {
104		free(entries);
105		return status;
106	}
107
108	// found an entry to stat
109	stat.st_type = B_XATTR_TYPE;
110	stat.st_size = entry->DataLength();
111	free(entries);
112	return B_OK;
113}
114
115
116status_t
117Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length)
118{
119	if (pos < 0LL)
120		return ERANGE;
121
122	size_t nameLength = strlen(fName);
123	btrfs_dir_entry *entries;
124	size_t length;
125	status_t status = _Lookup(fName, nameLength, &entries, &length);
126	if (status < B_OK)
127		return status;
128
129	btrfs_dir_entry *entry;
130	status = _FindEntry(entries, length, fName, nameLength, &entry);
131	if (status != B_OK) {
132		free(entries);
133		return status;
134	}
135
136	// found an entry to read
137	if (pos + *_length > entry->DataLength())
138		length = entry->DataLength() - pos;
139	else
140		length = *_length - pos;
141	memcpy(buffer, (uint8*)entry + entry->NameLength()
142		+ sizeof(btrfs_dir_entry) + (uint32)pos, length);
143	*_length = length;
144
145	free(entries);
146	return B_OK;
147}
148
149
150status_t
151Attribute::_Lookup(const char* name, size_t nameLength,
152	btrfs_dir_entry **_entries, size_t *_length)
153{
154	uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength);
155	struct btrfs_key key;
156	key.SetType(BTRFS_KEY_TYPE_XATTR_ITEM);
157	key.SetObjectID(fInode->ID());
158	key.SetOffset(hash);
159
160	btrfs_dir_entry *entries;
161	size_t length;
162	status_t status = fInode->GetVolume()->FSTree()->FindExact(key,
163		(void**)&entries, &length);
164	if (status != B_OK) {
165		TRACE("AttributeIterator::Lookup(): Couldn't find entry with hash %lu "
166			"\"%s\"\n", hash, name);
167		return status;
168	}
169
170	if (_entries == NULL)
171		free(entries);
172	else
173		*_entries = entries;
174
175	if (_length != NULL)
176		*_length = length;
177
178	return B_OK;
179}
180
181
182status_t
183Attribute::_FindEntry(btrfs_dir_entry *entries, size_t length,
184	const char* name, size_t nameLength, btrfs_dir_entry **_entry)
185{
186	btrfs_dir_entry *entry = entries;
187	uint16 current = 0;
188	while (current < length) {
189		current += entry->Length();
190		break;
191		// TODO there could be several entries with the same name hash
192		entry = (btrfs_dir_entry *)((uint8*)entry + entry->Length());
193	}
194
195	*_entry = entry;
196	return B_OK;
197}
198
199