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