xref: /haiku/src/add-ons/kernel/file_systems/bfs/Index.cpp (revision 80d75f15dfa48ebea421c6b2c19a5296cc63d7eb)
1 /*
2  * Copyright 2001-2007, 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->ID(), 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->ID(), fNode->ID());
49 	fNode = NULL;
50 	fName = NULL;
51 }
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 	BPlusTree *tree;
79 	if (indices->GetTree(&tree) != B_OK)
80 		return B_BAD_VALUE;
81 
82 	ino_t id;
83 	status_t status = tree->Find((uint8 *)name, (uint16)strlen(name), &id);
84 	if (status != B_OK)
85 		return status;
86 
87 	Vnode vnode(fVolume, id);
88 	if (vnode.Get(&fNode) != B_OK)
89 		return B_ENTRY_NOT_FOUND;
90 
91 	if (fNode == NULL) {
92 		FATAL(("fatal error at Index::InitCheck(), get_vnode() returned "
93 			"NULL pointer\n"));
94 		return B_ERROR;
95 	}
96 
97 	vnode.Keep();
98 	return B_OK;
99 }
100 
101 
102 /*!
103 	Returns a standard type code for the stat() index type codes. Returns
104 	zero if the type is not known (can only happen if the mode field is
105 	corrupted somehow or not that of an index).
106 */
107 uint32
108 Index::Type()
109 {
110 	if (fNode == NULL)
111 		return 0;
112 
113 	switch (fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
114 			| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX
115 			| S_DOUBLE_INDEX)) {
116 		case S_INT_INDEX:
117 			return B_INT32_TYPE;
118 		case S_UINT_INDEX:
119 			return B_UINT32_TYPE;
120 		case S_LONG_LONG_INDEX:
121 			return B_INT64_TYPE;
122 		case S_ULONG_LONG_INDEX:
123 			return B_UINT64_TYPE;
124 		case S_FLOAT_INDEX:
125 			return B_FLOAT_TYPE;
126 		case S_DOUBLE_INDEX:
127 			return B_DOUBLE_TYPE;
128 		case S_STR_INDEX:
129 			return B_STRING_TYPE;
130 	}
131 	FATAL(("index has unknown type!\n"));
132 	return 0;
133 }
134 
135 
136 size_t
137 Index::KeySize()
138 {
139 	if (fNode == NULL)
140 		return 0;
141 
142 	int32 mode = fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
143 		| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX
144 		| S_DOUBLE_INDEX);
145 
146 	if (mode == S_STR_INDEX)
147 		// string indices don't have a fixed key size
148 		return 0;
149 
150 	switch (mode) {
151 		case S_INT_INDEX:
152 		case S_UINT_INDEX:
153 			return sizeof(int32);
154 		case S_LONG_LONG_INDEX:
155 		case S_ULONG_LONG_INDEX:
156 			return sizeof(int64);
157 		case S_FLOAT_INDEX:
158 			return sizeof(float);
159 		case S_DOUBLE_INDEX:
160 			return sizeof(double);
161 	}
162 	FATAL(("index has unknown type!\n"));
163 	return 0;
164 }
165 
166 
167 status_t
168 Index::Create(Transaction &transaction, const char *name, uint32 type)
169 {
170 	Unset();
171 
172 	int32 mode = 0;
173 	switch (type) {
174 		case B_INT32_TYPE:
175 			mode = S_INT_INDEX;
176 			break;
177 		case B_UINT32_TYPE:
178 			mode = S_UINT_INDEX;
179 			break;
180 		case B_INT64_TYPE:
181 			mode = S_LONG_LONG_INDEX;
182 			break;
183 		case B_UINT64_TYPE:
184 			mode = S_ULONG_LONG_INDEX;
185 			break;
186 		case B_FLOAT_TYPE:
187 			mode = S_FLOAT_INDEX;
188 			break;
189 		case B_DOUBLE_TYPE:
190 			mode = S_DOUBLE_INDEX;
191 			break;
192 		case B_STRING_TYPE:
193 		case B_MIME_STRING_TYPE:
194 			// B_MIME_STRING_TYPE is the only supported non-standard type, but
195 			// will be handled like a B_STRING_TYPE internally
196 			mode = S_STR_INDEX;
197 			break;
198 		default:
199 			return B_BAD_TYPE;
200 	}
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 	if (oldKey != NULL) {
272 		status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength,
273 			inode->ID());
274 		if (status == B_ENTRY_NOT_FOUND) {
275 			// That's not nice, but no reason to let the whole thing fail
276 			INFORM(("Could not find value in index \"%s\"!\n", name));
277 		} else if (status < B_OK)
278 			return status;
279 	}
280 
281 	// add the new key to the tree
282 
283 	if (newKey != NULL) {
284 		status = tree->Insert(transaction, (const uint8 *)newKey, newLength,
285 			inode->ID());
286 	}
287 
288 	RETURN_ERROR(status);
289 }
290 
291 
292 status_t
293 Index::InsertName(Transaction &transaction, const char *name, Inode *inode)
294 {
295 	return UpdateName(transaction, NULL, name, inode);
296 }
297 
298 
299 status_t
300 Index::RemoveName(Transaction &transaction, const char *name, Inode *inode)
301 {
302 	return UpdateName(transaction, name, NULL, inode);
303 }
304 
305 
306 status_t
307 Index::UpdateName(Transaction &transaction, const char *oldName,
308 	const char *newName, Inode *inode)
309 {
310 	ASSERT(inode->IsRegularNode());
311 
312 	uint16 oldLength = oldName != NULL ? strlen(oldName) : 0;
313 	uint16 newLength = newName != NULL ? strlen(newName) : 0;
314 	return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName,
315 		oldLength, (uint8 *)newName, newLength, inode);
316 }
317 
318 
319 status_t
320 Index::InsertSize(Transaction &transaction, Inode *inode)
321 {
322 	ASSERT(inode->IsFile());
323 
324 	off_t size = inode->Size();
325 	return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size,
326 		sizeof(int64), inode);
327 }
328 
329 
330 status_t
331 Index::RemoveSize(Transaction &transaction, Inode *inode)
332 {
333 	ASSERT(inode->IsFile());
334 
335 	// Inode::OldSize() is the size that's in the index
336 	off_t size = inode->OldSize();
337 	return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size,
338 		sizeof(int64), NULL, 0, inode);
339 }
340 
341 
342 status_t
343 Index::UpdateSize(Transaction &transaction, Inode *inode)
344 {
345 	ASSERT(inode->IsFile());
346 
347 	off_t oldSize = inode->OldSize();
348 	off_t newSize = inode->Size();
349 
350 	status_t status = Update(transaction, "size", B_INT64_TYPE,
351 		(uint8 *)&oldSize, sizeof(int64), (uint8 *)&newSize, sizeof(int64),
352 		inode);
353 	if (status == B_OK)
354 		inode->UpdateOldSize();
355 
356 	return status;
357 }
358 
359 
360 status_t
361 Index::InsertLastModified(Transaction &transaction, Inode *inode)
362 {
363 	ASSERT(inode->IsFile() || inode->IsSymLink());
364 
365 	off_t modified = inode->LastModified();
366 	return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0,
367 		(uint8 *)&modified, sizeof(int64), inode);
368 }
369 
370 
371 status_t
372 Index::RemoveLastModified(Transaction &transaction, Inode *inode)
373 {
374 	ASSERT(inode->IsFile() || inode->IsSymLink());
375 
376 	// Inode::OldLastModified() is the value which is in the index
377 	off_t modified = inode->OldLastModified();
378 	return Update(transaction, "last_modified", B_INT64_TYPE,
379 		(uint8 *)&modified, sizeof(int64), NULL, 0, inode);
380 }
381 
382 
383 status_t
384 Index::UpdateLastModified(Transaction &transaction, Inode *inode,
385 	off_t modified)
386 {
387 	ASSERT(inode->IsFile() || inode->IsSymLink());
388 
389 	off_t oldModified = inode->OldLastModified();
390 	if (modified == -1)
391 		modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT;
392 	modified |= fVolume->GetUniqueID() & INODE_TIME_MASK;
393 
394 	status_t status = Update(transaction, "last_modified", B_INT64_TYPE,
395 		(uint8 *)&oldModified, sizeof(int64), (uint8 *)&modified,
396 		sizeof(int64), inode);
397 
398 	inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified);
399 	if (status == B_OK)
400 		inode->UpdateOldLastModified();
401 
402 	return status;
403 }
404 
405