/* * Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de. * Copyright 2010, Clemens Zeidler * Copyright 2024, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #include "Query.h" #include "BPlusTree.h" #include "bfs.h" #include "Debug.h" #include "Index.h" #include "Inode.h" #include "Volume.h" #include #include // #pragma mark - QueryPolicy struct Query::QueryPolicy { typedef Query Context; typedef ::Inode Entry; typedef ::Inode Node; struct Index : ::Index { bool isSpecialTime; Index(Context* context) : ::Index(context->fVolume), isSpecialTime(false) { } }; struct IndexIterator : TreeIterator { off_t offset; bool isSpecialTime; IndexIterator(BPlusTree* tree) : TreeIterator(tree), offset(0), isSpecialTime(false) { } }; struct NodeHolder { Vnode vnode; NodeGetter nodeGetter; RecursiveLocker smallDataLocker; }; static const int32 kMaxFileNameLength = INODE_FILE_NAME_LENGTH; // Entry interface static ino_t EntryGetParentID(Inode* inode) { return inode->ParentID(); } static Node* EntryGetNode(Entry* entry) { return entry; } static ino_t EntryGetNodeID(Entry* entry) { return entry->ID(); } static ssize_t EntryGetName(Inode* inode, void* buffer, size_t bufferSize) { status_t status = inode->GetName((char*)buffer, bufferSize); if (status != B_OK) return status; return strlen((const char*)buffer) + 1; } static const char* EntryGetNameNoCopy(NodeHolder& holder, Inode* inode) { // we need to lock before accessing Inode::Name() status_t status = holder.nodeGetter.SetTo(inode); if (status != B_OK) return NULL; holder.smallDataLocker.SetTo(inode->SmallDataLock(), false); uint8* buffer = (uint8*)inode->Name(holder.nodeGetter.Node()); if (buffer == NULL) { holder.smallDataLocker.Unlock(); return NULL; } return (const char*)buffer; } // Index interface static status_t IndexSetTo(Index& index, const char* attribute) { status_t status = index.SetTo(attribute); if (status == B_OK) { // The special time flag is set if the time values are shifted // 64-bit values to reduce the number of duplicates. // We have to be able to compare them against unshifted values // later. The only index which needs this is the last_modified // index, but we may want to open that feature for other indices, // too one day. index.isSpecialTime = (strcmp(attribute, "last_modified") == 0); } return status; } static void IndexUnset(Index& index) { index.Unset(); } static int32 IndexGetSize(Index& index) { off_t size = index.Node()->Size() / index.Node()->GetVolume()->BlockSize(); if (size > INT32_MAX) return INT32_MAX; return size; } static type_code IndexGetType(Index& index) { return index.Type(); } static int32 IndexGetKeySize(Index& index) { return index.KeySize(); } static IndexIterator* IndexCreateIterator(Index& index) { IndexIterator* iterator = new(std::nothrow) IndexIterator(index.Node()->Tree()); if (iterator == NULL) return NULL; iterator->isSpecialTime = index.isSpecialTime; return iterator; } // IndexIterator interface static void IndexIteratorDelete(IndexIterator* iterator) { delete iterator; } static status_t IndexIteratorFind(IndexIterator* iterator, const void* value, size_t size) { int64 shiftedTime; if (iterator->isSpecialTime) { // int64 time index; convert value. shiftedTime = *(int64*)value << INODE_TIME_SHIFT; value = &shiftedTime; } return iterator->Find((const uint8*)value, size); } static status_t IndexIteratorFetchNextEntry(IndexIterator* iterator, void* indexValue, size_t* _keyLength, size_t bufferSize, size_t* _duplicate) { uint16 keyLength; uint16 duplicate; status_t status = iterator->GetNextEntry((uint8*)indexValue, &keyLength, bufferSize, &iterator->offset, &duplicate); if (status != B_OK) return status; if (iterator->isSpecialTime) { // int64 time index; convert value. *(int64*)indexValue >>= INODE_TIME_SHIFT; } *_keyLength = keyLength; *_duplicate = duplicate; return B_OK; } static status_t IndexIteratorGetEntry(Context* context, IndexIterator* iterator, NodeHolder& holder, Inode** _entry) { holder.vnode.SetTo(context->fVolume, iterator->offset); Inode* inode; status_t status = holder.vnode.Get(&inode); if (status != B_OK) { REPORT_ERROR(status); FATAL(("could not get inode %" B_PRIdOFF " in index!\n", iterator->offset)); return status; } *_entry = inode; return B_OK; } static void IndexIteratorSkipDuplicates(IndexIterator* iterator) { iterator->SkipDuplicates(); } static void IndexIteratorSuspend(IndexIterator* indexIterator) { // Nothing to do. } static void IndexIteratorResume(IndexIterator* indexIterator) { // Nothing to do. } // Node interface static const off_t NodeGetSize(Inode* inode) { return inode->Size(); } static time_t NodeGetLastModifiedTime(Inode* inode) { return bfs_inode::ToSecs(inode->Node().LastModifiedTime()); } static status_t NodeGetAttribute(NodeHolder& holder, Inode* inode, const char* attributeName, uint8*& buffer, size_t* size, int32* type) { status_t status = holder.nodeGetter.SetTo(inode); if (status != B_OK) return status; Inode* attribute; holder.smallDataLocker.SetTo(inode->SmallDataLock(), false); small_data* smallData = inode->FindSmallData(holder.nodeGetter.Node(), attributeName); if (smallData != NULL) { buffer = smallData->Data(); *type = smallData->type; *size = smallData->data_size; } else { // needed to unlock the small_data section as fast as possible holder.smallDataLocker.Unlock(); holder.nodeGetter.Unset(); if (inode->GetAttribute(attributeName, &attribute) == B_OK) { *type = attribute->Type(); if (*size > (size_t)attribute->Size()) *size = attribute->Size(); if (*size > MAX_INDEX_KEY_LENGTH) *size = MAX_INDEX_KEY_LENGTH; if (attribute->ReadAt(0, (uint8*)buffer, size) < B_OK) { inode->ReleaseAttribute(attribute); return B_IO_ERROR; } inode->ReleaseAttribute(attribute); } else return B_ENTRY_NOT_FOUND; } return B_OK; } static Entry* NodeGetFirstReferrer(Node* node) { return node; } static Entry* NodeGetNextReferrer(Node* node, Entry* entry) { return NULL; } // Volume interface static dev_t ContextGetVolumeID(Context* context) { return context->fVolume->ID(); } }; // #pragma mark - Query Query::Query(Volume* volume) : fVolume(volume), fImpl(NULL) { } Query::~Query() { if (fImpl != NULL) { if ((fImpl->Flags() & B_LIVE_QUERY) != 0) fVolume->RemoveQuery(this); delete fImpl; } } /*static*/ status_t Query::Create(Volume* volume, const char* queryString, uint32 flags, port_id port, uint32 token, Query*& _query) { Query* query = new(std::nothrow) Query(volume); if (query == NULL) return B_NO_MEMORY; status_t error = query->_Init(queryString, flags, port, token); if (error != B_OK) { delete query; return error; } _query = query; return B_OK; } status_t Query::Rewind() { return fImpl->Rewind(); } status_t Query::GetNextEntry(struct dirent* entry, size_t size) { return fImpl->GetNextEntry(entry, size); } void Query::LiveUpdate(Inode* inode, const char* attribute, int32 type, const void* oldKey, size_t oldLength, const void* newKey, size_t newLength) { fImpl->LiveUpdate(inode, inode, attribute, type, (const uint8*)oldKey, oldLength, (const uint8*)newKey, newLength); } void Query::LiveUpdateRenameMove(Inode* inode, ino_t oldDirectoryID, const char* oldName, size_t oldLength, ino_t newDirectoryID, const char* newName, size_t newLength) { fImpl->LiveUpdateRenameMove(inode, inode, oldDirectoryID, oldName, oldLength, newDirectoryID, newName, newLength); } status_t Query::_Init(const char* queryString, uint32 flags, port_id port, uint32 token) { status_t error = QueryImpl::Create(this, queryString, flags, port, token, fImpl); if (error != B_OK) return error; if ((fImpl->Flags() & B_LIVE_QUERY) != 0) fVolume->AddQuery(this); return B_OK; }