1 /* Index - index access functions 2 * 3 * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de. 4 * This file may be used under the terms of the MIT License. 5 */ 6 7 8 #include "Debug.h" 9 #include "Index.h" 10 #include "Volume.h" 11 #include "Inode.h" 12 #include "BPlusTree.h" 13 14 #include <util/kernel_cpp.h> 15 #include <TypeConstants.h> 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 put_vnode(fVolume->ID(), fNode->ID()); 38 } 39 40 41 void 42 Index::Unset() 43 { 44 if (fNode == NULL) 45 return; 46 47 put_vnode(fVolume->ID(), fNode->ID()); 48 fNode = NULL; 49 fName = NULL; 50 } 51 52 53 /** Sets the index to specified one. Returns an error if the index could 54 * not be found or initialized. 55 * Note, Index::Update() may be called on the object even if this method 56 * failed previously. In this case, it will only update live queries for 57 * the updated attribute. 58 */ 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 BPlusTree *tree; 78 if (indices->GetTree(&tree) != B_OK) 79 return B_BAD_VALUE; 80 81 vnode_id id; 82 status_t status = tree->Find((uint8 *)name, (uint16)strlen(name), &id); 83 if (status != B_OK) 84 return status; 85 86 Vnode vnode(fVolume, id); 87 if (vnode.Get(&fNode) != B_OK) 88 return B_ENTRY_NOT_FOUND; 89 90 if (fNode == NULL) { 91 FATAL(("fatal error at Index::InitCheck(), get_vnode() returned 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 105 uint32 106 Index::Type() 107 { 108 if (fNode == NULL) 109 return 0; 110 111 switch (fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX | 112 S_ULONG_LONG_INDEX | S_FLOAT_INDEX | 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 | S_LONG_LONG_INDEX | 140 S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX); 141 142 if (mode == S_STR_INDEX) 143 // string indices don't have a fixed key size 144 return 0; 145 146 switch (mode) { 147 case S_INT_INDEX: 148 case S_UINT_INDEX: 149 return sizeof(int32); 150 case S_LONG_LONG_INDEX: 151 case S_ULONG_LONG_INDEX: 152 return sizeof(int64); 153 case S_FLOAT_INDEX: 154 return sizeof(float); 155 case S_DOUBLE_INDEX: 156 return sizeof(double); 157 } 158 FATAL(("index has unknown type!\n")); 159 return 0; 160 } 161 162 163 status_t 164 Index::Create(Transaction &transaction, const char *name, uint32 type) 165 { 166 Unset(); 167 168 int32 mode = 0; 169 switch (type) { 170 case B_INT32_TYPE: 171 mode = S_INT_INDEX; 172 break; 173 case B_UINT32_TYPE: 174 mode = S_UINT_INDEX; 175 break; 176 case B_INT64_TYPE: 177 mode = S_LONG_LONG_INDEX; 178 break; 179 case B_UINT64_TYPE: 180 mode = S_ULONG_LONG_INDEX; 181 break; 182 case B_FLOAT_TYPE: 183 mode = S_FLOAT_INDEX; 184 break; 185 case B_DOUBLE_TYPE: 186 mode = S_DOUBLE_INDEX; 187 break; 188 case B_STRING_TYPE: 189 case B_MIME_STRING_TYPE: 190 // B_MIME_STRING_TYPE is the only supported non-standard type, but 191 // will be handled like a B_STRING_TYPE internally 192 mode = S_STR_INDEX; 193 break; 194 default: 195 return B_BAD_TYPE; 196 } 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, &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 219 status_t 220 Index::Update(Transaction &transaction, const char *name, int32 type, const uint8 *oldKey, 221 uint16 oldLength, const uint8 *newKey, 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 (type != 0 && !compareKeys(type, oldKey, oldLength, newKey, newLength)) 237 return B_OK; 238 239 // update all live queries about the change, if they have an index or not 240 if (type != 0) 241 fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength, newKey, newLength); 242 243 status_t status; 244 if (((name != fName || strcmp(name, fName)) && (status = SetTo(name)) < B_OK) 245 || fNode == NULL) 246 return B_BAD_INDEX; 247 248 // now that we have the type, check again for equality 249 if (type == 0 && !compareKeys(Type(), oldKey, oldLength, newKey, newLength)) 250 return B_OK; 251 252 // same for the live query update 253 if (type == 0) 254 fVolume->UpdateLiveQueries(inode, name, Type(), oldKey, oldLength, newKey, newLength); 255 256 BPlusTree *tree; 257 if ((status = Node()->GetTree(&tree)) < B_OK) 258 return status; 259 260 // remove the old key from the tree 261 262 if (oldKey != NULL) { 263 status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength, inode->ID()); 264 if (status == B_ENTRY_NOT_FOUND) { 265 // That's not nice, but should be 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, inode->ID()); 275 276 RETURN_ERROR(status); 277 } 278 279 280 status_t 281 Index::InsertName(Transaction &transaction, const char *name, Inode *inode) 282 { 283 return UpdateName(transaction, NULL, name, inode); 284 } 285 286 287 status_t 288 Index::RemoveName(Transaction &transaction, const char *name, Inode *inode) 289 { 290 return UpdateName(transaction, name, NULL, inode); 291 } 292 293 294 status_t 295 Index::UpdateName(Transaction &transaction, const char *oldName, 296 const char *newName, Inode *inode) 297 { 298 ASSERT(inode->IsRegularNode()); 299 300 uint16 oldLength = oldName ? strlen(oldName) : 0; 301 uint16 newLength = newName ? strlen(newName) : 0; 302 return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName, 303 oldLength, (uint8 *)newName, newLength, inode); 304 } 305 306 307 status_t 308 Index::InsertSize(Transaction &transaction, Inode *inode) 309 { 310 ASSERT(inode->IsFile()); 311 312 off_t size = inode->Size(); 313 return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size, sizeof(int64), inode); 314 } 315 316 317 status_t 318 Index::RemoveSize(Transaction &transaction, Inode *inode) 319 { 320 ASSERT(inode->IsFile()); 321 322 // Inode::OldSize() is the size that's in the index 323 off_t size = inode->OldSize(); 324 return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size, sizeof(int64), NULL, 0, inode); 325 } 326 327 328 status_t 329 Index::UpdateSize(Transaction &transaction, Inode *inode) 330 { 331 ASSERT(inode->IsFile()); 332 333 off_t oldSize = inode->OldSize(); 334 off_t newSize = inode->Size(); 335 336 status_t status = Update(transaction, "size", B_INT64_TYPE, (uint8 *)&oldSize, 337 sizeof(int64), (uint8 *)&newSize, sizeof(int64), inode); 338 if (status == B_OK) 339 inode->UpdateOldSize(); 340 341 return status; 342 } 343 344 345 status_t 346 Index::InsertLastModified(Transaction &transaction, Inode *inode) 347 { 348 ASSERT(inode->IsFile() || inode->IsSymLink()); 349 350 off_t modified = inode->LastModified(); 351 return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0, 352 (uint8 *)&modified, sizeof(int64), inode); 353 } 354 355 356 status_t 357 Index::RemoveLastModified(Transaction &transaction, Inode *inode) 358 { 359 ASSERT(inode->IsFile() || inode->IsSymLink()); 360 361 // Inode::OldLastModified() is the value which is in the index 362 off_t modified = inode->OldLastModified(); 363 return Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&modified, 364 sizeof(int64), NULL, 0, inode); 365 } 366 367 368 status_t 369 Index::UpdateLastModified(Transaction &transaction, Inode *inode, off_t modified) 370 { 371 ASSERT(inode->IsFile() || inode->IsSymLink()); 372 373 off_t oldModified = inode->OldLastModified(); 374 if (modified == -1) 375 modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT; 376 modified |= fVolume->GetUniqueID() & INODE_TIME_MASK; 377 378 status_t status = Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&oldModified, 379 sizeof(int64), (uint8 *)&modified, sizeof(int64), inode); 380 381 inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified); 382 if (status == B_OK) 383 inode->UpdateOldLastModified(); 384 385 return status; 386 } 387 388