1 /* 2 * Copyright 2001-2008, 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->FSVolume(), 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->FSVolume(), fNode->ID()); 49 fNode = NULL; 50 fName = NULL; 51 } 52 53 54 /*! Sets the index to specified one. Returns an error if the index could 55 not be found or initialized. 56 Note, Index::Update() may be called on the object even if this method 57 failed previously. In this case, it will only update live queries for 58 the updated attribute. 59 */ 60 status_t 61 Index::SetTo(const char* name) 62 { 63 // remove the old node, if the index is set for the second time 64 Unset(); 65 66 fName = name; 67 // only stores the pointer, so it assumes that it will stay constant 68 // in further comparisons (currently only used in Index::Update()) 69 70 // Note, the name is saved even if the index couldn't be initialized! 71 // This is used to optimize Index::Update() in case there is no index 72 73 Inode* indices = fVolume->IndicesNode(); 74 if (indices == NULL) 75 return B_ENTRY_NOT_FOUND; 76 77 InodeReadLocker locker(indices); 78 79 BPlusTree* tree; 80 if (indices->GetTree(&tree) != B_OK) 81 return B_BAD_VALUE; 82 83 ino_t id; 84 status_t status = tree->Find((uint8*)name, (uint16)strlen(name), &id); 85 if (status != B_OK) 86 return status; 87 88 Vnode vnode(fVolume, id); 89 if (vnode.Get(&fNode) != B_OK) 90 return B_ENTRY_NOT_FOUND; 91 92 if (fNode == NULL) { 93 FATAL(("fatal error at Index::InitCheck(), get_vnode() returned " 94 "NULL pointer\n")); 95 return B_ERROR; 96 } 97 98 vnode.Keep(); 99 return B_OK; 100 } 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 /*! 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 Node()->WriteLockInTransaction(transaction); 272 273 if (oldKey != NULL) { 274 status = tree->Remove(transaction, (const uint8*)oldKey, oldLength, 275 inode->ID()); 276 if (status == B_ENTRY_NOT_FOUND) { 277 // That's not nice, but no reason to let the whole thing fail 278 INFORM(("Could not find value in index \"%s\"!\n", name)); 279 } else if (status < B_OK) 280 return status; 281 } 282 283 // add the new key to the tree 284 285 if (newKey != NULL) { 286 status = tree->Insert(transaction, (const uint8*)newKey, newLength, 287 inode->ID()); 288 } 289 290 RETURN_ERROR(status); 291 } 292 293 294 status_t 295 Index::InsertName(Transaction &transaction, const char* name, Inode* inode) 296 { 297 return UpdateName(transaction, NULL, name, inode); 298 } 299 300 301 status_t 302 Index::RemoveName(Transaction &transaction, const char* name, Inode* inode) 303 { 304 return UpdateName(transaction, name, NULL, inode); 305 } 306 307 308 status_t 309 Index::UpdateName(Transaction &transaction, const char* oldName, 310 const char* newName, Inode* inode) 311 { 312 ASSERT(inode->IsRegularNode()); 313 314 uint16 oldLength = oldName != NULL ? strlen(oldName) : 0; 315 uint16 newLength = newName != NULL ? strlen(newName) : 0; 316 return Update(transaction, "name", B_STRING_TYPE, (uint8*)oldName, 317 oldLength, (uint8*)newName, newLength, inode); 318 } 319 320 321 status_t 322 Index::InsertSize(Transaction &transaction, Inode* inode) 323 { 324 ASSERT(inode->InSizeIndex()); 325 326 off_t size = inode->Size(); 327 return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8*)&size, 328 sizeof(int64), inode); 329 } 330 331 332 status_t 333 Index::RemoveSize(Transaction &transaction, Inode* inode) 334 { 335 ASSERT(inode->InSizeIndex()); 336 337 // Inode::OldSize() is the size that's in the index 338 off_t size = inode->OldSize(); 339 return Update(transaction, "size", B_INT64_TYPE, (uint8*)&size, 340 sizeof(int64), NULL, 0, inode); 341 } 342 343 344 status_t 345 Index::UpdateSize(Transaction &transaction, Inode* inode) 346 { 347 ASSERT(inode->InSizeIndex()); 348 349 off_t oldSize = inode->OldSize(); 350 off_t newSize = inode->Size(); 351 352 status_t status = Update(transaction, "size", B_INT64_TYPE, 353 (uint8*)&oldSize, sizeof(int64), (uint8*)&newSize, sizeof(int64), 354 inode); 355 if (status == B_OK) 356 inode->UpdateOldSize(); 357 358 return status; 359 } 360 361 362 status_t 363 Index::InsertLastModified(Transaction &transaction, Inode* inode) 364 { 365 ASSERT(inode->InLastModifiedIndex()); 366 367 off_t modified = inode->LastModified(); 368 return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0, 369 (uint8*)&modified, sizeof(int64), inode); 370 } 371 372 373 status_t 374 Index::RemoveLastModified(Transaction &transaction, Inode* inode) 375 { 376 ASSERT(inode->InLastModifiedIndex()); 377 378 // Inode::OldLastModified() is the value which is in the index 379 off_t modified = inode->OldLastModified(); 380 return Update(transaction, "last_modified", B_INT64_TYPE, 381 (uint8*)&modified, sizeof(int64), NULL, 0, inode); 382 } 383 384 385 status_t 386 Index::UpdateLastModified(Transaction &transaction, Inode* inode, 387 bigtime_t modified) 388 { 389 ASSERT(inode->InLastModifiedIndex()); 390 391 bigtime_t oldModified = inode->OldLastModified(); 392 if (modified == -1) 393 modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT; 394 modified &= ~INODE_TIME_MASK; 395 modified |= fVolume->GetUniqueID() & INODE_TIME_MASK; 396 397 status_t status = Update(transaction, "last_modified", B_INT64_TYPE, 398 (uint8*)&oldModified, sizeof(int64), (uint8*)&modified, 399 sizeof(int64), inode); 400 401 inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified); 402 if (status == B_OK) 403 inode->UpdateOldLastModified(); 404 405 return status; 406 } 407 408