1/* Index - index access functions
2**
3** Initial version by Axel Dörfler, axeld@pinc-software.de
4** This file may be used under the terms of the OpenBeOS 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
24Index::Index(Volume *volume)
25	:
26	fVolume(volume),
27	fNode(NULL)
28{
29}
30
31
32Index::~Index()
33{
34	if (fNode == NULL)
35		return;
36
37	put_vnode(fVolume->ID(), fNode->ID());
38}
39
40
41void
42Index::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
60status_t
61Index::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
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 | 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
133size_t
134Index::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
163status_t
164Index::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
219status_t
220Index::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			FATAL(("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 status;
277}
278
279
280status_t
281Index::InsertName(Transaction *transaction, const char *name, Inode *inode)
282{
283	return UpdateName(transaction, NULL, name, inode);
284}
285
286
287status_t
288Index::RemoveName(Transaction *transaction, const char *name, Inode *inode)
289{
290	return UpdateName(transaction, name, NULL, inode);
291}
292
293
294status_t
295Index::UpdateName(Transaction *transaction, const char *oldName, const char *newName, Inode *inode)
296{
297	uint16 oldLength = oldName ? strlen(oldName) : 0;
298	uint16 newLength = newName ? strlen(newName) : 0;
299	return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName, oldLength,
300		(uint8 *)newName, newLength, inode);
301}
302
303
304status_t
305Index::InsertSize(Transaction *transaction, Inode *inode)
306{
307	off_t size = inode->Size();
308	return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size, sizeof(int64), inode);
309}
310
311
312status_t
313Index::RemoveSize(Transaction *transaction, Inode *inode)
314{
315	// Inode::OldSize() is the size that's in the index
316	off_t size = inode->OldSize();
317	return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size, sizeof(int64), NULL, 0, inode);
318}
319
320
321status_t
322Index::UpdateSize(Transaction *transaction, Inode *inode)
323{
324	off_t oldSize = inode->OldSize();
325	off_t newSize = inode->Size();
326
327	status_t status = Update(transaction, "size", B_INT64_TYPE, (uint8 *)&oldSize,
328		sizeof(int64), (uint8 *)&newSize, sizeof(int64), inode);
329	if (status == B_OK)
330		inode->UpdateOldSize();
331
332	return status;
333}
334
335
336status_t
337Index::InsertLastModified(Transaction *transaction, Inode *inode)
338{
339	off_t modified = inode->LastModified();
340	return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0,
341		(uint8 *)&modified, sizeof(int64), inode);
342}
343
344
345status_t
346Index::RemoveLastModified(Transaction *transaction, Inode *inode)
347{
348	// Inode::OldLastModified() is the value which is in the index
349	off_t modified = inode->OldLastModified();
350	return Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&modified,
351		sizeof(int64), NULL, 0, inode);
352}
353
354
355status_t
356Index::UpdateLastModified(Transaction *transaction, Inode *inode, off_t modified)
357{
358	off_t oldModified = inode->OldLastModified();
359	if (modified == -1)
360		modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT;
361	modified |= fVolume->GetUniqueID() & INODE_TIME_MASK;
362
363	status_t status = Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&oldModified,
364		sizeof(int64), (uint8 *)&modified, sizeof(int64), inode);
365
366	inode->Node()->last_modified_time = modified;
367	if (status == B_OK)
368		inode->UpdateOldLastModified();
369
370	return status;
371}
372
373