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 20 Index::Index(Volume* volume) 21 : 22 fVolume(volume), 23 fNode(NULL), 24 fName(NULL) 25 { 26 } 27 28 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 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 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 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 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 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 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 285 Index::InsertName(Transaction& transaction, const char* name, Inode* inode) 286 { 287 return UpdateName(transaction, NULL, name, inode); 288 } 289 290 291 status_t 292 Index::RemoveName(Transaction& transaction, const char* name, Inode* inode) 293 { 294 return UpdateName(transaction, name, NULL, inode); 295 } 296 297 298 status_t 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 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 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 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 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 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 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