xref: /haiku/src/add-ons/kernel/file_systems/bfs/Index.cpp (revision 58481f0f6ef1a61ba07283f012cafbc2ed874ead)
1 /*
2  * Copyright 2001-2008, 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;
80 	if (indices->GetTree(&tree) != B_OK)
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;
265 	status_t status = Node()->GetTree(&tree);
266 	if (status < B_OK)
267 		return status;
268 
269 	// remove the old key from the tree
270 
271 	Node()->WriteLockInTransaction(transaction);
272 
273 	if (oldKey != NULL) {
274 		status = tree->Remove(transaction, (const uint8*)oldKey, oldLength,
275 			inode->ID());
276 		if (status == B_ENTRY_NOT_FOUND) {
277 			// That's not nice, but no reason to let the whole thing fail
278 			INFORM(("Could not find value in index \"%s\"!\n", name));
279 		} else if (status < B_OK)
280 			return status;
281 	}
282 
283 	// add the new key to the tree
284 
285 	if (newKey != NULL) {
286 		status = tree->Insert(transaction, (const uint8*)newKey, newLength,
287 			inode->ID());
288 	}
289 
290 	RETURN_ERROR(status);
291 }
292 
293 
294 status_t
295 Index::InsertName(Transaction &transaction, const char* name, Inode* inode)
296 {
297 	return UpdateName(transaction, NULL, name, inode);
298 }
299 
300 
301 status_t
302 Index::RemoveName(Transaction &transaction, const char* name, Inode* inode)
303 {
304 	return UpdateName(transaction, name, NULL, inode);
305 }
306 
307 
308 status_t
309 Index::UpdateName(Transaction &transaction, const char* oldName,
310 	const char* newName, Inode* inode)
311 {
312 	ASSERT(inode->IsRegularNode());
313 
314 	uint16 oldLength = oldName != NULL ? strlen(oldName) : 0;
315 	uint16 newLength = newName != NULL ? strlen(newName) : 0;
316 	return Update(transaction, "name", B_STRING_TYPE, (uint8*)oldName,
317 		oldLength, (uint8*)newName, newLength, inode);
318 }
319 
320 
321 status_t
322 Index::InsertSize(Transaction &transaction, Inode* inode)
323 {
324 	ASSERT(inode->InSizeIndex());
325 
326 	off_t size = inode->Size();
327 	return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8*)&size,
328 		sizeof(int64), inode);
329 }
330 
331 
332 status_t
333 Index::RemoveSize(Transaction &transaction, Inode* inode)
334 {
335 	ASSERT(inode->InSizeIndex());
336 
337 	// Inode::OldSize() is the size that's in the index
338 	off_t size = inode->OldSize();
339 	return Update(transaction, "size", B_INT64_TYPE, (uint8*)&size,
340 		sizeof(int64), NULL, 0, inode);
341 }
342 
343 
344 status_t
345 Index::UpdateSize(Transaction &transaction, Inode* inode)
346 {
347 	ASSERT(inode->InSizeIndex());
348 
349 	off_t oldSize = inode->OldSize();
350 	off_t newSize = inode->Size();
351 
352 	status_t status = Update(transaction, "size", B_INT64_TYPE,
353 		(uint8*)&oldSize, sizeof(int64), (uint8*)&newSize, sizeof(int64),
354 		inode);
355 	if (status == B_OK)
356 		inode->UpdateOldSize();
357 
358 	return status;
359 }
360 
361 
362 status_t
363 Index::InsertLastModified(Transaction &transaction, Inode* inode)
364 {
365 	ASSERT(inode->InLastModifiedIndex());
366 
367 	off_t modified = inode->LastModified();
368 	return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0,
369 		(uint8*)&modified, sizeof(int64), inode);
370 }
371 
372 
373 status_t
374 Index::RemoveLastModified(Transaction &transaction, Inode* inode)
375 {
376 	ASSERT(inode->InLastModifiedIndex());
377 
378 	// Inode::OldLastModified() is the value which is in the index
379 	off_t modified = inode->OldLastModified();
380 	return Update(transaction, "last_modified", B_INT64_TYPE,
381 		(uint8*)&modified, sizeof(int64), NULL, 0, inode);
382 }
383 
384 
385 status_t
386 Index::UpdateLastModified(Transaction &transaction, Inode* inode,
387 	bigtime_t modified)
388 {
389 	ASSERT(inode->InLastModifiedIndex());
390 
391 	bigtime_t oldModified = inode->OldLastModified();
392 	if (modified == -1)
393 		modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT;
394 	modified &= ~INODE_TIME_MASK;
395 	modified |= fVolume->GetUniqueID() & INODE_TIME_MASK;
396 
397 	status_t status = Update(transaction, "last_modified", B_INT64_TYPE,
398 		(uint8*)&oldModified, sizeof(int64), (uint8*)&modified,
399 		sizeof(int64), inode);
400 
401 	inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified);
402 	if (status == B_OK)
403 		inode->UpdateOldLastModified();
404 
405 	return status;
406 }
407 
408