xref: /haiku/src/add-ons/kernel/file_systems/bfs/Attribute.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2004-2017, 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 
18 extern void fill_stat_buffer(Inode* inode, struct stat& stat);
19 
20 
21 Attribute::Attribute(Inode* inode)
22 	:
23 	fNodeGetter(inode->GetVolume()),
24 	fInode(inode),
25 	fSmall(NULL),
26 	fAttribute(NULL),
27 	fName(NULL)
28 {
29 }
30 
31 
32 Attribute::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 
44 Attribute::~Attribute()
45 {
46 	Put();
47 }
48 
49 
50 status_t
51 Attribute::InitCheck()
52 {
53 	return (fSmall != NULL || fAttribute != NULL) ? B_OK : B_NO_INIT;
54 }
55 
56 
57 status_t
58 Attribute::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 
77 status_t
78 Attribute::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 		fNodeGetter.SetToNode(fInode);
87 		if (fNodeGetter.Node() == NULL)
88 			return B_IO_ERROR;
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 
103 void
104 Attribute::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 
119 status_t
120 Attribute::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 
151 status_t
152 Attribute::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 
180 status_t
181 Attribute::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 
203 status_t
204 Attribute::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 
214 status_t
215 Attribute::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 
226 status_t
227 Attribute::_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