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
Attribute(Inode * inode)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
Attribute(Inode * inode,attr_cookie * cookie)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
~Attribute()51 Attribute::~Attribute()
52 {
53 Put();
54 }
55
56
57 status_t
InitCheck()58 Attribute::InitCheck()
59 {
60 return (fBodyEntry != NULL || fBlockEntry != NULL) ? B_OK : B_NO_INIT;
61 }
62
63
64 status_t
CheckAccess(const char * name,int openMode)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
Find(const char * name)73 Attribute::Find(const char* name)
74 {
75 return _Find(name, -1);
76 }
77
78
79 status_t
Find(int32 index)80 Attribute::Find(int32 index)
81 {
82 return _Find(NULL, index);
83 }
84
85
86 status_t
GetName(char * name,size_t * _nameLength)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
Put()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
Create(const char * name,type_code type,int openMode,attr_cookie ** _cookie)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
Open(const char * name,int openMode,attr_cookie ** _cookie)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
Stat(struct stat & stat)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
Read(attr_cookie * cookie,off_t pos,uint8 * buffer,size_t * _length)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
Write(Transaction & transaction,attr_cookie * cookie,off_t pos,const uint8 * buffer,size_t * _length,bool * _created)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
_Truncate()237 Attribute::_Truncate()
238 {
239 // TODO: Implement
240 return B_ERROR;
241 }
242
243
244 status_t
_Find(const char * name,int32 index)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
_FindAttributeBody(const uint8 * start,const uint8 * end,const char * name,int32 index,int32 * count,ext2_xattr_entry ** _entry)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
_FindAttributeBlock(const uint8 * start,const uint8 * end,const char * name,int32 index,int32 * count,ext2_xattr_entry ** _entry)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
_FindAttribute(const uint8 * start,const uint8 * end,const char * name,int32 index,int32 * count,ext2_xattr_entry ** _entry)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
_PrefixedName(ext2_xattr_entry * entry,char * _name,size_t * _nameLength)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