1 /* 2 * Copyright 2001-2007, 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->ID(), 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->ID(), fNode->ID()); 49 fNode = NULL; 50 fName = NULL; 51 } 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 BPlusTree *tree; 79 if (indices->GetTree(&tree) != B_OK) 80 return B_BAD_VALUE; 81 82 ino_t id; 83 status_t status = tree->Find((uint8 *)name, (uint16)strlen(name), &id); 84 if (status != B_OK) 85 return status; 86 87 Vnode vnode(fVolume, id); 88 if (vnode.Get(&fNode) != B_OK) 89 return B_ENTRY_NOT_FOUND; 90 91 if (fNode == NULL) { 92 FATAL(("fatal error at Index::InitCheck(), get_vnode() returned " 93 "NULL pointer\n")); 94 return B_ERROR; 95 } 96 97 vnode.Keep(); 98 return B_OK; 99 } 100 101 102 /*! 103 Returns a standard type code for the stat() index type codes. Returns 104 zero if the type is not known (can only happen if the mode field is 105 corrupted somehow or not that of an index). 106 */ 107 uint32 108 Index::Type() 109 { 110 if (fNode == NULL) 111 return 0; 112 113 switch (fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX 114 | S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX 115 | S_DOUBLE_INDEX)) { 116 case S_INT_INDEX: 117 return B_INT32_TYPE; 118 case S_UINT_INDEX: 119 return B_UINT32_TYPE; 120 case S_LONG_LONG_INDEX: 121 return B_INT64_TYPE; 122 case S_ULONG_LONG_INDEX: 123 return B_UINT64_TYPE; 124 case S_FLOAT_INDEX: 125 return B_FLOAT_TYPE; 126 case S_DOUBLE_INDEX: 127 return B_DOUBLE_TYPE; 128 case S_STR_INDEX: 129 return B_STRING_TYPE; 130 } 131 FATAL(("index has unknown type!\n")); 132 return 0; 133 } 134 135 136 size_t 137 Index::KeySize() 138 { 139 if (fNode == NULL) 140 return 0; 141 142 int32 mode = fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX 143 | S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX 144 | S_DOUBLE_INDEX); 145 146 if (mode == S_STR_INDEX) 147 // string indices don't have a fixed key size 148 return 0; 149 150 switch (mode) { 151 case S_INT_INDEX: 152 case S_UINT_INDEX: 153 return sizeof(int32); 154 case S_LONG_LONG_INDEX: 155 case S_ULONG_LONG_INDEX: 156 return sizeof(int64); 157 case S_FLOAT_INDEX: 158 return sizeof(float); 159 case S_DOUBLE_INDEX: 160 return sizeof(double); 161 } 162 FATAL(("index has unknown type!\n")); 163 return 0; 164 } 165 166 167 status_t 168 Index::Create(Transaction &transaction, const char *name, uint32 type) 169 { 170 Unset(); 171 172 int32 mode = 0; 173 switch (type) { 174 case B_INT32_TYPE: 175 mode = S_INT_INDEX; 176 break; 177 case B_UINT32_TYPE: 178 mode = S_UINT_INDEX; 179 break; 180 case B_INT64_TYPE: 181 mode = S_LONG_LONG_INDEX; 182 break; 183 case B_UINT64_TYPE: 184 mode = S_ULONG_LONG_INDEX; 185 break; 186 case B_FLOAT_TYPE: 187 mode = S_FLOAT_INDEX; 188 break; 189 case B_DOUBLE_TYPE: 190 mode = S_DOUBLE_INDEX; 191 break; 192 case B_STRING_TYPE: 193 case B_MIME_STRING_TYPE: 194 // B_MIME_STRING_TYPE is the only supported non-standard type, but 195 // will be handled like a B_STRING_TYPE internally 196 mode = S_STR_INDEX; 197 break; 198 default: 199 return B_BAD_TYPE; 200 } 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 if (oldKey != NULL) { 272 status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength, 273 inode->ID()); 274 if (status == B_ENTRY_NOT_FOUND) { 275 // That's not nice, but no reason to let the whole thing fail 276 INFORM(("Could not find value in index \"%s\"!\n", name)); 277 } else if (status < B_OK) 278 return status; 279 } 280 281 // add the new key to the tree 282 283 if (newKey != NULL) { 284 status = tree->Insert(transaction, (const uint8 *)newKey, newLength, 285 inode->ID()); 286 } 287 288 RETURN_ERROR(status); 289 } 290 291 292 status_t 293 Index::InsertName(Transaction &transaction, const char *name, Inode *inode) 294 { 295 return UpdateName(transaction, NULL, name, inode); 296 } 297 298 299 status_t 300 Index::RemoveName(Transaction &transaction, const char *name, Inode *inode) 301 { 302 return UpdateName(transaction, name, NULL, inode); 303 } 304 305 306 status_t 307 Index::UpdateName(Transaction &transaction, const char *oldName, 308 const char *newName, Inode *inode) 309 { 310 ASSERT(inode->IsRegularNode()); 311 312 uint16 oldLength = oldName != NULL ? strlen(oldName) : 0; 313 uint16 newLength = newName != NULL ? strlen(newName) : 0; 314 return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName, 315 oldLength, (uint8 *)newName, newLength, inode); 316 } 317 318 319 status_t 320 Index::InsertSize(Transaction &transaction, Inode *inode) 321 { 322 ASSERT(inode->IsFile()); 323 324 off_t size = inode->Size(); 325 return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size, 326 sizeof(int64), inode); 327 } 328 329 330 status_t 331 Index::RemoveSize(Transaction &transaction, Inode *inode) 332 { 333 ASSERT(inode->IsFile()); 334 335 // Inode::OldSize() is the size that's in the index 336 off_t size = inode->OldSize(); 337 return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size, 338 sizeof(int64), NULL, 0, inode); 339 } 340 341 342 status_t 343 Index::UpdateSize(Transaction &transaction, Inode *inode) 344 { 345 ASSERT(inode->IsFile()); 346 347 off_t oldSize = inode->OldSize(); 348 off_t newSize = inode->Size(); 349 350 status_t status = Update(transaction, "size", B_INT64_TYPE, 351 (uint8 *)&oldSize, sizeof(int64), (uint8 *)&newSize, sizeof(int64), 352 inode); 353 if (status == B_OK) 354 inode->UpdateOldSize(); 355 356 return status; 357 } 358 359 360 status_t 361 Index::InsertLastModified(Transaction &transaction, Inode *inode) 362 { 363 ASSERT(inode->IsFile() || inode->IsSymLink()); 364 365 off_t modified = inode->LastModified(); 366 return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0, 367 (uint8 *)&modified, sizeof(int64), inode); 368 } 369 370 371 status_t 372 Index::RemoveLastModified(Transaction &transaction, Inode *inode) 373 { 374 ASSERT(inode->IsFile() || inode->IsSymLink()); 375 376 // Inode::OldLastModified() is the value which is in the index 377 off_t modified = inode->OldLastModified(); 378 return Update(transaction, "last_modified", B_INT64_TYPE, 379 (uint8 *)&modified, sizeof(int64), NULL, 0, inode); 380 } 381 382 383 status_t 384 Index::UpdateLastModified(Transaction &transaction, Inode *inode, 385 off_t modified) 386 { 387 ASSERT(inode->IsFile() || inode->IsSymLink()); 388 389 off_t oldModified = inode->OldLastModified(); 390 if (modified == -1) 391 modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT; 392 modified |= fVolume->GetUniqueID() & INODE_TIME_MASK; 393 394 status_t status = Update(transaction, "last_modified", B_INT64_TYPE, 395 (uint8 *)&oldModified, sizeof(int64), (uint8 *)&modified, 396 sizeof(int64), inode); 397 398 inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified); 399 if (status == B_OK) 400 inode->UpdateOldLastModified(); 401 402 return status; 403 } 404 405