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