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