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
20Index::Index(Volume* volume)
21	:
22	fVolume(volume),
23	fNode(NULL),
24	fName(NULL)
25{
26}
27
28
29Index::~Index()
30{
31	if (fNode == NULL)
32		return;
33
34	if (fVolume->ID() >= 0)
35		put_vnode(fVolume->FSVolume(), fNode->ID());
36}
37
38
39void
40Index::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*/
58status_t
59Index::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*/
105uint32
106Index::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
134size_t
135Index::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
165status_t
166Index::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*/
219status_t
220Index::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
284status_t
285Index::InsertName(Transaction& transaction, const char* name, Inode* inode)
286{
287	return UpdateName(transaction, NULL, name, inode);
288}
289
290
291status_t
292Index::RemoveName(Transaction& transaction, const char* name, Inode* inode)
293{
294	return UpdateName(transaction, name, NULL, inode);
295}
296
297
298status_t
299Index::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
311status_t
312Index::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
322status_t
323Index::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
334status_t
335Index::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
352status_t
353Index::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
363status_t
364Index::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
375status_t
376Index::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