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