xref: /haiku/src/add-ons/kernel/file_systems/bfs/Attribute.cpp (revision b30304acc8c37e678a1bf66976d15bdab103f931)
1 /*
2  * Copyright 2004-2007, 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 first write
14 
15 
16 extern void fill_stat_buffer(Inode *inode, struct stat &stat);
17 
18 
19 Attribute::Attribute(Inode *inode)
20 	:
21 	fNodeGetter(inode->GetVolume()),
22 	fInode(inode),
23 	fSmall(NULL),
24 	fAttribute(NULL),
25 	fName(NULL)
26 {
27 }
28 
29 
30 Attribute::Attribute(Inode *inode, attr_cookie *cookie)
31 	:
32 	fNodeGetter(inode->GetVolume()),
33 	fInode(inode),
34 	fSmall(NULL),
35 	fAttribute(NULL),
36 	fName(NULL)
37 {
38 	Get(cookie->name);
39 }
40 
41 
42 Attribute::~Attribute()
43 {
44 	Put();
45 }
46 
47 
48 status_t
49 Attribute::InitCheck()
50 {
51 	return (fSmall != NULL || fAttribute != NULL) ? B_OK : B_NO_INIT;
52 }
53 
54 
55 status_t
56 Attribute::CheckAccess(const char *name, int openMode)
57 {
58 	// Opening the name attribute using this function is not allowed,
59 	// also using the reserved indices name, last_modified, and size
60 	// shouldn't be allowed.
61 	// ToDo: we might think about allowing to update those values, but
62 	//	really change their corresponding values in the bfs_inode structure
63 	if (name[0] == FILE_NAME_NAME && name[1] == '\0'
64 /*		|| !strcmp(name, "name")
65 		|| !strcmp(name, "last_modified")
66 		|| !strcmp(name, "size")*/)
67 		RETURN_ERROR(B_NOT_ALLOWED);
68 
69 	return fInode->CheckPermissions(openModeToAccess(openMode)
70 		| (openMode & O_TRUNC ? W_OK : 0));
71 }
72 
73 
74 status_t
75 Attribute::Get(const char *name)
76 {
77 	Put();
78 
79 	fName = name;
80 
81 	// try to find it in the small data region
82 	if (fInode->SmallDataLock().Lock() == B_OK) {
83 		fNodeGetter.SetToNode(fInode);
84 		fSmall = fInode->FindSmallData(fNodeGetter.Node(), (const char *)name);
85 		if (fSmall != NULL)
86 			return B_OK;
87 
88 		fInode->SmallDataLock().Unlock();
89 		fNodeGetter.Unset();
90 	}
91 
92 	// then, search in the attribute directory
93 	return fInode->GetAttribute(name, &fAttribute);
94 }
95 
96 
97 void
98 Attribute::Put()
99 {
100 	if (fSmall != NULL) {
101 		fInode->SmallDataLock().Unlock();
102 		fNodeGetter.Unset();
103 		fSmall = NULL;
104 	}
105 
106 	if (fAttribute != NULL) {
107 		fInode->ReleaseAttribute(fAttribute);
108 		fAttribute = NULL;
109 	}
110 }
111 
112 
113 status_t
114 Attribute::Create(const char *name, type_code type, int openMode, attr_cookie **_cookie)
115 {
116 	status_t status = CheckAccess(name, openMode);
117 	if (status < B_OK)
118 		return status;
119 
120 	attr_cookie *cookie = (attr_cookie *)malloc(sizeof(attr_cookie));
121 	if (cookie == NULL)
122 		RETURN_ERROR(B_NO_MEMORY);
123 
124 	fName = name;
125 
126 	// initialize the cookie
127 	strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
128 	cookie->type = type;
129 	cookie->open_mode = openMode;
130 	cookie->create = true;
131 
132 	if (Get(name) == B_OK) {
133 		// attribute already exists
134 		if ((openMode & O_TRUNC) != 0)
135 			_Truncate();
136 	}
137 	*_cookie = cookie;
138 	return B_OK;
139 }
140 
141 
142 status_t
143 Attribute::Open(const char *name, int openMode, attr_cookie **_cookie)
144 {
145 	status_t status = CheckAccess(name, openMode);
146 	if (status < B_OK)
147 		return status;
148 
149 	status = Get(name);
150 	if (status < B_OK)
151 		return status;
152 
153 	attr_cookie *cookie = (attr_cookie *)malloc(sizeof(attr_cookie));
154 	if (cookie == NULL)
155 		RETURN_ERROR(B_NO_MEMORY);
156 
157 	// initialize the cookie
158 	strlcpy(cookie->name, fName, B_ATTR_NAME_LENGTH);
159 	cookie->open_mode = openMode;
160 	cookie->create = false;
161 
162 	// Should we truncate the attribute?
163 	if ((openMode & O_TRUNC) != 0)
164 		_Truncate();
165 
166 	*_cookie = cookie;
167 	return B_OK;
168 }
169 
170 
171 status_t
172 Attribute::Stat(struct stat &stat)
173 {
174 	if (fSmall == NULL && fAttribute == NULL)
175 		return B_NO_INIT;
176 
177 	if (fSmall != NULL) {
178 		fill_stat_buffer(fInode, stat);
179 
180 		// overwrite some data to suit our needs
181 		stat.st_type = fSmall->Type();
182 		stat.st_size = fSmall->DataSize();
183 	}
184 
185 	if (fAttribute != NULL)
186 		fill_stat_buffer(fAttribute, stat);
187 
188 	return B_OK;
189 }
190 
191 
192 status_t
193 Attribute::Read(attr_cookie *cookie, off_t pos, uint8 *buffer, size_t *_length)
194 {
195 	if (fSmall == NULL && fAttribute == NULL)
196 		return B_NO_INIT;
197 
198 	// ToDo: move small_data logic from Inode::ReadAttribute() over to here!
199 	return fInode->ReadAttribute(cookie->name, 0, pos, buffer, _length);
200 }
201 
202 
203 status_t
204 Attribute::Write(Transaction &transaction, attr_cookie *cookie,
205 	off_t pos, const uint8 *buffer, size_t *_length)
206 {
207 	if (!cookie->create && fSmall == NULL && fAttribute == NULL)
208 		return B_NO_INIT;
209 
210 	return fInode->WriteAttribute(transaction, cookie->name, cookie->type,
211 		pos, buffer, _length);
212 }
213 
214 
215 status_t
216 Attribute::_Truncate()
217 {
218 	if (fSmall != NULL) {
219 		// TODO: as long as Inode::_AddSmallData() works like it does,
220 		// we've got nothing to do here
221 		return B_OK;
222 	}
223 
224 	if (fAttribute != NULL) {
225 		WriteLocked locked(fAttribute->Lock());
226 		Transaction transaction(fAttribute->GetVolume(), fAttribute->BlockNumber());
227 
228 		status_t status = fAttribute->SetFileSize(transaction, 0);
229 		if (status >= B_OK)
230 			status = fAttribute->WriteBack(transaction);
231 
232 		if (status < B_OK)
233 			return status;
234 
235 		transaction.Done();
236 	}
237 
238 	return B_OK;
239 }
240 
241