xref: /haiku/src/add-ons/kernel/file_systems/bfs/Index.cpp (revision 81f5654c124bf46fba0fd251f208e2d88d81e1ce)
1 /* Index - index access functions
2 **
3 ** Initial version by Axel Dörfler, axeld@pinc-software.de
4 ** This file may be used under the terms of the OpenBeOS License.
5 */
6 
7 
8 #include "Debug.h"
9 #include "Index.h"
10 #include "Volume.h"
11 #include "Inode.h"
12 #include "BPlusTree.h"
13 
14 #include <util/kernel_cpp.h>
15 #include <TypeConstants.h>
16 
17 // B_MIME_STRING_TYPE is defined in storage/Mime.h, but we
18 // don't need the whole file here; the type can't change anyway
19 #ifndef _MIME_H
20 #	define B_MIME_STRING_TYPE 'MIMS'
21 #endif
22 
23 
24 Index::Index(Volume *volume)
25 	:
26 	fVolume(volume),
27 	fNode(NULL)
28 {
29 }
30 
31 
32 Index::~Index()
33 {
34 	if (fNode == NULL)
35 		return;
36 
37 	put_vnode(fVolume->ID(), fNode->ID());
38 }
39 
40 
41 void
42 Index::Unset()
43 {
44 	if (fNode == NULL)
45 		return;
46 
47 	put_vnode(fVolume->ID(), fNode->ID());
48 	fNode = NULL;
49 	fName = NULL;
50 }
51 
52 
53 /** Sets the index to specified one. Returns an error if the index could
54  *	not be found or initialized.
55  *	Note, Index::Update() may be called on the object even if this method
56  *	failed previously. In this case, it will only update live queries for
57  *	the updated attribute.
58  */
59 
60 status_t
61 Index::SetTo(const char *name)
62 {
63 	// remove the old node, if the index is set for the second time
64 	Unset();
65 
66 	fName = name;
67 		// only stores the pointer, so it assumes that it will stay constant
68 		// in further comparisons (currently only used in Index::Update())
69 
70 	// Note, the name is saved even if the index couldn't be initialized!
71 	// This is used to optimize Index::Update() in case there is no index
72 
73 	Inode *indices = fVolume->IndicesNode();
74 	if (indices == NULL)
75 		return B_ENTRY_NOT_FOUND;
76 
77 	BPlusTree *tree;
78 	if (indices->GetTree(&tree) != B_OK)
79 		return B_BAD_VALUE;
80 
81 	vnode_id id;
82 	status_t status = tree->Find((uint8 *)name, (uint16)strlen(name), &id);
83 	if (status != B_OK)
84 		return status;
85 
86 	Vnode vnode(fVolume, id);
87 	if (vnode.Get(&fNode) != B_OK)
88 		return B_ENTRY_NOT_FOUND;
89 
90 	if (fNode == NULL) {
91 		FATAL(("fatal error at Index::InitCheck(), get_vnode() returned NULL pointer\n"));
92 		return B_ERROR;
93 	}
94 
95 	vnode.Keep();
96 	return B_OK;
97 }
98 
99 
100 /** Returns a standard type code for the stat() index type codes. Returns
101  *	zero if the type is not known (can only happen if the mode field is
102  *	corrupted somehow or not that of an index).
103  */
104 
105 uint32
106 Index::Type()
107 {
108 	if (fNode == NULL)
109 		return 0;
110 
111 	switch (fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX |
112 							 S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX)) {
113 		case S_INT_INDEX:
114 			return B_INT32_TYPE;
115 		case S_UINT_INDEX:
116 			return B_UINT32_TYPE;
117 		case S_LONG_LONG_INDEX:
118 			return B_INT64_TYPE;
119 		case S_ULONG_LONG_INDEX:
120 			return B_UINT64_TYPE;
121 		case S_FLOAT_INDEX:
122 			return B_FLOAT_TYPE;
123 		case S_DOUBLE_INDEX:
124 			return B_DOUBLE_TYPE;
125 		case S_STR_INDEX:
126 			return B_STRING_TYPE;
127 	}
128 	FATAL(("index has unknown type!\n"));
129 	return 0;
130 }
131 
132 
133 size_t
134 Index::KeySize()
135 {
136 	if (fNode == NULL)
137 		return 0;
138 
139 	int32 mode = fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX |
140 								  S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX);
141 
142 	if (mode == S_STR_INDEX)
143 		// string indices don't have a fixed key size
144 		return 0;
145 
146 	switch (mode) {
147 		case S_INT_INDEX:
148 		case S_UINT_INDEX:
149 			return sizeof(int32);
150 		case S_LONG_LONG_INDEX:
151 		case S_ULONG_LONG_INDEX:
152 			return sizeof(int64);
153 		case S_FLOAT_INDEX:
154 			return sizeof(float);
155 		case S_DOUBLE_INDEX:
156 			return sizeof(double);
157 	}
158 	FATAL(("index has unknown type!\n"));
159 	return 0;
160 }
161 
162 
163 status_t
164 Index::Create(Transaction *transaction, const char *name, uint32 type)
165 {
166 	Unset();
167 
168 	int32 mode = 0;
169 	switch (type) {
170 		case B_INT32_TYPE:
171 			mode = S_INT_INDEX;
172 			break;
173 		case B_UINT32_TYPE:
174 			mode = S_UINT_INDEX;
175 			break;
176 		case B_INT64_TYPE:
177 			mode = S_LONG_LONG_INDEX;
178 			break;
179 		case B_UINT64_TYPE:
180 			mode = S_ULONG_LONG_INDEX;
181 			break;
182 		case B_FLOAT_TYPE:
183 			mode = S_FLOAT_INDEX;
184 			break;
185 		case B_DOUBLE_TYPE:
186 			mode = S_DOUBLE_INDEX;
187 			break;
188 		case B_STRING_TYPE:
189 		case B_MIME_STRING_TYPE:
190 			// B_MIME_STRING_TYPE is the only supported non-standard type, but
191 			// will be handled like a B_STRING_TYPE internally
192 			mode = S_STR_INDEX;
193 			break;
194 		default:
195 			return B_BAD_TYPE;
196 	}
197 
198 
199 	// do we need to create the index directory first?
200 	if (fVolume->IndicesNode() == NULL) {
201 		status_t status = fVolume->CreateIndicesRoot(transaction);
202 		if (status < B_OK)
203 			RETURN_ERROR(status);
204 	}
205 
206 	// Inode::Create() will keep the inode locked for us
207 	return Inode::Create(transaction, fVolume->IndicesNode(), name,
208 		S_INDEX_DIR | S_DIRECTORY | mode, 0, type, NULL, &fNode);
209 }
210 
211 
212 /**	Updates the specified index, the oldKey will be removed from, the newKey
213  *	inserted into the tree.
214  *	If the method returns B_BAD_INDEX, it means the index couldn't be found -
215  *	the most common reason will be that the index doesn't exist.
216  *	You may not want to let the whole transaction fail because of that.
217  */
218 
219 status_t
220 Index::Update(Transaction *transaction, const char *name, int32 type, const uint8 *oldKey,
221 	uint16 oldLength, const uint8 *newKey, uint16 newLength, Inode *inode)
222 {
223 	if (name == NULL
224 		|| oldKey == NULL && newKey == NULL
225 		|| oldKey != NULL && oldLength == 0
226 		|| newKey != NULL && newLength == 0)
227 		return B_BAD_VALUE;
228 
229 	// B_MIME_STRING_TYPE is the only supported non-standard type
230 	if (type == B_MIME_STRING_TYPE)
231 		type = B_STRING_TYPE;
232 
233 	// If the two keys are identical, don't do anything - only compare if the
234 	// type has been set, until we have a real type code, we can't do much
235 	// about the comparison here
236 	if (type != 0 && !compareKeys(type, oldKey, oldLength, newKey, newLength))
237 		return B_OK;
238 
239 	// update all live queries about the change, if they have an index or not
240 	if (type != 0)
241 		fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength, newKey, newLength);
242 
243 	status_t status;
244 	if (((name != fName || strcmp(name, fName)) && (status = SetTo(name)) < B_OK)
245 		|| fNode == NULL)
246 		return B_BAD_INDEX;
247 
248 	// now that we have the type, check again for equality
249 	if (type == 0 && !compareKeys(Type(), oldKey, oldLength, newKey, newLength))
250 		return B_OK;
251 
252 	// same for the live query update
253 	if (type == 0)
254 		fVolume->UpdateLiveQueries(inode, name, Type(), oldKey, oldLength, newKey, newLength);
255 
256 	BPlusTree *tree;
257 	if ((status = Node()->GetTree(&tree)) < B_OK)
258 		return status;
259 
260 	// remove the old key from the tree
261 
262 	if (oldKey != NULL) {
263 		status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength, inode->ID());
264 		if (status == B_ENTRY_NOT_FOUND) {
265 			// That's not nice, but should be no reason to let the whole thing fail
266 			FATAL(("Could not find value in index \"%s\"!\n", name));
267 		} else if (status < B_OK)
268 			return status;
269 	}
270 
271 	// add the new key to the tree
272 
273 	if (newKey != NULL)
274 		status = tree->Insert(transaction, (const uint8 *)newKey, newLength, inode->ID());
275 
276 	return status;
277 }
278 
279 
280 status_t
281 Index::InsertName(Transaction *transaction, const char *name, Inode *inode)
282 {
283 	return UpdateName(transaction, NULL, name, inode);
284 }
285 
286 
287 status_t
288 Index::RemoveName(Transaction *transaction, const char *name, Inode *inode)
289 {
290 	return UpdateName(transaction, name, NULL, inode);
291 }
292 
293 
294 status_t
295 Index::UpdateName(Transaction *transaction, const char *oldName, const char *newName, Inode *inode)
296 {
297 	uint16 oldLength = oldName ? strlen(oldName) : 0;
298 	uint16 newLength = newName ? strlen(newName) : 0;
299 	return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName, oldLength,
300 		(uint8 *)newName, newLength, inode);
301 }
302 
303 
304 status_t
305 Index::InsertSize(Transaction *transaction, Inode *inode)
306 {
307 	off_t size = inode->Size();
308 	return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size, sizeof(int64), inode);
309 }
310 
311 
312 status_t
313 Index::RemoveSize(Transaction *transaction, Inode *inode)
314 {
315 	// Inode::OldSize() is the size that's in the index
316 	off_t size = inode->OldSize();
317 	return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size, sizeof(int64), NULL, 0, inode);
318 }
319 
320 
321 status_t
322 Index::UpdateSize(Transaction *transaction, Inode *inode)
323 {
324 	off_t oldSize = inode->OldSize();
325 	off_t newSize = inode->Size();
326 
327 	status_t status = Update(transaction, "size", B_INT64_TYPE, (uint8 *)&oldSize,
328 		sizeof(int64), (uint8 *)&newSize, sizeof(int64), inode);
329 	if (status == B_OK)
330 		inode->UpdateOldSize();
331 
332 	return status;
333 }
334 
335 
336 status_t
337 Index::InsertLastModified(Transaction *transaction, Inode *inode)
338 {
339 	off_t modified = inode->LastModified();
340 	return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0,
341 		(uint8 *)&modified, sizeof(int64), inode);
342 }
343 
344 
345 status_t
346 Index::RemoveLastModified(Transaction *transaction, Inode *inode)
347 {
348 	// Inode::OldLastModified() is the value which is in the index
349 	off_t modified = inode->OldLastModified();
350 	return Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&modified,
351 		sizeof(int64), NULL, 0, inode);
352 }
353 
354 
355 status_t
356 Index::UpdateLastModified(Transaction *transaction, Inode *inode, off_t modified)
357 {
358 	off_t oldModified = inode->OldLastModified();
359 	if (modified == -1)
360 		modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT;
361 	modified |= fVolume->GetUniqueID() & INODE_TIME_MASK;
362 
363 	status_t status = Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&oldModified,
364 		sizeof(int64), (uint8 *)&modified, sizeof(int64), inode);
365 
366 	inode->Node()->last_modified_time = modified;
367 	if (status == B_OK)
368 		inode->UpdateOldLastModified();
369 
370 	return status;
371 }
372 
373