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