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 // B_MIME_STRING_TYPE is defined in storage/Mime.h, but we 18 // don't need the whole file here; the type can't change anyway 19 #ifndef _MIME_H 20 # define B_MIME_STRING_TYPE 'MIMS' 21 #endif 22 23 24 Index::Index(Volume* volume) 25 : 26 fVolume(volume), 27 fNode(NULL) 28 { 29 } 30 31 32 Index::~Index() 33 { 34 if (fNode == NULL) 35 return; 36 37 if (fVolume->ID() >= 0) 38 put_vnode(fVolume->FSVolume(), fNode->ID()); 39 } 40 41 42 void 43 Index::Unset() 44 { 45 if (fNode == NULL) 46 return; 47 48 if (fVolume->ID() >= 0) 49 put_vnode(fVolume->FSVolume(), fNode->ID()); 50 fNode = NULL; 51 fName = NULL; 52 } 53 54 55 /*! Sets the index to specified one. Returns an error if the index could 56 not be found or initialized. 57 Note, Index::Update() may be called on the object even if this method 58 failed previously. In this case, it will only update live queries for 59 the updated attribute. 60 */ 61 status_t 62 Index::SetTo(const char* name) 63 { 64 // remove the old node, if the index is set for the second time 65 Unset(); 66 67 fName = name; 68 // only stores the pointer, so it assumes that it will stay constant 69 // in further comparisons (currently only used in Index::Update()) 70 71 // Note, the name is saved even if the index couldn't be initialized! 72 // This is used to optimize Index::Update() in case there is no index 73 74 Inode* indices = fVolume->IndicesNode(); 75 if (indices == NULL) 76 return B_ENTRY_NOT_FOUND; 77 78 InodeReadLocker locker(indices); 79 80 BPlusTree* tree = indices->Tree(); 81 if (tree == NULL) 82 return B_BAD_VALUE; 83 84 ino_t id; 85 status_t status = tree->Find((uint8*)name, (uint16)strlen(name), &id); 86 if (status != B_OK) 87 return status; 88 89 Vnode vnode(fVolume, id); 90 if (vnode.Get(&fNode) != B_OK) 91 return B_ENTRY_NOT_FOUND; 92 93 if (fNode == NULL) { 94 FATAL(("fatal error at Index::InitCheck(), get_vnode() returned " 95 "NULL pointer\n")); 96 return B_ERROR; 97 } 98 99 vnode.Keep(); 100 return B_OK; 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 /*! Updates the specified index, the oldKey will be removed from, the newKey 217 inserted into the tree. 218 If the method returns B_BAD_INDEX, it means the index couldn't be found - 219 the most common reason will be that the index doesn't exist. 220 You may not want to let the whole transaction fail because of that. 221 */ 222 status_t 223 Index::Update(Transaction& transaction, const char* name, int32 type, 224 const uint8* oldKey, uint16 oldLength, const uint8* newKey, 225 uint16 newLength, Inode* inode) 226 { 227 if (name == NULL 228 || (oldKey == NULL && newKey == NULL) 229 || (oldKey != NULL && oldLength == 0) 230 || (newKey != NULL && newLength == 0)) 231 return B_BAD_VALUE; 232 233 // B_MIME_STRING_TYPE is the only supported non-standard type 234 if (type == B_MIME_STRING_TYPE) 235 type = B_STRING_TYPE; 236 237 // If the two keys are identical, don't do anything - only compare if the 238 // type has been set, until we have a real type code, we can't do much 239 // about the comparison here 240 if (!compareKeys(type, oldKey, oldLength, newKey, newLength)) 241 return B_OK; 242 243 // update all live queries about the change, if they have an index or not 244 fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength, 245 newKey, newLength); 246 247 if (((name != fName || strcmp(name, fName)) && SetTo(name) != B_OK) 248 || fNode == NULL) 249 return B_BAD_INDEX; 250 251 BPlusTree* tree = Node()->Tree(); 252 if (tree == NULL) 253 return B_BAD_VALUE; 254 255 // remove the old key from the tree 256 257 Node()->WriteLockInTransaction(transaction); 258 259 status_t status = B_OK; 260 261 if (oldKey != NULL) { 262 status = tree->Remove(transaction, (const uint8*)oldKey, oldLength, 263 inode->ID()); 264 if (status == B_ENTRY_NOT_FOUND) { 265 // That's not nice, but no reason to let the whole thing fail 266 INFORM(("Could not find value in index \"%s\"!\n", name)); 267 } else if (status != B_OK) 268 return status; 269 } 270 271 // add the new key to the tree 272 273 if (newKey != NULL) { 274 status = tree->Insert(transaction, (const uint8*)newKey, newLength, 275 inode->ID()); 276 } 277 278 RETURN_ERROR(status); 279 } 280 281 282 status_t 283 Index::InsertName(Transaction& transaction, const char* name, Inode* inode) 284 { 285 return UpdateName(transaction, NULL, name, inode); 286 } 287 288 289 status_t 290 Index::RemoveName(Transaction& transaction, const char* name, Inode* inode) 291 { 292 return UpdateName(transaction, name, NULL, inode); 293 } 294 295 296 status_t 297 Index::UpdateName(Transaction& transaction, const char* oldName, 298 const char* newName, Inode* inode) 299 { 300 ASSERT(inode->IsRegularNode()); 301 302 uint16 oldLength = oldName != NULL ? strlen(oldName) : 0; 303 uint16 newLength = newName != NULL ? strlen(newName) : 0; 304 return Update(transaction, "name", B_STRING_TYPE, (uint8*)oldName, 305 oldLength, (uint8*)newName, newLength, inode); 306 } 307 308 309 status_t 310 Index::InsertSize(Transaction& transaction, Inode* inode) 311 { 312 ASSERT(inode->InSizeIndex()); 313 314 off_t size = inode->Size(); 315 return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8*)&size, 316 sizeof(int64), inode); 317 } 318 319 320 status_t 321 Index::RemoveSize(Transaction& transaction, Inode* inode) 322 { 323 ASSERT(inode->InSizeIndex()); 324 325 // Inode::OldSize() is the size that's in the index 326 off_t size = inode->OldSize(); 327 return Update(transaction, "size", B_INT64_TYPE, (uint8*)&size, 328 sizeof(int64), NULL, 0, inode); 329 } 330 331 332 status_t 333 Index::UpdateSize(Transaction& transaction, Inode* inode) 334 { 335 ASSERT(inode->InSizeIndex()); 336 337 off_t oldSize = inode->OldSize(); 338 off_t newSize = inode->Size(); 339 340 status_t status = Update(transaction, "size", B_INT64_TYPE, 341 (uint8*)&oldSize, sizeof(int64), (uint8*)&newSize, sizeof(int64), 342 inode); 343 if (status == B_OK) 344 inode->UpdateOldSize(); 345 346 return status; 347 } 348 349 350 status_t 351 Index::InsertLastModified(Transaction& transaction, Inode* inode) 352 { 353 ASSERT(inode->InLastModifiedIndex()); 354 355 off_t modified = inode->LastModified(); 356 return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0, 357 (uint8*)&modified, sizeof(int64), inode); 358 } 359 360 361 status_t 362 Index::RemoveLastModified(Transaction& transaction, Inode* inode) 363 { 364 ASSERT(inode->InLastModifiedIndex()); 365 366 // Inode::OldLastModified() is the value which is in the index 367 off_t modified = inode->OldLastModified(); 368 return Update(transaction, "last_modified", B_INT64_TYPE, 369 (uint8*)&modified, sizeof(int64), NULL, 0, inode); 370 } 371 372 373 status_t 374 Index::UpdateLastModified(Transaction& transaction, Inode* inode, 375 bigtime_t modified) 376 { 377 ASSERT(inode->InLastModifiedIndex()); 378 379 bigtime_t oldModified = inode->OldLastModified(); 380 if (modified == -1) 381 modified = bfs_inode::ToInode(real_time_clock_usecs()); 382 383 status_t status = Update(transaction, "last_modified", B_INT64_TYPE, 384 (uint8*)&oldModified, sizeof(int64), (uint8*)&modified, 385 sizeof(int64), inode); 386 387 inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified); 388 if (status == B_OK) 389 inode->UpdateOldLastModified(); 390 391 return status; 392 } 393 394