1/*
2 * Copyright 2010, 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#include "Utility.h"
13
14#include <stdio.h>
15
16
17//#define TRACE_EXT2
18#ifdef TRACE_EXT2
19#	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
20#else
21#	define TRACE(x...) ;
22#endif
23
24
25Attribute::Attribute(Inode* inode)
26	:
27	fVolume(inode->GetVolume()),
28	fBlock(fVolume),
29	fInode(inode),
30	fBodyEntry(NULL),
31	fBlockEntry(NULL),
32	fName(NULL)
33{
34
35}
36
37
38Attribute::Attribute(Inode* inode, attr_cookie* cookie)
39	:
40	fVolume(inode->GetVolume()),
41	fBlock(fVolume),
42	fInode(inode),
43	fBodyEntry(NULL),
44	fBlockEntry(NULL),
45	fName(NULL)
46{
47	Find(cookie->name);
48}
49
50
51Attribute::~Attribute()
52{
53	Put();
54}
55
56
57status_t
58Attribute::InitCheck()
59{
60	return (fBodyEntry != NULL || fBlockEntry != NULL) ? B_OK : B_NO_INIT;
61}
62
63
64status_t
65Attribute::CheckAccess(const char* name, int openMode)
66{
67	return fInode->CheckPermissions(open_mode_to_access(openMode)
68		| (openMode & O_TRUNC ? W_OK : 0));
69}
70
71
72status_t
73Attribute::Find(const char* name)
74{
75	return _Find(name, -1);
76}
77
78
79status_t
80Attribute::Find(int32 index)
81{
82	return _Find(NULL, index);
83}
84
85
86status_t
87Attribute::GetName(char* name, size_t* _nameLength)
88{
89	if (fBodyEntry == NULL && fBlockEntry == NULL)
90		return B_NO_INIT;
91	if (fBodyEntry != NULL)
92		return _PrefixedName(fBodyEntry, name, _nameLength);
93	else
94		return _PrefixedName(fBlockEntry, name, _nameLength);
95}
96
97
98void
99Attribute::Put()
100{
101	if (fBodyEntry != NULL) {
102		recursive_lock_unlock(&fInode->SmallDataLock());
103		fBlock.Unset();
104		fBodyEntry = NULL;
105	}
106
107	if (fBlockEntry != NULL) {
108		fBlock.Unset();
109		fBlockEntry = NULL;
110	}
111}
112
113
114status_t
115Attribute::Create(const char* name, type_code type, int openMode,
116	attr_cookie** _cookie)
117{
118	status_t status = CheckAccess(name, openMode);
119	if (status < B_OK)
120		return status;
121
122	attr_cookie* cookie = new(std::nothrow) attr_cookie;
123	if (cookie == NULL)
124		return B_NO_MEMORY;
125
126	fName = name;
127
128	// initialize the cookie
129	strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
130	cookie->type = type;
131	cookie->open_mode = openMode;
132	cookie->create = true;
133
134	if (Find(name) == B_OK) {
135		// attribute already exists
136		if ((openMode & O_TRUNC) != 0)
137			_Truncate();
138	}
139	*_cookie = cookie;
140	return B_OK;
141}
142
143
144status_t
145Attribute::Open(const char* name, int openMode, attr_cookie** _cookie)
146{
147	TRACE("Open\n");
148	status_t status = CheckAccess(name, openMode);
149	if (status < B_OK)
150		return status;
151
152	status = Find(name);
153	if (status < B_OK)
154		return status;
155
156	attr_cookie* cookie = new(std::nothrow) attr_cookie;
157	if (cookie == NULL)
158		return B_NO_MEMORY;
159
160	// initialize the cookie
161	strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
162	cookie->open_mode = openMode;
163	cookie->create = false;
164
165	// Should we truncate the attribute?
166	if ((openMode & O_TRUNC) != 0)
167		_Truncate();
168
169	*_cookie = cookie;
170	return B_OK;
171}
172
173
174status_t
175Attribute::Stat(struct stat& stat)
176{
177	TRACE("Stat\n");
178	if (fBodyEntry == NULL && fBlockEntry == NULL)
179		return B_NO_INIT;
180
181	stat.st_type = B_XATTR_TYPE;
182
183	if (fBodyEntry != NULL)
184		stat.st_size = fBodyEntry->ValueSize();
185	else if (fBlockEntry != NULL)
186		stat.st_size = fBlockEntry->ValueSize();
187
188	return B_OK;
189}
190
191
192status_t
193Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length)
194{
195	if (fBodyEntry == NULL && fBlockEntry == NULL)
196		return B_NO_INIT;
197
198	if (pos < 0LL)
199		return ERANGE;
200
201	size_t length = *_length;
202	const uint8* start = (uint8 *)fBlock.Block();
203	if (fBlockEntry != NULL) {
204		pos += fBlockEntry->ValueOffset();
205		if (((uint32)pos + length) > fVolume->BlockSize()
206			|| length > fBlockEntry->ValueSize())
207			return ERANGE;
208	} else {
209		start += fVolume->InodeBlockIndex(fInode->ID()) * fVolume->InodeSize();
210		const uint8* end = start + fVolume->InodeSize();
211		start += EXT2_INODE_NORMAL_SIZE + fInode->Node().ExtraInodeSize()
212			+ sizeof(uint32);
213		pos += fBodyEntry->ValueOffset();
214		if ((off_t)(pos + length) > (end - start) || length > fBodyEntry->ValueSize())
215			return ERANGE;
216	}
217	memcpy(buffer, start + (uint32)pos, length);
218
219	*_length = length;
220	return B_OK;
221}
222
223
224status_t
225Attribute::Write(Transaction& transaction, attr_cookie* cookie, off_t pos,
226	const uint8* buffer, size_t* _length, bool* _created)
227{
228	if (!cookie->create && fBodyEntry == NULL && fBlockEntry == NULL)
229		return B_NO_INIT;
230
231	// TODO: Implement
232	return B_ERROR;
233}
234
235
236status_t
237Attribute::_Truncate()
238{
239	// TODO: Implement
240	return B_ERROR;
241}
242
243
244status_t
245Attribute::_Find(const char* name, int32 index)
246{
247	Put();
248
249	fName = name;
250
251	// try to find it in the small data region
252	if (fInode->HasExtraAttributes()
253		&& recursive_lock_lock(&fInode->SmallDataLock()) == B_OK) {
254		off_t blockNum;
255		fVolume->GetInodeBlock(fInode->ID(), blockNum);
256
257		if (blockNum != 0) {
258			fBlock.SetTo(blockNum);
259			const uint8* start = fBlock.Block()
260				+ fVolume->InodeBlockIndex(fInode->ID()) * fVolume->InodeSize();
261			const uint8* end = start + fVolume->InodeSize();
262			int32 count = 0;
263			if (_FindAttributeBody(start + EXT2_INODE_NORMAL_SIZE
264				+ fInode->Node().ExtraInodeSize(), end, name, index, &count,
265				&fBodyEntry) == B_OK)
266				return B_OK;
267			index -= count;
268		}
269
270		recursive_lock_unlock(&fInode->SmallDataLock());
271		fBlock.Unset();
272	}
273
274	// then, search in the attribute directory
275	if (fInode->Node().ExtendedAttributesBlock() != 0) {
276		fBlock.SetTo(fInode->Node().ExtendedAttributesBlock());
277		if (_FindAttributeBlock(fBlock.Block(),
278			fBlock.Block() + fVolume->BlockSize(), name, index, NULL,
279			&fBlockEntry) == B_OK)
280			return B_OK;
281		fBlock.Unset();
282	}
283
284	return B_ENTRY_NOT_FOUND;
285}
286
287
288status_t
289Attribute::_FindAttributeBody(const uint8* start, const uint8* end,
290	const char* name, int32 index, int32 *count, ext2_xattr_entry** _entry)
291{
292	TRACE("_FindAttributeBody %p %p %s\n", start, end, name);
293	if (*((uint32*)start) != EXT2_XATTR_MAGIC)
294		return B_BAD_DATA;
295	return _FindAttribute(start + sizeof(uint32), end, name, index, count,
296		_entry);
297}
298
299
300status_t
301Attribute::_FindAttributeBlock(const uint8* start, const uint8* end, const char* name,
302	int32 index, int32 *count, ext2_xattr_entry** _entry)
303{
304	TRACE("_FindAttributeBlock %p %p %s\n", start, end, name);
305	ext2_xattr_header *header = (ext2_xattr_header*)start;
306	if (!header->IsValid())
307		return B_BAD_DATA;
308
309	return _FindAttribute(start + sizeof(ext2_xattr_header), end, name, index,
310		count, _entry);
311}
312
313
314status_t
315Attribute::_FindAttribute(const uint8* start, const uint8* end, const char* name,
316	int32 index, int32 *count, ext2_xattr_entry** _entry)
317{
318	TRACE("_FindAttribute %p %p %s\n", start, end, name);
319	char buffer[EXT2_XATTR_NAME_LENGTH];
320
321	int32 i = 0;
322	while (start < end) {
323		ext2_xattr_entry* entry = (ext2_xattr_entry*)start;
324		if (!entry->IsValid())
325			break;
326
327		size_t length = EXT2_XATTR_NAME_LENGTH;
328		if ((name != NULL && _PrefixedName(entry, buffer, &length) == B_OK
329				&& strncmp(name, buffer, length) == 0) || index == i) {
330			*_entry = entry;
331			return B_OK;
332		}
333		start += entry->Length();
334		i++;
335	}
336
337	if (count != NULL)
338		*count = i;
339	return B_ENTRY_NOT_FOUND;
340}
341
342
343status_t
344Attribute::_PrefixedName(ext2_xattr_entry* entry, char* _name, size_t* _nameLength)
345{
346	const char *indexNames[] = { "0", "user" };
347	size_t l = 0;
348
349	if (entry->NameIndex() < ((sizeof(indexNames) / sizeof(indexNames[0]))))
350		l = snprintf(_name, *_nameLength, "%s.%s.%.*s",
351			"linux", indexNames[entry->NameIndex()], entry->NameLength(),
352			entry->name);
353	else
354		l = snprintf(_name, *_nameLength, "%s.%d.%.*s",
355			"linux", entry->NameIndex(), entry->NameLength(), entry->name);
356	if (l < 1 || l > *_nameLength - 1)
357		return ENOBUFS;
358
359	*_nameLength = l + 1;
360	_name[l] = '\0';
361
362	return B_OK;
363}
364
365