xref: /haiku/src/add-ons/kernel/file_systems/bfs/Index.cpp (revision 7a74a5df454197933bc6e80a542102362ee98703)
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 
17 Index::Index(Volume* volume)
18 	:
19 	fVolume(volume),
20 	fNode(NULL)
21 {
22 }
23 
24 
25 Index::~Index()
26 {
27 	if (fNode == NULL)
28 		return;
29 
30 	if (fVolume->ID() >= 0)
31 		put_vnode(fVolume->FSVolume(), fNode->ID());
32 }
33 
34 
35 void
36 Index::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 */
54 status_t
55 Index::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 */
101 uint32
102 Index::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 
130 size_t
131 Index::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 
161 status_t
162 Index::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 */
215 status_t
216 Index::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 
275 status_t
276 Index::InsertName(Transaction& transaction, const char* name, Inode* inode)
277 {
278 	return UpdateName(transaction, NULL, name, inode);
279 }
280 
281 
282 status_t
283 Index::RemoveName(Transaction& transaction, const char* name, Inode* inode)
284 {
285 	return UpdateName(transaction, name, NULL, inode);
286 }
287 
288 
289 status_t
290 Index::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 
302 status_t
303 Index::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 
313 status_t
314 Index::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 
325 status_t
326 Index::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 
343 status_t
344 Index::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 
354 status_t
355 Index::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 
366 status_t
367 Index::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