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