xref: /haiku/src/add-ons/kernel/file_systems/bfs/Index.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
1 /*
2  * Copyright 2001-2014, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 
7 //! Index access functions
8 
9 
10 #include "Index.h"
11 
12 #include <file_systems/QueryParserUtils.h>
13 
14 #include "Debug.h"
15 #include "Volume.h"
16 #include "Inode.h"
17 #include "BPlusTree.h"
18 
19 
20 Index::Index(Volume* volume)
21 	:
22 	fVolume(volume),
23 	fNode(NULL)
24 {
25 }
26 
27 
28 Index::~Index()
29 {
30 	if (fNode == NULL)
31 		return;
32 
33 	if (fVolume->ID() >= 0)
34 		put_vnode(fVolume->FSVolume(), fNode->ID());
35 }
36 
37 
38 void
39 Index::Unset()
40 {
41 	if (fNode == NULL)
42 		return;
43 
44 	if (fVolume->ID() >= 0)
45 		put_vnode(fVolume->FSVolume(), fNode->ID());
46 	fNode = NULL;
47 	fName = NULL;
48 }
49 
50 
51 /*!	Sets the index to specified one. Returns an error if the index could
52 	not be found or initialized.
53 	Note, Index::Update() may be called on the object even if this method
54 	failed previously. In this case, it will only update live queries for
55 	the updated attribute.
56 */
57 status_t
58 Index::SetTo(const char* name)
59 {
60 	// remove the old node, if the index is set for the second time
61 	Unset();
62 
63 	fName = name;
64 		// only stores the pointer, so it assumes that it will stay constant
65 		// in further comparisons (currently only used in Index::Update())
66 
67 	// Note, the name is saved even if the index couldn't be initialized!
68 	// This is used to optimize Index::Update() in case there is no index
69 
70 	Inode* indices = fVolume->IndicesNode();
71 	if (indices == NULL)
72 		return B_ENTRY_NOT_FOUND;
73 
74 	InodeReadLocker locker(indices);
75 
76 	BPlusTree* tree = indices->Tree();
77 	if (tree == NULL)
78 		return B_BAD_VALUE;
79 
80 	ino_t id;
81 	status_t status = tree->Find((uint8*)name, (uint16)strlen(name), &id);
82 	if (status != B_OK)
83 		return status;
84 
85 	Vnode vnode(fVolume, id);
86 	if (vnode.Get(&fNode) != B_OK)
87 		return B_ENTRY_NOT_FOUND;
88 
89 	if (fNode == NULL) {
90 		FATAL(("fatal error at Index::InitCheck(), get_vnode() returned "
91 			"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 uint32
105 Index::Type()
106 {
107 	if (fNode == NULL)
108 		return 0;
109 
110 	switch (fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
111 			| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX
112 			| 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
140 		| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX
141 		| S_DOUBLE_INDEX);
142 
143 	if (mode == S_STR_INDEX)
144 		// string indices don't have a fixed key size
145 		return 0;
146 
147 	switch (mode) {
148 		case S_INT_INDEX:
149 		case S_UINT_INDEX:
150 			return sizeof(int32);
151 		case S_LONG_LONG_INDEX:
152 		case S_ULONG_LONG_INDEX:
153 			return sizeof(int64);
154 		case S_FLOAT_INDEX:
155 			return sizeof(float);
156 		case S_DOUBLE_INDEX:
157 			return sizeof(double);
158 	}
159 	FATAL(("index has unknown type!\n"));
160 	return 0;
161 }
162 
163 
164 status_t
165 Index::Create(Transaction& transaction, const char* name, uint32 type)
166 {
167 	Unset();
168 
169 	int32 mode = 0;
170 	switch (type) {
171 		case B_INT32_TYPE:
172 			mode = S_INT_INDEX;
173 			break;
174 		case B_UINT32_TYPE:
175 			mode = S_UINT_INDEX;
176 			break;
177 		case B_INT64_TYPE:
178 			mode = S_LONG_LONG_INDEX;
179 			break;
180 		case B_UINT64_TYPE:
181 			mode = S_ULONG_LONG_INDEX;
182 			break;
183 		case B_FLOAT_TYPE:
184 			mode = S_FLOAT_INDEX;
185 			break;
186 		case B_DOUBLE_TYPE:
187 			mode = S_DOUBLE_INDEX;
188 			break;
189 		case B_STRING_TYPE:
190 		case B_MIME_STRING_TYPE:
191 			// B_MIME_STRING_TYPE is the only supported non-standard type, but
192 			// will be handled like a B_STRING_TYPE internally
193 			mode = S_STR_INDEX;
194 			break;
195 		default:
196 			return B_BAD_TYPE;
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, 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 status_t
219 Index::Update(Transaction& transaction, const char* name, int32 type,
220 	const uint8* oldKey, uint16 oldLength, const uint8* newKey,
221 	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 (oldLength == 0) {
237 		if (newLength == 0)
238 			return B_OK;
239 	} else if (newLength != 0 && !QueryParser::compareKeys(type,
240 			oldKey, oldLength, newKey, newLength)) {
241 		return B_OK;
242 	}
243 
244 	// update all live queries about the change, if they have an index or not
245 	fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength,
246 		newKey, newLength);
247 
248 	if (((name != fName || strcmp(name, fName)) && SetTo(name) != B_OK)
249 		|| fNode == NULL)
250 		return B_BAD_INDEX;
251 
252 	BPlusTree* tree = Node()->Tree();
253 	if (tree == NULL)
254 		return B_BAD_VALUE;
255 
256 	// remove the old key from the tree
257 
258 	Node()->WriteLockInTransaction(transaction);
259 
260 	status_t status = B_OK;
261 
262 	if (oldKey != NULL) {
263 		status = tree->Remove(transaction, (const uint8*)oldKey, oldLength,
264 			inode->ID());
265 		if (status == B_ENTRY_NOT_FOUND) {
266 			// That's not nice, but no reason to let the whole thing fail
267 			INFORM(("Could not find value in index \"%s\"!\n", name));
268 		} else if (status != B_OK)
269 			return status;
270 	}
271 
272 	// add the new key to the tree
273 
274 	if (newKey != NULL) {
275 		status = tree->Insert(transaction, (const uint8*)newKey, newLength,
276 			inode->ID());
277 	}
278 
279 	RETURN_ERROR(status);
280 }
281 
282 
283 status_t
284 Index::InsertName(Transaction& transaction, const char* name, Inode* inode)
285 {
286 	return UpdateName(transaction, NULL, name, inode);
287 }
288 
289 
290 status_t
291 Index::RemoveName(Transaction& transaction, const char* name, Inode* inode)
292 {
293 	return UpdateName(transaction, name, NULL, inode);
294 }
295 
296 
297 status_t
298 Index::UpdateName(Transaction& transaction, const char* oldName,
299 	const char* newName, Inode* inode)
300 {
301 	ASSERT(inode->IsRegularNode());
302 
303 	uint16 oldLength = oldName != NULL ? strlen(oldName) : 0;
304 	uint16 newLength = newName != NULL ? strlen(newName) : 0;
305 	return Update(transaction, "name", B_STRING_TYPE, (uint8*)oldName,
306 		oldLength, (uint8*)newName, newLength, inode);
307 }
308 
309 
310 status_t
311 Index::InsertSize(Transaction& transaction, Inode* inode)
312 {
313 	ASSERT(inode->InSizeIndex());
314 
315 	off_t size = inode->Size();
316 	return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8*)&size,
317 		sizeof(int64), inode);
318 }
319 
320 
321 status_t
322 Index::RemoveSize(Transaction& transaction, Inode* inode)
323 {
324 	ASSERT(inode->InSizeIndex());
325 
326 	// Inode::OldSize() is the size that's in the index
327 	off_t size = inode->OldSize();
328 	return Update(transaction, "size", B_INT64_TYPE, (uint8*)&size,
329 		sizeof(int64), NULL, 0, inode);
330 }
331 
332 
333 status_t
334 Index::UpdateSize(Transaction& transaction, Inode* inode)
335 {
336 	ASSERT(inode->InSizeIndex());
337 
338 	off_t oldSize = inode->OldSize();
339 	off_t newSize = inode->Size();
340 
341 	status_t status = Update(transaction, "size", B_INT64_TYPE,
342 		(uint8*)&oldSize, sizeof(int64), (uint8*)&newSize, sizeof(int64),
343 		inode);
344 	if (status == B_OK)
345 		inode->UpdateOldSize();
346 
347 	return status;
348 }
349 
350 
351 status_t
352 Index::InsertLastModified(Transaction& transaction, Inode* inode)
353 {
354 	ASSERT(inode->InLastModifiedIndex());
355 
356 	off_t modified = inode->LastModified();
357 	return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0,
358 		(uint8*)&modified, sizeof(int64), inode);
359 }
360 
361 
362 status_t
363 Index::RemoveLastModified(Transaction& transaction, Inode* inode)
364 {
365 	ASSERT(inode->InLastModifiedIndex());
366 
367 	// Inode::OldLastModified() is the value which is in the index
368 	off_t modified = inode->OldLastModified();
369 	return Update(transaction, "last_modified", B_INT64_TYPE,
370 		(uint8*)&modified, sizeof(int64), NULL, 0, inode);
371 }
372 
373 
374 status_t
375 Index::UpdateLastModified(Transaction& transaction, Inode* inode,
376 	bigtime_t modified)
377 {
378 	ASSERT(inode->InLastModifiedIndex());
379 
380 	bigtime_t oldModified = inode->OldLastModified();
381 	if (modified == -1)
382 		modified = bfs_inode::ToInode(real_time_clock_usecs());
383 
384 	status_t status = Update(transaction, "last_modified", B_INT64_TYPE,
385 		(uint8*)&oldModified, sizeof(int64), (uint8*)&modified,
386 		sizeof(int64), inode);
387 
388 	inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified);
389 	if (status == B_OK)
390 		inode->UpdateOldLastModified();
391 
392 	return status;
393 }
394 
395