xref: /haiku/src/add-ons/kernel/file_systems/bfs/Index.cpp (revision 3cb015b1ee509d69c643506e8ff573808c86dcfc)
1 /* Index - index access functions
2  *
3  * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de.
4  * This file may be used under the terms of the MIT 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 			INFORM(("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_ERROR(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,
296 	const char *newName, Inode *inode)
297 {
298 	ASSERT(inode->IsRegularNode());
299 
300 	uint16 oldLength = oldName ? strlen(oldName) : 0;
301 	uint16 newLength = newName ? strlen(newName) : 0;
302 	return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName,
303 		oldLength, (uint8 *)newName, newLength, inode);
304 }
305 
306 
307 status_t
308 Index::InsertSize(Transaction &transaction, Inode *inode)
309 {
310 	ASSERT(inode->IsFile());
311 
312 	off_t size = inode->Size();
313 	return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size, sizeof(int64), inode);
314 }
315 
316 
317 status_t
318 Index::RemoveSize(Transaction &transaction, Inode *inode)
319 {
320 	ASSERT(inode->IsFile());
321 
322 	// Inode::OldSize() is the size that's in the index
323 	off_t size = inode->OldSize();
324 	return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size, sizeof(int64), NULL, 0, inode);
325 }
326 
327 
328 status_t
329 Index::UpdateSize(Transaction &transaction, Inode *inode)
330 {
331 	ASSERT(inode->IsFile());
332 
333 	off_t oldSize = inode->OldSize();
334 	off_t newSize = inode->Size();
335 
336 	status_t status = Update(transaction, "size", B_INT64_TYPE, (uint8 *)&oldSize,
337 		sizeof(int64), (uint8 *)&newSize, sizeof(int64), inode);
338 	if (status == B_OK)
339 		inode->UpdateOldSize();
340 
341 	return status;
342 }
343 
344 
345 status_t
346 Index::InsertLastModified(Transaction &transaction, Inode *inode)
347 {
348 	ASSERT(inode->IsFile() || inode->IsSymLink());
349 
350 	off_t modified = inode->LastModified();
351 	return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0,
352 		(uint8 *)&modified, sizeof(int64), inode);
353 }
354 
355 
356 status_t
357 Index::RemoveLastModified(Transaction &transaction, Inode *inode)
358 {
359 	ASSERT(inode->IsFile() || inode->IsSymLink());
360 
361 	// Inode::OldLastModified() is the value which is in the index
362 	off_t modified = inode->OldLastModified();
363 	return Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&modified,
364 		sizeof(int64), NULL, 0, inode);
365 }
366 
367 
368 status_t
369 Index::UpdateLastModified(Transaction &transaction, Inode *inode, off_t modified)
370 {
371 	ASSERT(inode->IsFile() || inode->IsSymLink());
372 
373 	off_t oldModified = inode->OldLastModified();
374 	if (modified == -1)
375 		modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT;
376 	modified |= fVolume->GetUniqueID() & INODE_TIME_MASK;
377 
378 	status_t status = Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&oldModified,
379 		sizeof(int64), (uint8 *)&modified, sizeof(int64), inode);
380 
381 	inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified);
382 	if (status == B_OK)
383 		inode->UpdateOldLastModified();
384 
385 	return status;
386 }
387 
388