xref: /haiku/src/add-ons/kernel/file_systems/bfs/Attribute.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
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 
17 extern void fill_stat_buffer(Inode* inode, struct stat& stat);
18 
19 
20 Attribute::Attribute(Inode* inode)
21 	:
22 	fNodeGetter(inode->GetVolume()),
23 	fInode(inode),
24 	fSmall(NULL),
25 	fAttribute(NULL),
26 	fName(NULL)
27 {
28 }
29 
30 
31 Attribute::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 
43 Attribute::~Attribute()
44 {
45 	Put();
46 }
47 
48 
49 status_t
50 Attribute::InitCheck()
51 {
52 	return (fSmall != NULL || fAttribute != NULL) ? B_OK : B_NO_INIT;
53 }
54 
55 
56 status_t
57 Attribute::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 
76 status_t
77 Attribute::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 
99 void
100 Attribute::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 
115 status_t
116 Attribute::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 
145 status_t
146 Attribute::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 
174 status_t
175 Attribute::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 
197 status_t
198 Attribute::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 
208 status_t
209 Attribute::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 
220 status_t
221 Attribute::_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