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