1/*
2 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
4 */
5
6//!	connection between pure inode and kernel_interface attributes
7
8
9#include "Attribute.h"
10
11
12// TODO: clean this up, find a better separation between Inode and this class
13// TODO: even after Create(), the attribute cannot be stat() for until the
14// first write
15
16
17extern void fill_stat_buffer(Inode* inode, struct stat& stat);
18
19
20Attribute::Attribute(Inode* inode)
21	:
22	fNodeGetter(inode->GetVolume()),
23	fInode(inode),
24	fSmall(NULL),
25	fAttribute(NULL),
26	fName(NULL)
27{
28}
29
30
31Attribute::Attribute(Inode* inode, attr_cookie* cookie)
32	:
33	fNodeGetter(inode->GetVolume()),
34	fInode(inode),
35	fSmall(NULL),
36	fAttribute(NULL),
37	fName(NULL)
38{
39	Get(cookie->name);
40}
41
42
43Attribute::~Attribute()
44{
45	Put();
46}
47
48
49status_t
50Attribute::InitCheck()
51{
52	return (fSmall != NULL || fAttribute != NULL) ? B_OK : B_NO_INIT;
53}
54
55
56status_t
57Attribute::CheckAccess(const char* name, int openMode)
58{
59	// Opening the name attribute using this function is not allowed,
60	// also using the reserved indices name, last_modified, and size
61	// shouldn't be allowed.
62	// TODO: we might think about allowing to update those values, but
63	//	really change their corresponding values in the bfs_inode structure
64	if (name[0] == FILE_NAME_NAME && name[1] == '\0'
65// TODO: reenable this check -- some WonderBrush locale files used them
66/*		|| !strcmp(name, "name")
67		|| !strcmp(name, "last_modified")
68		|| !strcmp(name, "size")*/)
69		RETURN_ERROR(B_NOT_ALLOWED);
70
71	return fInode->CheckPermissions(open_mode_to_access(openMode)
72		| (openMode & O_TRUNC ? W_OK : 0));
73}
74
75
76status_t
77Attribute::Get(const char* name)
78{
79	Put();
80
81	fName = name;
82
83	// try to find it in the small data region
84	if (recursive_lock_lock(&fInode->SmallDataLock()) == B_OK) {
85		fNodeGetter.SetToNode(fInode);
86		fSmall = fInode->FindSmallData(fNodeGetter.Node(), (const char*)name);
87		if (fSmall != NULL)
88			return B_OK;
89
90		recursive_lock_unlock(&fInode->SmallDataLock());
91		fNodeGetter.Unset();
92	}
93
94	// then, search in the attribute directory
95	return fInode->GetAttribute(name, &fAttribute);
96}
97
98
99void
100Attribute::Put()
101{
102	if (fSmall != NULL) {
103		recursive_lock_unlock(&fInode->SmallDataLock());
104		fNodeGetter.Unset();
105		fSmall = NULL;
106	}
107
108	if (fAttribute != NULL) {
109		fInode->ReleaseAttribute(fAttribute);
110		fAttribute = NULL;
111	}
112}
113
114
115status_t
116Attribute::Create(const char* name, type_code type, int openMode,
117	attr_cookie** _cookie)
118{
119	status_t status = CheckAccess(name, openMode);
120	if (status < B_OK)
121		return status;
122
123	attr_cookie* cookie = new(std::nothrow) attr_cookie;
124	if (cookie == NULL)
125		RETURN_ERROR(B_NO_MEMORY);
126
127	fName = name;
128
129	// initialize the cookie
130	strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
131	cookie->type = type;
132	cookie->open_mode = openMode;
133	cookie->create = true;
134
135	if (Get(name) == B_OK) {
136		// attribute already exists
137		if ((openMode & O_TRUNC) != 0)
138			_Truncate();
139	}
140	*_cookie = cookie;
141	return B_OK;
142}
143
144
145status_t
146Attribute::Open(const char* name, int openMode, attr_cookie** _cookie)
147{
148	status_t status = CheckAccess(name, openMode);
149	if (status < B_OK)
150		return status;
151
152	status = Get(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_ERROR(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	if (fSmall == NULL && fAttribute == NULL)
178		return B_NO_INIT;
179
180	if (fSmall != NULL) {
181		fill_stat_buffer(fInode, stat);
182
183		// overwrite some data to suit our needs
184		stat.st_type = fSmall->Type();
185		stat.st_size = fSmall->DataSize();
186		stat.st_mtim = stat.st_ctim;
187			// attribute changes cause status_change updates
188	}
189
190	if (fAttribute != NULL)
191		fill_stat_buffer(fAttribute, stat);
192
193	return B_OK;
194}
195
196
197status_t
198Attribute::Read(attr_cookie* cookie, off_t pos, uint8* buffer, size_t* _length)
199{
200	if (fSmall == NULL && fAttribute == NULL)
201		return B_NO_INIT;
202
203	// TODO: move small_data logic from Inode::ReadAttribute() over to here!
204	return fInode->ReadAttribute(cookie->name, 0, pos, buffer, _length);
205}
206
207
208status_t
209Attribute::Write(Transaction& transaction, attr_cookie* cookie, off_t pos,
210	const uint8* buffer, size_t* _length, bool* _created)
211{
212	if (!cookie->create && fSmall == NULL && fAttribute == NULL)
213		return B_NO_INIT;
214
215	return fInode->WriteAttribute(transaction, cookie->name, cookie->type,
216		pos, buffer, _length, _created);
217}
218
219
220status_t
221Attribute::_Truncate()
222{
223	if (fSmall != NULL) {
224		// TODO: as long as Inode::_AddSmallData() works like it does,
225		// we've got nothing to do here
226		return B_OK;
227	}
228
229	if (fAttribute != NULL) {
230		Transaction transaction(fAttribute->GetVolume(),
231			fAttribute->BlockNumber());
232		fAttribute->WriteLockInTransaction(transaction);
233
234		status_t status = fAttribute->SetFileSize(transaction, 0);
235		if (status >= B_OK)
236			status = fAttribute->WriteBack(transaction);
237
238		if (status < B_OK)
239			return status;
240
241		transaction.Done();
242	}
243
244	return B_OK;
245}
246
247