xref: /haiku/src/add-ons/kernel/file_systems/ext2/Attribute.cpp (revision cbe0a0c436162d78cc3f92a305b64918c839d079)
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 
25 Attribute::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 
38 Attribute::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 
51 Attribute::~Attribute()
52 {
53 	Put();
54 }
55 
56 
57 status_t
58 Attribute::InitCheck()
59 {
60 	return (fBodyEntry != NULL || fBlockEntry != NULL) ? B_OK : B_NO_INIT;
61 }
62 
63 
64 status_t
65 Attribute::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 
72 status_t
73 Attribute::Find(const char* name)
74 {
75 	return _Find(name, -1);
76 }
77 
78 
79 status_t
80 Attribute::Find(int32 index)
81 {
82 	return _Find(NULL, index);
83 }
84 
85 
86 status_t
87 Attribute::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 
98 void
99 Attribute::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 
114 status_t
115 Attribute::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 
144 status_t
145 Attribute::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 
174 status_t
175 Attribute::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 
192 status_t
193 Attribute::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 
224 status_t
225 Attribute::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 
236 status_t
237 Attribute::_Truncate()
238 {
239 	// TODO: Implement
240 	return B_ERROR;
241 }
242 
243 
244 status_t
245 Attribute::_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 
288 status_t
289 Attribute::_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 
300 status_t
301 Attribute::_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 
314 status_t
315 Attribute::_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 
343 status_t
344 Attribute::_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