1 /* 2 * Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de> 4 * Copyright 2024, Haiku, Inc. All rights reserved. 5 * Distributed under the terms of the MIT License. 6 */ 7 8 #include "Query.h" 9 10 #include "BPlusTree.h" 11 #include "bfs.h" 12 #include "Debug.h" 13 #include "Index.h" 14 #include "Inode.h" 15 #include "Volume.h" 16 17 #include <query_private.h> 18 #include <file_systems/QueryParser.h> 19 20 21 // #pragma mark - QueryPolicy 22 23 24 struct Query::QueryPolicy { 25 typedef Query Context; 26 typedef ::Inode Entry; 27 typedef ::Inode Node; 28 29 struct Index : ::Index { 30 Index(Context* context) 31 : 32 ::Index(context->fVolume) 33 { 34 } 35 }; 36 37 struct IndexIterator : TreeIterator { 38 off_t offset; 39 40 IndexIterator(BPlusTree* tree) 41 : 42 TreeIterator(tree) 43 { 44 } 45 }; 46 47 struct NodeHolder { 48 Vnode vnode; 49 NodeGetter nodeGetter; 50 RecursiveLocker smallDataLocker; 51 }; 52 53 static const int32 kMaxFileNameLength = INODE_FILE_NAME_LENGTH; 54 55 // Entry interface 56 57 static ino_t EntryGetParentID(Inode* inode) 58 { 59 return inode->ParentID(); 60 } 61 62 static Node* EntryGetNode(Entry* entry) 63 { 64 return entry; 65 } 66 67 static ino_t EntryGetNodeID(Entry* entry) 68 { 69 return entry->ID(); 70 } 71 72 static ssize_t EntryGetName(Inode* inode, void* buffer, size_t bufferSize) 73 { 74 RecursiveLocker locker(&inode->SmallDataLock()); 75 status_t status = inode->GetName((char*)buffer, bufferSize); 76 if (status != B_OK) 77 return status; 78 return strlen((const char*)buffer) + 1; 79 } 80 81 static const char* EntryGetNameNoCopy(NodeHolder& holder, Inode* inode) 82 { 83 // we need to lock before accessing Inode::Name() 84 status_t status = holder.nodeGetter.SetTo(inode); 85 if (status != B_OK) 86 return NULL; 87 88 holder.smallDataLocker.SetTo(inode->SmallDataLock(), false); 89 uint8* buffer = (uint8*)inode->Name(holder.nodeGetter.Node()); 90 if (buffer == NULL) { 91 holder.smallDataLocker.Unlock(); 92 return NULL; 93 } 94 95 return (const char*)buffer; 96 } 97 98 // Index interface 99 100 static status_t IndexSetTo(Index& index, const char* attribute) 101 { 102 return index.SetTo(attribute); 103 } 104 105 static void IndexUnset(Index& index) 106 { 107 index.Unset(); 108 } 109 110 static int32 IndexGetWeightedScore(Index& index, int32 score) 111 { 112 // take index size into account (1024 is the current node size 113 // in our B+trees) 114 // 2048 * 2048 == 4194304 is the maximum score (for an empty 115 // tree, since the header + 1 node are already 2048 bytes) 116 return score * ((2048 * 1024LL) / index.Node()->Size()); 117 } 118 119 static type_code IndexGetType(Index& index) 120 { 121 return index.Type(); 122 } 123 124 static int32 IndexGetKeySize(Index& index) 125 { 126 return index.KeySize(); 127 } 128 129 static IndexIterator* IndexCreateIterator(Index& index) 130 { 131 IndexIterator* iterator = new(std::nothrow) IndexIterator(index.Node()->Tree()); 132 if (iterator == NULL) 133 return NULL; 134 135 return iterator; 136 } 137 138 // IndexIterator interface 139 140 static void IndexIteratorDelete(IndexIterator* iterator) 141 { 142 delete iterator; 143 } 144 145 static status_t IndexIteratorFind(IndexIterator* iterator, 146 const void* value, size_t size) 147 { 148 return iterator->Find((const uint8*)value, size); 149 } 150 151 static status_t IndexIteratorFetchNextEntry(IndexIterator* iterator, 152 void* indexValue, size_t* _keyLength, size_t bufferSize, size_t* _duplicate) 153 { 154 uint16 keyLength; 155 uint16 duplicate; 156 status_t status = iterator->GetNextEntry((uint8*)indexValue, &keyLength, 157 bufferSize, &iterator->offset, &duplicate); 158 if (status != B_OK) 159 return status; 160 161 *_keyLength = keyLength; 162 *_duplicate = duplicate; 163 return B_OK; 164 } 165 166 static status_t IndexIteratorGetEntry(Context* context, IndexIterator* iterator, 167 NodeHolder& holder, Inode** _entry) 168 { 169 holder.vnode.SetTo(context->fVolume, iterator->offset); 170 Inode* inode; 171 status_t status = holder.vnode.Get(&inode); 172 if (status != B_OK) { 173 REPORT_ERROR(status); 174 FATAL(("could not get inode %" B_PRIdOFF " in index!\n", iterator->offset)); 175 return status; 176 } 177 178 *_entry = inode; 179 return B_OK; 180 } 181 182 static void IndexIteratorSkipDuplicates(IndexIterator* iterator) 183 { 184 iterator->SkipDuplicates(); 185 } 186 187 static void IndexIteratorSuspend(IndexIterator* indexIterator) 188 { 189 // Nothing to do. 190 } 191 192 static void IndexIteratorResume(IndexIterator* indexIterator) 193 { 194 // Nothing to do. 195 } 196 197 // Node interface 198 199 static const off_t NodeGetSize(Inode* inode) 200 { 201 return inode->Size(); 202 } 203 204 static time_t NodeGetLastModifiedTime(Inode* inode) 205 { 206 return inode->Node().LastModifiedTime(); 207 } 208 209 static status_t NodeGetAttribute(NodeHolder& holder, Inode* inode, 210 const char* attributeName, uint8*& buffer, size_t* size, int32* type) 211 { 212 status_t status = holder.nodeGetter.SetTo(inode); 213 if (status != B_OK) 214 return status; 215 216 Inode* attribute; 217 218 holder.smallDataLocker.SetTo(inode->SmallDataLock(), false); 219 small_data* smallData = inode->FindSmallData(holder.nodeGetter.Node(), 220 attributeName); 221 if (smallData != NULL) { 222 buffer = smallData->Data(); 223 *type = smallData->type; 224 *size = smallData->data_size; 225 } else { 226 // needed to unlock the small_data section as fast as possible 227 holder.smallDataLocker.Unlock(); 228 holder.nodeGetter.Unset(); 229 230 if (inode->GetAttribute(attributeName, &attribute) == B_OK) { 231 *type = attribute->Type(); 232 if (*size > (size_t)attribute->Size()) 233 *size = attribute->Size(); 234 235 if (*size > MAX_INDEX_KEY_LENGTH) 236 *size = MAX_INDEX_KEY_LENGTH; 237 238 if (attribute->ReadAt(0, (uint8*)buffer, size) < B_OK) { 239 inode->ReleaseAttribute(attribute); 240 return B_IO_ERROR; 241 } 242 inode->ReleaseAttribute(attribute); 243 } else 244 return B_ENTRY_NOT_FOUND; 245 } 246 return B_OK; 247 } 248 249 static Entry* NodeGetFirstReferrer(Node* node) 250 { 251 return node; 252 } 253 254 static Entry* NodeGetNextReferrer(Node* node, Entry* entry) 255 { 256 return NULL; 257 } 258 259 // Volume interface 260 261 static dev_t ContextGetVolumeID(Context* context) 262 { 263 return context->fVolume->ID(); 264 } 265 }; 266 267 268 // #pragma mark - Query 269 270 271 Query::Query(Volume* volume) 272 : 273 fVolume(volume), 274 fImpl(NULL) 275 { 276 } 277 278 279 Query::~Query() 280 { 281 if (fImpl != NULL) { 282 if ((fImpl->Flags() & B_LIVE_QUERY) != 0) 283 fVolume->RemoveQuery(this); 284 285 delete fImpl; 286 } 287 } 288 289 290 /*static*/ status_t 291 Query::Create(Volume* volume, const char* queryString, uint32 flags, 292 port_id port, uint32 token, Query*& _query) 293 { 294 Query* query = new(std::nothrow) Query(volume); 295 if (query == NULL) 296 return B_NO_MEMORY; 297 298 status_t error = query->_Init(queryString, flags, port, token); 299 if (error != B_OK) { 300 delete query; 301 return error; 302 } 303 304 _query = query; 305 return B_OK; 306 } 307 308 309 status_t 310 Query::Rewind() 311 { 312 return fImpl->Rewind(); 313 } 314 315 316 status_t 317 Query::GetNextEntry(struct dirent* entry, size_t size) 318 { 319 return fImpl->GetNextEntry(entry, size); 320 } 321 322 323 void 324 Query::LiveUpdate(Inode* inode, const char* attribute, int32 type, 325 const void* oldKey, size_t oldLength, const void* newKey, size_t newLength) 326 { 327 fImpl->LiveUpdate(inode, inode, attribute, type, 328 (const uint8*)oldKey, oldLength, 329 (const uint8*)newKey, newLength); 330 } 331 332 333 void 334 Query::LiveUpdateRenameMove(Inode* inode, 335 ino_t oldDirectoryID, const char* oldName, size_t oldLength, 336 ino_t newDirectoryID, const char* newName, size_t newLength) 337 { 338 fImpl->LiveUpdateRenameMove(inode, inode, 339 oldDirectoryID, oldName, oldLength, 340 newDirectoryID, newName, newLength); 341 } 342 343 344 status_t 345 Query::_Init(const char* queryString, uint32 flags, port_id port, uint32 token) 346 { 347 status_t error = QueryImpl::Create(this, queryString, flags, port, token, 348 fImpl); 349 if (error != B_OK) 350 return error; 351 352 if ((fImpl->Flags() & B_LIVE_QUERY) != 0) 353 fVolume->AddQuery(this); 354 355 return B_OK; 356 } 357