xref: /haiku/src/add-ons/kernel/file_systems/bfs/Index.cpp (revision 72156a402f54ea4be9dc3e3e9704c612f7d9ad16)
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 // 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 
24 Index::Index(Volume* volume)
25 	:
26 	fVolume(volume),
27 	fNode(NULL)
28 {
29 }
30 
31 
32 Index::~Index()
33 {
34 	if (fNode == NULL)
35 		return;
36 
37 	if (fVolume->ID() >= 0)
38 		put_vnode(fVolume->FSVolume(), fNode->ID());
39 }
40 
41 
42 void
43 Index::Unset()
44 {
45 	if (fNode == NULL)
46 		return;
47 
48 	if (fVolume->ID() >= 0)
49 		put_vnode(fVolume->FSVolume(), fNode->ID());
50 	fNode = NULL;
51 	fName = NULL;
52 }
53 
54 
55 /*!	Sets the index to specified one. Returns an error if the index could
56 	not be found or initialized.
57 	Note, Index::Update() may be called on the object even if this method
58 	failed previously. In this case, it will only update live queries for
59 	the updated attribute.
60 */
61 status_t
62 Index::SetTo(const char* name)
63 {
64 	// remove the old node, if the index is set for the second time
65 	Unset();
66 
67 	fName = name;
68 		// only stores the pointer, so it assumes that it will stay constant
69 		// in further comparisons (currently only used in Index::Update())
70 
71 	// Note, the name is saved even if the index couldn't be initialized!
72 	// This is used to optimize Index::Update() in case there is no index
73 
74 	Inode* indices = fVolume->IndicesNode();
75 	if (indices == NULL)
76 		return B_ENTRY_NOT_FOUND;
77 
78 	InodeReadLocker locker(indices);
79 
80 	BPlusTree* tree = indices->Tree();
81 	if (tree == NULL)
82 		return B_BAD_VALUE;
83 
84 	ino_t id;
85 	status_t status = tree->Find((uint8*)name, (uint16)strlen(name), &id);
86 	if (status != B_OK)
87 		return status;
88 
89 	Vnode vnode(fVolume, id);
90 	if (vnode.Get(&fNode) != B_OK)
91 		return B_ENTRY_NOT_FOUND;
92 
93 	if (fNode == NULL) {
94 		FATAL(("fatal error at Index::InitCheck(), get_vnode() returned "
95 			"NULL pointer\n"));
96 		return B_ERROR;
97 	}
98 
99 	vnode.Keep();
100 	return B_OK;
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 /*!	Updates the specified index, the oldKey will be removed from, the newKey
217 	inserted into the tree.
218 	If the method returns B_BAD_INDEX, it means the index couldn't be found -
219 	the most common reason will be that the index doesn't exist.
220 	You may not want to let the whole transaction fail because of that.
221 */
222 status_t
223 Index::Update(Transaction& transaction, const char* name, int32 type,
224 	const uint8* oldKey, uint16 oldLength, const uint8* newKey,
225 	uint16 newLength, Inode* inode)
226 {
227 	if (name == NULL
228 		|| (oldKey == NULL && newKey == NULL)
229 		|| (oldKey != NULL && oldLength == 0)
230 		|| (newKey != NULL && newLength == 0))
231 		return B_BAD_VALUE;
232 
233 	// B_MIME_STRING_TYPE is the only supported non-standard type
234 	if (type == B_MIME_STRING_TYPE)
235 		type = B_STRING_TYPE;
236 
237 	// If the two keys are identical, don't do anything - only compare if the
238 	// type has been set, until we have a real type code, we can't do much
239 	// about the comparison here
240 	if (!compareKeys(type, oldKey, oldLength, newKey, newLength))
241 		return B_OK;
242 
243 	// update all live queries about the change, if they have an index or not
244 	fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength,
245 		newKey, newLength);
246 
247 	if (((name != fName || strcmp(name, fName)) && SetTo(name) != B_OK)
248 		|| fNode == NULL)
249 		return B_BAD_INDEX;
250 
251 	BPlusTree* tree = Node()->Tree();
252 	if (tree == NULL)
253 		return B_BAD_VALUE;
254 
255 	// remove the old key from the tree
256 
257 	Node()->WriteLockInTransaction(transaction);
258 
259 	status_t status = B_OK;
260 
261 	if (oldKey != NULL) {
262 		status = tree->Remove(transaction, (const uint8*)oldKey, oldLength,
263 			inode->ID());
264 		if (status == B_ENTRY_NOT_FOUND) {
265 			// That's not nice, but no reason to let the whole thing fail
266 			INFORM(("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,
275 			inode->ID());
276 	}
277 
278 	RETURN_ERROR(status);
279 }
280 
281 
282 status_t
283 Index::InsertName(Transaction& transaction, const char* name, Inode* inode)
284 {
285 	return UpdateName(transaction, NULL, name, inode);
286 }
287 
288 
289 status_t
290 Index::RemoveName(Transaction& transaction, const char* name, Inode* inode)
291 {
292 	return UpdateName(transaction, name, NULL, inode);
293 }
294 
295 
296 status_t
297 Index::UpdateName(Transaction& transaction, const char* oldName,
298 	const char* newName, Inode* inode)
299 {
300 	ASSERT(inode->IsRegularNode());
301 
302 	uint16 oldLength = oldName != NULL ? strlen(oldName) : 0;
303 	uint16 newLength = newName != NULL ? strlen(newName) : 0;
304 	return Update(transaction, "name", B_STRING_TYPE, (uint8*)oldName,
305 		oldLength, (uint8*)newName, newLength, inode);
306 }
307 
308 
309 status_t
310 Index::InsertSize(Transaction& transaction, Inode* inode)
311 {
312 	ASSERT(inode->InSizeIndex());
313 
314 	off_t size = inode->Size();
315 	return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8*)&size,
316 		sizeof(int64), inode);
317 }
318 
319 
320 status_t
321 Index::RemoveSize(Transaction& transaction, Inode* inode)
322 {
323 	ASSERT(inode->InSizeIndex());
324 
325 	// Inode::OldSize() is the size that's in the index
326 	off_t size = inode->OldSize();
327 	return Update(transaction, "size", B_INT64_TYPE, (uint8*)&size,
328 		sizeof(int64), NULL, 0, inode);
329 }
330 
331 
332 status_t
333 Index::UpdateSize(Transaction& transaction, Inode* inode)
334 {
335 	ASSERT(inode->InSizeIndex());
336 
337 	off_t oldSize = inode->OldSize();
338 	off_t newSize = inode->Size();
339 
340 	status_t status = Update(transaction, "size", B_INT64_TYPE,
341 		(uint8*)&oldSize, sizeof(int64), (uint8*)&newSize, sizeof(int64),
342 		inode);
343 	if (status == B_OK)
344 		inode->UpdateOldSize();
345 
346 	return status;
347 }
348 
349 
350 status_t
351 Index::InsertLastModified(Transaction& transaction, Inode* inode)
352 {
353 	ASSERT(inode->InLastModifiedIndex());
354 
355 	off_t modified = inode->LastModified();
356 	return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0,
357 		(uint8*)&modified, sizeof(int64), inode);
358 }
359 
360 
361 status_t
362 Index::RemoveLastModified(Transaction& transaction, Inode* inode)
363 {
364 	ASSERT(inode->InLastModifiedIndex());
365 
366 	// Inode::OldLastModified() is the value which is in the index
367 	off_t modified = inode->OldLastModified();
368 	return Update(transaction, "last_modified", B_INT64_TYPE,
369 		(uint8*)&modified, sizeof(int64), NULL, 0, inode);
370 }
371 
372 
373 status_t
374 Index::UpdateLastModified(Transaction& transaction, Inode* inode,
375 	bigtime_t modified)
376 {
377 	ASSERT(inode->InLastModifiedIndex());
378 
379 	bigtime_t oldModified = inode->OldLastModified();
380 	if (modified == -1)
381 		modified = bfs_inode::ToInode(real_time_clock_usecs());
382 
383 	status_t status = Update(transaction, "last_modified", B_INT64_TYPE,
384 		(uint8*)&oldModified, sizeof(int64), (uint8*)&modified,
385 		sizeof(int64), inode);
386 
387 	inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified);
388 	if (status == B_OK)
389 		inode->UpdateOldLastModified();
390 
391 	return status;
392 }
393 
394