xref: /haiku/src/add-ons/kernel/file_systems/bfs/Index.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
1 /*
2  * Copyright 2001-2017, 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 	fName(NULL)
25 {
26 }
27 
28 
29 Index::~Index()
30 {
31 	if (fNode == NULL)
32 		return;
33 
34 	if (fVolume->ID() >= 0)
35 		put_vnode(fVolume->FSVolume(), fNode->ID());
36 }
37 
38 
39 void
40 Index::Unset()
41 {
42 	if (fNode == NULL)
43 		return;
44 
45 	if (fVolume->ID() >= 0)
46 		put_vnode(fVolume->FSVolume(), fNode->ID());
47 	fNode = NULL;
48 	fName = NULL;
49 }
50 
51 
52 /*!	Sets the index to specified one. Returns an error if the index could
53 	not be found or initialized.
54 	Note, Index::Update() may be called on the object even if this method
55 	failed previously. In this case, it will only update live queries for
56 	the updated attribute.
57 */
58 status_t
59 Index::SetTo(const char* name)
60 {
61 	// remove the old node, if the index is set for the second time
62 	Unset();
63 
64 	fName = name;
65 		// only stores the pointer, so it assumes that it will stay constant
66 		// in further comparisons (currently only used in Index::Update())
67 
68 	// Note, the name is saved even if the index couldn't be initialized!
69 	// This is used to optimize Index::Update() in case there is no index
70 
71 	Inode* indices = fVolume->IndicesNode();
72 	if (indices == NULL)
73 		return B_ENTRY_NOT_FOUND;
74 
75 	InodeReadLocker locker(indices);
76 
77 	BPlusTree* tree = indices->Tree();
78 	if (tree == NULL)
79 		return B_BAD_VALUE;
80 
81 	ino_t 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 "
92 			"NULL pointer\n"));
93 		return B_ERROR;
94 	}
95 
96 	vnode.Keep();
97 	return B_OK;
98 }
99 
100 
101 /*!	Returns a standard type code for the stat() index type codes. Returns
102 	zero if the type is not known (can only happen if the mode field is
103 	corrupted somehow or not that of an index).
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
112 			| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX
113 			| S_DOUBLE_INDEX)) {
114 		case S_INT_INDEX:
115 			return B_INT32_TYPE;
116 		case S_UINT_INDEX:
117 			return B_UINT32_TYPE;
118 		case S_LONG_LONG_INDEX:
119 			return B_INT64_TYPE;
120 		case S_ULONG_LONG_INDEX:
121 			return B_UINT64_TYPE;
122 		case S_FLOAT_INDEX:
123 			return B_FLOAT_TYPE;
124 		case S_DOUBLE_INDEX:
125 			return B_DOUBLE_TYPE;
126 		case S_STR_INDEX:
127 			return B_STRING_TYPE;
128 	}
129 	FATAL(("index has unknown type!\n"));
130 	return 0;
131 }
132 
133 
134 size_t
135 Index::KeySize()
136 {
137 	if (fNode == NULL)
138 		return 0;
139 
140 	int32 mode = fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
141 		| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX
142 		| S_DOUBLE_INDEX);
143 
144 	if (mode == S_STR_INDEX)
145 		// string indices don't have a fixed key size
146 		return 0;
147 
148 	switch (mode) {
149 		case S_INT_INDEX:
150 		case S_UINT_INDEX:
151 			return sizeof(int32);
152 		case S_LONG_LONG_INDEX:
153 		case S_ULONG_LONG_INDEX:
154 			return sizeof(int64);
155 		case S_FLOAT_INDEX:
156 			return sizeof(float);
157 		case S_DOUBLE_INDEX:
158 			return sizeof(double);
159 	}
160 	FATAL(("index has unknown type!\n"));
161 	return 0;
162 }
163 
164 
165 status_t
166 Index::Create(Transaction& transaction, const char* name, uint32 type)
167 {
168 	Unset();
169 
170 	int32 mode = 0;
171 	switch (type) {
172 		case B_INT32_TYPE:
173 			mode = S_INT_INDEX;
174 			break;
175 		case B_UINT32_TYPE:
176 			mode = S_UINT_INDEX;
177 			break;
178 		case B_INT64_TYPE:
179 			mode = S_LONG_LONG_INDEX;
180 			break;
181 		case B_UINT64_TYPE:
182 			mode = S_ULONG_LONG_INDEX;
183 			break;
184 		case B_FLOAT_TYPE:
185 			mode = S_FLOAT_INDEX;
186 			break;
187 		case B_DOUBLE_TYPE:
188 			mode = S_DOUBLE_INDEX;
189 			break;
190 		case B_STRING_TYPE:
191 		case B_MIME_STRING_TYPE:
192 			// B_MIME_STRING_TYPE is the only supported non-standard type, but
193 			// will be handled like a B_STRING_TYPE internally
194 			mode = S_STR_INDEX;
195 			break;
196 		default:
197 			return B_BAD_TYPE;
198 	}
199 
200 	// do we need to create the index directory first?
201 	if (fVolume->IndicesNode() == NULL) {
202 		status_t status = fVolume->CreateIndicesRoot(transaction);
203 		if (status < B_OK)
204 			RETURN_ERROR(status);
205 	}
206 
207 	// Inode::Create() will keep the inode locked for us
208 	return Inode::Create(transaction, fVolume->IndicesNode(), name,
209 		S_INDEX_DIR | S_DIRECTORY | mode, 0, type, NULL, NULL, &fNode);
210 }
211 
212 
213 /*!	Updates the specified index, the oldKey will be removed from, the newKey
214 	inserted into the tree.
215 	If the method returns B_BAD_INDEX, it means the index couldn't be found -
216 	the most common reason will be that the index doesn't exist.
217 	You may not want to let the whole transaction fail because of that.
218 */
219 status_t
220 Index::Update(Transaction& transaction, const char* name, int32 type,
221 	const uint8* oldKey, uint16 oldLength, const uint8* newKey,
222 	uint16 newLength, Inode* inode)
223 {
224 	if (name == NULL
225 		|| (oldKey == NULL && newKey == NULL)
226 		|| (oldKey != NULL && oldLength == 0)
227 		|| (newKey != NULL && newLength == 0))
228 		return B_BAD_VALUE;
229 
230 	// B_MIME_STRING_TYPE is the only supported non-standard type
231 	if (type == B_MIME_STRING_TYPE)
232 		type = B_STRING_TYPE;
233 
234 	// If the two keys are identical, don't do anything - only compare if the
235 	// type has been set, until we have a real type code, we can't do much
236 	// about the comparison here
237 	if (oldLength == 0) {
238 		if (newLength == 0)
239 			return B_OK;
240 	} else if (newLength != 0 && !QueryParser::compareKeys(type,
241 			oldKey, oldLength, newKey, newLength)) {
242 		return B_OK;
243 	}
244 
245 	// update all live queries about the change, if they have an index or not
246 	fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength,
247 		newKey, newLength);
248 
249 	if (((name != fName || strcmp(name, fName)) && SetTo(name) != B_OK)
250 		|| fNode == NULL)
251 		return B_BAD_INDEX;
252 
253 	BPlusTree* tree = Node()->Tree();
254 	if (tree == NULL)
255 		return B_BAD_VALUE;
256 
257 	// remove the old key from the tree
258 
259 	Node()->WriteLockInTransaction(transaction);
260 
261 	status_t status = B_OK;
262 
263 	if (oldKey != NULL) {
264 		status = tree->Remove(transaction, (const uint8*)oldKey, oldLength,
265 			inode->ID());
266 		if (status == B_ENTRY_NOT_FOUND) {
267 			// That's not nice, but no reason to let the whole thing fail
268 			INFORM(("Could not find value in index \"%s\"!\n", name));
269 		} else if (status != B_OK)
270 			return status;
271 	}
272 
273 	// add the new key to the tree
274 
275 	if (newKey != NULL) {
276 		status = tree->Insert(transaction, (const uint8*)newKey, newLength,
277 			inode->ID());
278 	}
279 
280 	RETURN_ERROR(status);
281 }
282 
283 
284 status_t
285 Index::InsertName(Transaction& transaction, const char* name, Inode* inode)
286 {
287 	return UpdateName(transaction, NULL, name, inode);
288 }
289 
290 
291 status_t
292 Index::RemoveName(Transaction& transaction, const char* name, Inode* inode)
293 {
294 	return UpdateName(transaction, name, NULL, inode);
295 }
296 
297 
298 status_t
299 Index::UpdateName(Transaction& transaction, const char* oldName,
300 	const char* newName, Inode* inode)
301 {
302 	ASSERT(inode->IsRegularNode());
303 
304 	uint16 oldLength = oldName != NULL ? strlen(oldName) : 0;
305 	uint16 newLength = newName != NULL ? strlen(newName) : 0;
306 	return Update(transaction, "name", B_STRING_TYPE, (uint8*)oldName,
307 		oldLength, (uint8*)newName, newLength, inode);
308 }
309 
310 
311 status_t
312 Index::InsertSize(Transaction& transaction, Inode* inode)
313 {
314 	ASSERT(inode->InSizeIndex());
315 
316 	off_t size = inode->Size();
317 	return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8*)&size,
318 		sizeof(int64), inode);
319 }
320 
321 
322 status_t
323 Index::RemoveSize(Transaction& transaction, Inode* inode)
324 {
325 	ASSERT(inode->InSizeIndex());
326 
327 	// Inode::OldSize() is the size that's in the index
328 	off_t size = inode->OldSize();
329 	return Update(transaction, "size", B_INT64_TYPE, (uint8*)&size,
330 		sizeof(int64), NULL, 0, inode);
331 }
332 
333 
334 status_t
335 Index::UpdateSize(Transaction& transaction, Inode* inode)
336 {
337 	ASSERT(inode->InSizeIndex());
338 
339 	off_t oldSize = inode->OldSize();
340 	off_t newSize = inode->Size();
341 
342 	status_t status = Update(transaction, "size", B_INT64_TYPE,
343 		(uint8*)&oldSize, sizeof(int64), (uint8*)&newSize, sizeof(int64),
344 		inode);
345 	if (status == B_OK)
346 		inode->UpdateOldSize();
347 
348 	return status;
349 }
350 
351 
352 status_t
353 Index::InsertLastModified(Transaction& transaction, Inode* inode)
354 {
355 	ASSERT(inode->InLastModifiedIndex());
356 
357 	off_t modified = inode->LastModified();
358 	return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0,
359 		(uint8*)&modified, sizeof(int64), inode);
360 }
361 
362 
363 status_t
364 Index::RemoveLastModified(Transaction& transaction, Inode* inode)
365 {
366 	ASSERT(inode->InLastModifiedIndex());
367 
368 	// Inode::OldLastModified() is the value which is in the index
369 	off_t modified = inode->OldLastModified();
370 	return Update(transaction, "last_modified", B_INT64_TYPE,
371 		(uint8*)&modified, sizeof(int64), NULL, 0, inode);
372 }
373 
374 
375 status_t
376 Index::UpdateLastModified(Transaction& transaction, Inode* inode,
377 	bigtime_t modified)
378 {
379 	ASSERT(inode->InLastModifiedIndex());
380 
381 	bigtime_t oldModified = inode->OldLastModified();
382 	if (modified == -1)
383 		modified = bfs_inode::ToInode(real_time_clock_usecs());
384 
385 	status_t status = Update(transaction, "last_modified", B_INT64_TYPE,
386 		(uint8*)&oldModified, sizeof(int64), (uint8*)&modified,
387 		sizeof(int64), inode);
388 
389 	inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified);
390 	// No matter if the index exists or not, we will update the old last
391 	// modified field, since this is also being used to determine whether
392 	// or not to send out notifications.
393 	if (status == B_OK || status == B_BAD_INDEX)
394 		inode->UpdateOldLastModified();
395 
396 	return status;
397 }
398 
399