diff --git a/src/add-ons/kernel/file_systems/ramfs/Jamfile b/src/add-ons/kernel/file_systems/ramfs/Jamfile index 446c9ba17a..e358f8ec30 100644 --- a/src/add-ons/kernel/file_systems/ramfs/Jamfile +++ b/src/add-ons/kernel/file_systems/ramfs/Jamfile @@ -1,7 +1,7 @@ SubDir HAIKU_TOP src add-ons kernel file_systems ramfs ; UsePrivateKernelHeaders ; -UsePrivateHeaders file_systems ; +UsePrivateHeaders file_systems storage ; DEFINES += DEBUG_APP="\\\"ramfs\\\"" ; @@ -13,7 +13,6 @@ KernelAddon ramfs AttributeIndexImpl.cpp AttributeIterator.cpp DataContainer.cpp - DebugSupport.cpp Directory.cpp Entry.cpp EntryIterator.cpp @@ -31,7 +30,10 @@ KernelAddon ramfs SizeIndex.cpp SymLink.cpp Volume.cpp + + DebugSupport.cpp + QueryParserUtils.cpp ; -SEARCH on [ FGristFiles DebugSupport.cpp ] +SEARCH on [ FGristFiles DebugSupport.cpp QueryParserUtils.cpp ] += [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems shared ] ; diff --git a/src/add-ons/kernel/file_systems/ramfs/Query.cpp b/src/add-ons/kernel/file_systems/ramfs/Query.cpp index 0b4e721904..41b62460be 100644 --- a/src/add-ons/kernel/file_systems/ramfs/Query.cpp +++ b/src/add-ons/kernel/file_systems/ramfs/Query.cpp @@ -1,133 +1,84 @@ -/* Query - query parsing and evaluation - * - * The pattern matching is roughly based on code originally written - * by J. Kercheval, and on code written by Kenneth Almquist, though - * it shares no code. - * - * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de. - * This file may be used under the terms of the MIT License. +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2023, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. */ -// Adjusted by Ingo Weinhold for usage in RAM FS. - - #include "Query.h" -#include "DebugSupport.h" + #include "Directory.h" #include "Entry.h" -#include "Misc.h" #include "Node.h" #include "Volume.h" #include "Index.h" -#include -#include -#include -#include - -#include -#include -#include +#include -// IndexWrapper - -// constructor -IndexWrapper::IndexWrapper(Volume *volume) - : fVolume(volume), - fIndex(NULL) -{ -} - -// SetTo -status_t -IndexWrapper::SetTo(const char *name) -{ - fIndex = NULL; - if (fVolume) - fIndex = fVolume->FindIndex(name); - return (fIndex ? B_OK : B_ENTRY_NOT_FOUND); -} - -// Unset -void -IndexWrapper::Unset() -{ - fIndex = NULL; -} - -// Type -uint32 -IndexWrapper::Type() const -{ - return (fIndex ? fIndex->GetType() : 0); -} - -// GetSize -off_t -IndexWrapper::GetSize() const -{ - // Compute a fake "index size" based on the number of entries - // (1024 + 16 * entry count), so we don't need to adjust the code using it. - return 1024LL + (fIndex ? fIndex->CountEntries() : 0) * 16LL; -} - -// KeySize -int32 -IndexWrapper::KeySize() const -{ - return (fIndex ? fIndex->GetKeyLength() : 0); -} +// #pragma mark - IndexIterator -// IndexIterator +class IndexIterator { +public: + IndexIterator(Index *index); -// constructor -IndexIterator::IndexIterator(IndexWrapper *indexWrapper) - : fIndexWrapper(indexWrapper), + status_t Find(const uint8 *const key, size_t keyLength); + status_t Rewind(); + status_t GetNextEntry(uint8 *buffer, size_t *keyLength, Entry **entry); + + Index* GetIndex() const { return fIndex; } + +private: + Index *fIndex; + IndexEntryIterator fIterator; + bool fInitialized; +}; + + +IndexIterator::IndexIterator(Index *index) + : fIndex(index), fIterator(), fInitialized(false) { } -// Find + status_t IndexIterator::Find(const uint8 *const key, size_t keyLength) { status_t error = B_ENTRY_NOT_FOUND; - if (fIndexWrapper && fIndexWrapper->fIndex) { + if (fIndex) { // TODO: We actually don't want an exact Find() here, but rather a // FindClose(). - fInitialized = fIndexWrapper->fIndex->Find(key, keyLength, &fIterator); + fInitialized = fIndex->Find(key, keyLength, &fIterator); if (fInitialized) error = B_OK; } return error; } -// Rewind + status_t IndexIterator::Rewind() { status_t error = B_ENTRY_NOT_FOUND; - if (fIndexWrapper && fIndexWrapper->fIndex) { - fInitialized = fIndexWrapper->fIndex->GetIterator(&fIterator); + if (fIndex) { + fInitialized = fIndex->GetIterator(&fIterator); if (fInitialized) error = B_OK; } return error; } -// GetNextEntry + status_t -IndexIterator::GetNextEntry(uint8 *buffer, uint16 *_keyLength, - size_t /*bufferSize*/, Entry **_entry) +IndexIterator::GetNextEntry(uint8 *buffer, size_t *_keyLength, Entry **_entry) { status_t error = B_ENTRY_NOT_FOUND; - if (fIndexWrapper && fIndexWrapper->fIndex) { + if (fIndex) { // init iterator, if not done yet if (!fInitialized) { - fIndexWrapper->fIndex->GetIterator(&fIterator); + fIndex->GetIterator(&fIterator); fInitialized = true; } @@ -146,1645 +97,258 @@ IndexIterator::GetNextEntry(uint8 *buffer, uint16 *_keyLength, } -// compare_integral -template -static inline -int -compare_integral(const Key &a, const Key &b) -{ - if (a < b) - return -1; - else if (a > b) - return 1; - return 0; -} +// #pragma mark - QueryPolicy -// compare_keys -static -int -compare_keys(const uint8 *key1, size_t length1, const uint8 *key2, - size_t length2, uint32 type) -{ - switch (type) { - case B_INT32_TYPE: - return compare_integral(*(int32*)key1, *(int32*)key2); - case B_UINT32_TYPE: - return compare_integral(*(uint32*)key1, *(uint32*)key2); - case B_INT64_TYPE: - return compare_integral(*(int64*)key1, *(int64*)key2); - case B_UINT64_TYPE: - return compare_integral(*(uint64*)key1, *(uint64*)key2); - case B_FLOAT_TYPE: - return compare_integral(*(float*)key1, *(float*)key2); - case B_DOUBLE_TYPE: - return compare_integral(*(double*)key1, *(double*)key2); - case B_STRING_TYPE: + +struct Query::QueryPolicy { + typedef Query Context; + typedef ::Entry Entry; + typedef ::Node Node; + + struct Index { + Query* query; + ::Index* index; + + Index(Context* context) + : + query(context) { - int result = strncmp((const char*)key1, (const char*)key2, - min(length1, length2)); - if (result == 0) { - result = compare_integral(strnlen((const char*)key1, length1), - strnlen((const char*)key2, length2)); - } - return result; } - } - return -1; -} + }; -// compareKeys -static inline -int -compareKeys(uint32 type, const uint8 *key1, size_t length1, const uint8 *key2, - size_t length2) -{ - return compare_keys(key1, length1, key2, length2, type); -} - - - - - -// The parser has a very static design, but it will do what is required. -// -// ParseOr(), ParseAnd(), ParseEquation() are guarantying the operator -// precedence, that is =,!=,>,<,>=,<= .. && .. ||. -// Apparently, the "!" (not) can only be used with brackets. -// -// If you think that there are too few NULL pointer checks in some places -// of the code, just read the beginning of the query constructor. -// The API is not fully available, just the Query and the Expression class -// are. - - -enum ops { - OP_NONE, - - OP_AND, - OP_OR, - - OP_EQUATION, - - OP_EQUAL, - OP_UNEQUAL, - OP_GREATER_THAN, - OP_LESS_THAN, - OP_GREATER_THAN_OR_EQUAL, - OP_LESS_THAN_OR_EQUAL, -}; - -enum match { - NO_MATCH = 0, - MATCH_OK = 1, - - MATCH_BAD_PATTERN = -2, - MATCH_INVALID_CHARACTER -}; - -// return values from isValidPattern() -enum { - PATTERN_INVALID_ESCAPE = -3, - PATTERN_INVALID_RANGE, - PATTERN_INVALID_SET -}; - -union value { - int64 Int64; - uint64 Uint64; - int32 Int32; - uint32 Uint32; - float Float; - double Double; - char CString[kMaxIndexKeyLength]; -}; - -// B_MIME_STRING_TYPE is defined in storage/Mime.h, but we -// don't need the whole file here; the type can't change anyway -#ifndef _MIME_H -# define B_MIME_STRING_TYPE 'MIMS' -#endif - -class Term { - public: - Term(int8 op) : fOp(op), fParent(NULL) {} - virtual ~Term() {} - - int8 Op() const { return fOp; } - - void SetParent(Term *parent) { fParent = parent; } - Term *Parent() const { return fParent; } - - virtual status_t Match(Entry *entry, Node* node, - const char *attribute = NULL, int32 type = 0, - const uint8 *key = NULL, size_t size = 0) = 0; - virtual void Complement() = 0; - - virtual void CalculateScore(IndexWrapper &index) = 0; - virtual int32 Score() const = 0; - - virtual status_t InitCheck() = 0; - - virtual bool NeedsEntry() = 0; - -#if DEBUG - virtual void PrintToStream() = 0; -#endif - - protected: - int8 fOp; - Term *fParent; -}; - -// Although an Equation object is quite independent from the volume on which -// the query is run, there are some dependencies that are produced while -// querying: -// The type/size of the value, the score, and if it has an index or not. -// So you could run more than one query on the same volume, but it might return -// wrong values when it runs concurrently on another volume. -// That's not an issue right now, because we run single-threaded and don't use -// queries more than once. - -class Equation : public Term { - public: - Equation(char **expr); - virtual ~Equation(); - - virtual status_t InitCheck(); - - status_t ParseQuotedString(char **_start, char **_end); - char *CopyString(char *start, char *end); - - virtual status_t Match(Entry *entry, Node* node, - const char *attribute = NULL, int32 type = 0, - const uint8 *key = NULL, size_t size = 0); - virtual void Complement(); - - status_t PrepareQuery(Volume *volume, IndexWrapper &index, IndexIterator **iterator, - bool queryNonIndexed); - status_t GetNextMatching(Volume *volume, IndexIterator *iterator, - struct dirent *dirent, size_t bufferSize); - - virtual void CalculateScore(IndexWrapper &index); - virtual int32 Score() const { return fScore; } - - virtual bool NeedsEntry(); - -#if DEBUG - virtual void PrintToStream(); -#endif - - private: - Equation(const Equation &); - Equation &operator=(const Equation &); - // no implementation - - status_t ConvertValue(type_code type); - bool CompareTo(const uint8 *value, uint16 size); - uint8 *Value() const { return (uint8 *)&fValue; } - status_t MatchEmptyString(); - - char *fAttribute; - char *fString; - union value fValue; - type_code fType; - size_t fSize; - bool fIsPattern; - - int32 fScore; - bool fHasIndex; -}; - -class Operator : public Term { - public: - Operator(Term *,int8,Term *); - virtual ~Operator(); - - Term *Left() const { return fLeft; } - Term *Right() const { return fRight; } - - virtual status_t Match(Entry *entry, Node* node, - const char *attribute = NULL, int32 type = 0, - const uint8 *key = NULL, size_t size = 0); - virtual void Complement(); - - virtual void CalculateScore(IndexWrapper &index); - virtual int32 Score() const; - - virtual status_t InitCheck(); - - virtual bool NeedsEntry(); - - //Term *Copy() const; -#if DEBUG - virtual void PrintToStream(); -#endif - - private: - Operator(const Operator &); - Operator &operator=(const Operator &); - // no implementation - - Term *fLeft,*fRight; -}; - - -//--------------------------------- - - -void -skipWhitespace(char **expr, int32 skip = 0) -{ - char *string = (*expr) + skip; - while (*string == ' ' || *string == '\t') string++; - *expr = string; -} - - -void -skipWhitespaceReverse(char **expr,char *stop) -{ - char *string = *expr; - while (string > stop && (*string == ' ' || *string == '\t')) string--; - *expr = string; -} - - -// #pragma mark - - - -uint32 -utf8ToUnicode(char **string) -{ - uint8 *bytes = (uint8 *)*string; - int32 length; - uint8 mask = 0x1f; - - switch (bytes[0] & 0xf0) { - case 0xc0: - case 0xd0: length = 2; break; - case 0xe0: length = 3; break; - case 0xf0: - mask = 0x0f; - length = 4; - break; - default: - // valid 1-byte character - // and invalid characters - (*string)++; - return bytes[0]; - } - uint32 c = bytes[0] & mask; - int32 i = 1; - for (;i < length && (bytes[i] & 0x80) > 0;i++) - c = (c << 6) | (bytes[i] & 0x3f); - - if (i < length) { - // invalid character - (*string)++; - return (uint32)bytes[0]; - } - *string += length; - return c; -} - - -int32 -getFirstPatternSymbol(char *string) -{ - char c; - - for (int32 index = 0;(c = *string++);index++) { - if (c == '*' || c == '?' || c == '[') - return index; - } - return -1; -} - - -bool -isPattern(char *string) -{ - return getFirstPatternSymbol(string) >= 0 ? true : false; -} - - -status_t -isValidPattern(char *pattern) -{ - while (*pattern) { - switch (*pattern++) { - case '\\': - // the escape character must not be at the end of the pattern - if (!*pattern++) - return PATTERN_INVALID_ESCAPE; - break; - - case '[': - if (pattern[0] == ']' || !pattern[0]) - return PATTERN_INVALID_SET; - - while (*pattern != ']') { - if (*pattern == '\\' && !*++pattern) - return PATTERN_INVALID_ESCAPE; - - if (!*pattern) - return PATTERN_INVALID_SET; - - if (pattern[0] == '-' && pattern[1] == '-') - return PATTERN_INVALID_RANGE; - - pattern++; - } - break; + struct IndexIterator : ::IndexIterator { + IndexIterator(::Index* index) + : + ::IndexIterator(index) + { } - } - return B_OK; -} + }; + static const int32 kMaxFileNameLength = B_FILE_NAME_LENGTH; -/** Matches the string against the given wildcard pattern. - * Returns either MATCH_OK, or NO_MATCH when everything went fine, - * or values < 0 (see enum at the top of Query.cpp) if an error - * occurs - */ + // Entry interface -status_t -matchString(char *pattern, char *string) -{ - while (*pattern) { - // end of string == valid end of pattern? - if (!string[0]) { - while (pattern[0] == '*') - pattern++; - return !pattern[0] ? MATCH_OK : NO_MATCH; - } - - switch (*pattern++) { - case '?': - { - // match exactly one UTF-8 character; we are - // not interested in the result - utf8ToUnicode(&string); - break; - } - - case '*': - { - // compact pattern - while (true) { - if (pattern[0] == '?') { - if (!*++string) - return NO_MATCH; - } else if (pattern[0] != '*') - break; - - pattern++; - } - - // if the pattern is done, we have matched the string - if (!pattern[0]) - return MATCH_OK; - - while(true) { - // we have removed all occurences of '*' and '?' - if (pattern[0] == string[0] - || pattern[0] == '[' - || pattern[0] == '\\') { - status_t status = matchString(pattern,string); - if (status < B_OK || status == MATCH_OK) - return status; - } - - // we could be nice here and just jump to the next - // UTF-8 character - but we wouldn't gain that much - // and it'd be slower (since we're checking for - // equality before entering the recursion) - if (!*++string) - return NO_MATCH; - } - break; - } - - case '[': - { - bool invert = false; - if (pattern[0] == '^' || pattern[0] == '!') { - invert = true; - pattern++; - } - - if (!pattern[0] || pattern[0] == ']') - return MATCH_BAD_PATTERN; - - uint32 c = utf8ToUnicode(&string); - bool matched = false; - - while (pattern[0] != ']') { - if (!pattern[0]) - return MATCH_BAD_PATTERN; - - if (pattern[0] == '\\') - pattern++; - - uint32 first = utf8ToUnicode(&pattern); - - // Does this character match, or is this a range? - if (first == c) { - matched = true; - break; - } else if (pattern[0] == '-' && pattern[1] != ']' && pattern[1]) { - pattern++; - - if (pattern[0] == '\\') { - pattern++; - if (!pattern[0]) - return MATCH_BAD_PATTERN; - } - uint32 last = utf8ToUnicode(&pattern); - - if (c >= first && c <= last) { - matched = true; - break; - } - } - } - - if (invert) - matched = !matched; - - if (matched) { - while (pattern[0] != ']') { - if (!pattern[0]) - return MATCH_BAD_PATTERN; - pattern++; - } - pattern++; - break; - } - return NO_MATCH; - } - - case '\\': - if (!pattern[0]) - return MATCH_BAD_PATTERN; - // supposed to fall through - default: - if (pattern[-1] != string[0]) - return NO_MATCH; - string++; - break; - } + static ino_t EntryGetParentID(Entry* entry) + { + return entry->GetParent()->GetID(); } - if (string[0]) - return NO_MATCH; - - return MATCH_OK; -} - - -// #pragma mark - - - -Equation::Equation(char **expr) - : Term(OP_EQUATION), - fAttribute(NULL), - fString(NULL), - fType(0), - fIsPattern(false) -{ - char *string = *expr; - char *start = string; - char *end = NULL; - - // Since the equation is the integral part of any query, we're just parsing - // the whole thing here. - // The whitespace at the start is already removed in Expression::ParseEquation() - - if (*start == '"' || *start == '\'') { - // string is quoted (start has to be on the beginning of a string) - if (ParseQuotedString(&start, &end) < B_OK) - return; - - // set string to a valid start of the equation symbol - string = end + 2; - skipWhitespace(&string); - if (*string != '=' && *string != '<' && *string != '>' && *string != '!') { - *expr = string; - return; - } - } else { - // search the (in)equation for the actual equation symbol (and for other operators - // in case the equation is malformed) - while (*string && *string != '=' && *string != '<' && *string != '>' && *string != '!' - && *string != '&' && *string != '|') - string++; - - // get the attribute string (and trim whitespace), in case - // the string was not quoted - end = string - 1; - skipWhitespaceReverse(&end, start); + static Node* EntryGetNode(Entry* entry) + { + return entry->GetNode(); } - // attribute string is empty (which is not allowed) - if (start > end) - return; - - // at this point, "start" points to the beginning of the string, "end" points - // to the last character of the string, and "string" points to the first - // character of the equation symbol - - // test for the right symbol (as this doesn't need any memory) - switch (*string) { - case '=': - fOp = OP_EQUAL; - break; - case '>': - fOp = *(string + 1) == '=' ? OP_GREATER_THAN_OR_EQUAL : OP_GREATER_THAN; - break; - case '<': - fOp = *(string + 1) == '=' ? OP_LESS_THAN_OR_EQUAL : OP_LESS_THAN; - break; - case '!': - if (*(string + 1) != '=') - return; - fOp = OP_UNEQUAL; - break; - - // any invalid characters will be rejected - default: - *expr = string; - return; - } - // lets change "start" to point to the first character after the symbol - if (*(string + 1) == '=') - string++; - string++; - skipWhitespace(&string); - - // allocate & copy the attribute string - - fAttribute = CopyString(start, end); - if (fAttribute == NULL) - return; - - start = string; - if (*start == '"' || *start == '\'') { - // string is quoted (start has to be on the beginning of a string) - if (ParseQuotedString(&start, &end) < B_OK) - return; - - string = end + 2; - skipWhitespace(&string); - } else { - while (*string && *string != '&' && *string != '|' && *string != ')') - string++; - - end = string - 1; - skipWhitespaceReverse(&end, start); + static ino_t EntryGetNodeID(Entry* entry) + { + return entry->GetNode()->GetID(); } - // at this point, "start" will point to the first character of the value, - // "end" will point to its last character, and "start" to the first non- - // whitespace character after the value string + static ssize_t EntryGetName(Entry* entry, void* buffer, size_t bufferSize) + { + const char* name = entry->GetName(); + size_t nameLength = strlen(name); + if (nameLength >= bufferSize) + return B_BUFFER_OVERFLOW; - fString = CopyString(start, end); - if (fString == NULL) - return; - - // patterns are only allowed for these operations (and strings) - if (fOp == OP_EQUAL || fOp == OP_UNEQUAL) { - fIsPattern = isPattern(fString); - if (fIsPattern && isValidPattern(fString) < B_OK) { - // we only want to have valid patterns; setting fString - // to NULL will cause InitCheck() to fail - free(fString); - fString = NULL; - } + memcpy(buffer, name, nameLength + 1); + return nameLength + 1; } - *expr = string; -} - - -Equation::~Equation() -{ - if (fAttribute != NULL) - free(fAttribute); - if (fString != NULL) - free(fString); -} - - -status_t -Equation::InitCheck() -{ - if (fAttribute == NULL - || fString == NULL - || fOp == OP_NONE) - return B_BAD_VALUE; - - return B_OK; -} - - -status_t -Equation::ParseQuotedString(char **_start, char **_end) -{ - char *start = *_start; - char quote = *start++; - char *end = start; - - for (;*end && *end != quote;end++) { - if (*end == '\\') - end++; - } - if (*end == '\0') - return B_BAD_VALUE; - - *_start = start; - *_end = end - 1; - - return B_OK; -} - - -char * -Equation::CopyString(char *start, char *end) -{ - // end points to the last character of the string - and the length - // also has to include the null-termination - int32 length = end + 2 - start; - // just to make sure; since that's the max. attribute name length and - // the max. string in an index, it make sense to have it that way - if (length > (int32)kMaxIndexKeyLength || length <= 0) - return NULL; - - char *copy = (char *)malloc(length); - if (copy == NULL) - return NULL; - - memcpy(copy,start,length - 1); - copy[length - 1] = '\0'; - - return copy; -} - - -status_t -Equation::ConvertValue(type_code type) -{ - // Has the type already been converted? - if (type == fType) - return B_OK; - - char *string = fString; - - switch (type) { - case B_MIME_STRING_TYPE: - type = B_STRING_TYPE; - // supposed to fall through - case B_STRING_TYPE: - strncpy(fValue.CString, string, kMaxIndexKeyLength); - fValue.CString[kMaxIndexKeyLength - 1] = '\0'; - fSize = strlen(fValue.CString); - break; - case B_INT32_TYPE: - fValue.Int32 = strtol(string, &string, 0); - fSize = sizeof(int32); - break; - case B_UINT32_TYPE: - fValue.Int32 = strtoul(string, &string, 0); - fSize = sizeof(uint32); - break; - case B_INT64_TYPE: - fValue.Int64 = strtoll(string, &string, 0); - fSize = sizeof(int64); - break; - case B_UINT64_TYPE: - fValue.Uint64 = strtoull(string, &string, 0); - fSize = sizeof(uint64); - break; - case B_FLOAT_TYPE: - fValue.Float = strtod(string, &string); - fSize = sizeof(float); - break; - case B_DOUBLE_TYPE: - fValue.Double = strtod(string, &string); - fSize = sizeof(double); - break; - default: - FATAL("query value conversion to 0x%" B_PRIx32 " requested!\n", type); - // should we fail here or just do a safety int32 conversion? - return B_ERROR; + static const char* EntryGetNameNoCopy(Entry* entry, void* buffer, + size_t bufferSize) + { + return entry->GetName(); } - fType = type; + // Index interface - // patterns are only allowed for string types - if (fType != B_STRING_TYPE && fIsPattern) - fIsPattern = false; - - return B_OK; -} - - -/** Returns true when the key matches the equation. You have to - * call ConvertValue() before this one. - */ - -bool -Equation::CompareTo(const uint8 *value, uint16 size) -{ - int32 compare; - - // fIsPattern is only true if it's a string type, and fOp OP_EQUAL, or OP_UNEQUAL - if (fIsPattern) { - // we have already validated the pattern, so we don't check for failing - // here - if something is broken, and matchString() returns an error, - // we just don't match - compare = matchString(fValue.CString, (char *)value) == MATCH_OK ? 0 : 1; - } else - compare = compareKeys(fType, value, size, Value(), fSize); - - switch (fOp) { - case OP_EQUAL: - return compare == 0; - case OP_UNEQUAL: - return compare != 0; - case OP_LESS_THAN: - return compare < 0; - case OP_LESS_THAN_OR_EQUAL: - return compare <= 0; - case OP_GREATER_THAN: - return compare > 0; - case OP_GREATER_THAN_OR_EQUAL: - return compare >= 0; - } - FATAL("Unknown/Unsupported operation: %d\n", fOp); - return false; -} - - -void -Equation::Complement() -{ - D(if (fOp <= OP_EQUATION || fOp > OP_LESS_THAN_OR_EQUAL) { - FATAL("op out of range!"); - return; - }); - - int8 complementOp[] = {OP_UNEQUAL, OP_EQUAL, OP_LESS_THAN_OR_EQUAL, - OP_GREATER_THAN_OR_EQUAL, OP_LESS_THAN, OP_GREATER_THAN}; - fOp = complementOp[fOp - OP_EQUAL]; -} - - -status_t -Equation::MatchEmptyString() -{ - // there is no matching attribute, we will just bail out if we - // already know that our value is not of a string type. - // If not, it will be converted to a string - and then be compared with "". - // That's why we have to call ConvertValue() here - but it will be - // a cheap call for the next time - // Should we do this only for OP_UNEQUAL? - if (fType != 0 && fType != B_STRING_TYPE) - return NO_MATCH; - - status_t status = ConvertValue(B_STRING_TYPE); - if (status == B_OK) - status = CompareTo((const uint8 *)"", fSize) ? MATCH_OK : NO_MATCH; - - return status; -} - - -/** Matches the inode's attribute value with the equation. - * Returns MATCH_OK if it matches, NO_MATCH if not, < 0 if something went wrong - */ - -status_t -Equation::Match(Entry *entry, Node* node, const char *attributeName, int32 type, - const uint8 *key, size_t size) -{ - // get a pointer to the attribute in question - union value value; - const uint8 *buffer; - - // first, check if we are matching for a live query and use that value - if (attributeName != NULL && !strcmp(fAttribute, attributeName)) { - if (key == NULL) { - if (type == B_STRING_TYPE) { - // special case: a NULL "name" means the entry has been removed - // or not yet been added -- we refuse to match, whatever the - // pattern - if (!strcmp(fAttribute, "name")) - return NO_MATCH; - - return MatchEmptyString(); - } - - return NO_MATCH; - } - buffer = const_cast(key); - } else if (!strcmp(fAttribute, "name")) { - // if not, check for "fake" attributes, "name", "size", "last_modified", - if (!entry) - return B_ERROR; - buffer = (uint8 *)entry->GetName(); - if (buffer == NULL) - return B_ERROR; - - type = B_STRING_TYPE; - size = strlen((const char *)buffer); - } else if (!strcmp(fAttribute,"size")) { - value.Int64 = node->GetSize(); - buffer = (uint8 *)&value; - type = B_INT64_TYPE; - } else if (!strcmp(fAttribute,"last_modified")) { - value.Int32 = node->GetMTime(); - buffer = (uint8 *)&value; - type = B_INT32_TYPE; - } else { - // then for attributes - Attribute *attribute = NULL; - buffer = (const uint8*)alloca(kMaxIndexKeyLength); - - if (node->FindAttribute(fAttribute, &attribute) == B_OK) { - attribute->GetKey((uint8*)buffer, &size); - type = attribute->GetType(); - } else - return MatchEmptyString(); - } - // prepare own value for use, if it is possible to convert it - status_t status = ConvertValue(type); - if (status == B_OK) - status = CompareTo(buffer, size) ? MATCH_OK : NO_MATCH; - - RETURN_ERROR(status); -} - - -void -Equation::CalculateScore(IndexWrapper &index) -{ - // As always, these values could be tuned and refined. - // And the code could also need some real world testing :-) - - // do we have to operate on a "foreign" index? - if (fOp == OP_UNEQUAL || index.SetTo(fAttribute) < B_OK) { - fScore = 0; - return; + static status_t IndexSetTo(Index& index, const char* attribute) + { + index.index = index.query->fVolume->FindIndex(attribute); + return index.index != NULL ? B_OK : B_ENTRY_NOT_FOUND; } - // if we have a pattern, how much does it help our search? - if (fIsPattern) - fScore = getFirstPatternSymbol(fString) << 3; - else { - // Score by operator - if (fOp == OP_EQUAL) - // higher than pattern="255 chars+*" - fScore = 2048; - else - // the pattern search is regarded cheaper when you have at - // least one character to set your index to - fScore = 5; + static void IndexUnset(Index& index) + { + index.index = NULL; } - // take index size into account (1024 is the current node size - // in our B+trees) - // 2048 * 2048 == 4194304 is the maximum score (for an empty - // tree, since the header + 1 node are already 2048 bytes) - fScore = fScore * ((2048 * 1024LL) / index.GetSize()); -} - - -status_t -Equation::PrepareQuery(Volume */*volume*/, IndexWrapper &index, IndexIterator **iterator, bool queryNonIndexed) -{ - status_t status = index.SetTo(fAttribute); - - // if we should query attributes without an index, we can just proceed here - if (status < B_OK && !queryNonIndexed) - return B_ENTRY_NOT_FOUND; - - type_code type; - - // special case for OP_UNEQUAL - it will always operate through the whole index - // but we need the call to the original index to get the correct type - if (status < B_OK || fOp == OP_UNEQUAL) { - // Try to get an index that holds all files (name) - // Also sets the default type for all attributes without index - // to string. - type = status < B_OK ? B_STRING_TYPE : index.Type(); - - if (index.SetTo("name") < B_OK) - return B_ENTRY_NOT_FOUND; - - fHasIndex = false; - } else { - fHasIndex = true; - type = index.Type(); + static int32 IndexGetWeightedScore(Index& index, int32 score) + { + // should be inversely proportional to the index size; max input score + // is 2048 + static const int32 maxFactor = (1024 * 1024) - 1; + return score * (maxFactor / + std::min(maxFactor, std::max((int32)1, index.index->CountEntries()))); } - if (ConvertValue(type) < B_OK) - return B_BAD_VALUE; - - *iterator = new IndexIterator(&index); - if (*iterator == NULL) - return B_NO_MEMORY; - - if ((fOp == OP_EQUAL || fOp == OP_GREATER_THAN || fOp == OP_GREATER_THAN_OR_EQUAL - || fIsPattern) - && fHasIndex) { - // set iterator to the exact position - - int32 keySize = index.KeySize(); - - // at this point, fIsPattern is only true if it's a string type, and fOp - // is either OP_EQUAL or OP_UNEQUAL - if (fIsPattern) { - // let's see if we can use the beginning of the key for positioning - // the iterator and adjust the key size; if not, just leave the - // iterator at the start and return success - keySize = getFirstPatternSymbol(fString); - if (keySize <= 0) - return B_OK; - } - - if (keySize == 0) { - // B_STRING_TYPE doesn't have a fixed length, so it was set - // to 0 before - we compute the correct value here - if (fType == B_STRING_TYPE) { - keySize = strlen(fValue.CString); - - // The empty string is a special case - we normally don't check - // for the trailing null byte, in the case for the empty string - // we do it explicitly, because there can't be keys in the B+tree - // with a length of zero - if (keySize == 0) - keySize = 1; - } else - RETURN_ERROR(B_ENTRY_NOT_FOUND); - } - - status = (*iterator)->Find(Value(), keySize); - if (fOp == OP_EQUAL && !fIsPattern) - return status; - else if (status == B_ENTRY_NOT_FOUND - && (fIsPattern || fOp == OP_GREATER_THAN - || fOp == OP_GREATER_THAN_OR_EQUAL)) { - return (*iterator)->Rewind(); - } - - RETURN_ERROR(status); + static type_code IndexGetType(Index& index) + { + return index.index->GetType(); } - return B_OK; -} - - -status_t -Equation::GetNextMatching(Volume *volume, IndexIterator *iterator, - struct dirent *dirent, size_t bufferSize) -{ - while (true) { - union value indexValue; - uint16 keyLength; - Entry *entry = NULL; - - status_t status = iterator->GetNextEntry((uint8*)&indexValue, &keyLength, - (uint16)sizeof(indexValue), &entry); - if (status < B_OK) - return status; - - // only compare against the index entry when this is the correct - // index for the equation - if (fHasIndex && !CompareTo((uint8 *)&indexValue, keyLength)) { - // They aren't equal? let the operation decide what to do - // Since we always start at the beginning of the index (or the correct - // position), only some needs to be stopped if the entry doesn't fit. - if (fOp == OP_LESS_THAN - || fOp == OP_LESS_THAN_OR_EQUAL - || (fOp == OP_EQUAL && !fIsPattern)) - return B_ENTRY_NOT_FOUND; - - continue; - } - - // ToDo: check user permissions here - but which one?! - // we could filter out all those where we don't have - // read access... (we should check for every parent - // directory if the X_OK is allowed) - // Although it's quite expensive to open all parents, - // it's likely that the application that runs the - // query will do something similar (and we don't have - // to do it for root, either). - - // go up in the tree until a &&-operator is found, and check if the - // inode matches with the rest of the expression - we don't have to - // check ||-operators for that - Term *term = this; - status = MATCH_OK; - - if (!fHasIndex) - status = Match(entry, entry->GetNode()); - - while (term != NULL && status == MATCH_OK) { - Operator *parent = (Operator *)term->Parent(); - if (parent == NULL) - break; - - if (parent->Op() == OP_AND) { - // choose the other child of the parent - Term *other = parent->Right(); - if (other == term) - other = parent->Left(); - - if (other == NULL) { - FATAL("&&-operator has only one child... (parent = %p)\n", parent); - break; - } - status = other->Match(entry, entry->GetNode()); - if (status < 0) { - REPORT_ERROR(status); - status = NO_MATCH; - } - } - term = (Term *)parent; - } - - if (status == MATCH_OK) { - size_t nameLen = strlen(entry->GetName()); - - // check, whether the entry fits into the buffer, - // and fill it in - size_t length = (dirent->d_name + nameLen + 1) - (char*)dirent; - if (length > bufferSize) - RETURN_ERROR(B_BUFFER_OVERFLOW); - - dirent->d_dev = volume->GetID(); - dirent->d_ino = entry->GetNode()->GetID(); - dirent->d_pdev = volume->GetID(); - dirent->d_pino = entry->GetParent()->GetID(); - - memcpy(dirent->d_name, entry->GetName(), nameLen); - dirent->d_name[nameLen] = '\0'; - dirent->d_reclen = length; - } - - if (status == MATCH_OK) - return B_OK; - } - RETURN_ERROR(B_ERROR); -} - - -bool -Equation::NeedsEntry() -{ - return strcmp(fAttribute, "name") == 0; -} - - -// #pragma mark - - - -Operator::Operator(Term *left, int8 op, Term *right) - : Term(op), - fLeft(left), - fRight(right) -{ - if (left) - left->SetParent(this); - if (right) - right->SetParent(this); -} - - -Operator::~Operator() -{ - delete fLeft; - delete fRight; -} - - -status_t -Operator::Match(Entry *entry, Node* node, const char *attribute, - int32 type, const uint8 *key, size_t size) -{ - if (fOp == OP_AND) { - status_t status = fLeft->Match(entry, node, attribute, type, key, size); - if (status != MATCH_OK) - return status; - - return fRight->Match(entry, node, attribute, type, key, size); - } else { - // choose the term with the better score for OP_OR - if (fRight->Score() > fLeft->Score()) { - status_t status = fRight->Match(entry, node, attribute, type, key, - size); - if (status != NO_MATCH) - return status; - } - return fLeft->Match(entry, node, attribute, type, key, size); - } -} - - -void -Operator::Complement() -{ - if (fOp == OP_AND) - fOp = OP_OR; - else - fOp = OP_AND; - - fLeft->Complement(); - fRight->Complement(); -} - - -void -Operator::CalculateScore(IndexWrapper &index) -{ - fLeft->CalculateScore(index); - fRight->CalculateScore(index); -} - - -int32 -Operator::Score() const -{ - if (fOp == OP_AND) { - // return the one with the better score - if (fRight->Score() > fLeft->Score()) - return fRight->Score(); - - return fLeft->Score(); + static int32 IndexGetKeySize(Index& index) + { + return index.index->GetKeyLength(); } - // for OP_OR, be honest, and return the one with the worse score - if (fRight->Score() < fLeft->Score()) - return fRight->Score(); - - return fLeft->Score(); -} - - -status_t -Operator::InitCheck() -{ - if ((fOp != OP_AND && fOp != OP_OR) - || fLeft == NULL || fLeft->InitCheck() < B_OK - || fRight == NULL || fRight->InitCheck() < B_OK) - return B_ERROR; - - return B_OK; -} - - -bool -Operator::NeedsEntry() -{ - return ((fLeft && fLeft->NeedsEntry()) || (fRight && fRight->NeedsEntry())); -} - - -#if 0 -Term * -Operator::Copy() const -{ - if (fEquation != NULL) { - Equation *equation = new Equation(*fEquation); - if (equation == NULL) + static IndexIterator* IndexCreateIterator(Index& index) + { + IndexIterator* iterator = new(std::nothrow) IndexIterator(index.index); + if (iterator == NULL) return NULL; - Term *term = new Term(equation); - if (term == NULL) - delete equation; - - return term; + return iterator; } - Term *left = NULL, *right = NULL; + // IndexIterator interface - if (fLeft != NULL && (left = fLeft->Copy()) == NULL) - return NULL; - if (fRight != NULL && (right = fRight->Copy()) == NULL) { - delete left; - return NULL; + static void IndexIteratorDelete(IndexIterator* indexIterator) + { + delete indexIterator; } - Term *term = new Term(left,fOp,right); - if (term == NULL) { - delete left; - delete right; - return NULL; - } - return term; -} -#endif - - -// #pragma mark - - -#if DEBUG -void -Operator::PrintToStream() -{ - D(__out("( ")); - if (fLeft != NULL) - fLeft->PrintToStream(); - - const char* op; - switch (fOp) { - case OP_OR: op = "OR"; break; - case OP_AND: op = "AND"; break; - default: op = "?"; break; - } - D(__out(" %s ", op)); - - if (fRight != NULL) - fRight->PrintToStream(); - - D(__out(" )")); -} - - -void -Equation::PrintToStream() -{ - const char* op; - switch (fOp) { - case OP_EQUAL: op = "=="; break; - case OP_UNEQUAL: op = "!="; break; - case OP_GREATER_THAN: op = ">"; break; - case OP_GREATER_THAN_OR_EQUAL: op = ">="; break; - case OP_LESS_THAN: op = "<"; break; - case OP_LESS_THAN_OR_EQUAL: op = "<="; break; - default: op = "???"; break; - } - D(__out("[\"%s\" %s \"%s\"]", fAttribute, op, fString)); -} - - -#endif /* DEBUG */ - -// #pragma mark - - - -Expression::Expression(char *expr) -{ - if (expr == NULL) - return; - - fTerm = ParseOr(&expr); - if (fTerm != NULL && fTerm->InitCheck() < B_OK) { - FATAL("Corrupt tree in expression!\n"); - delete fTerm; - fTerm = NULL; - } - D(if (fTerm != NULL) { - fTerm->PrintToStream(); - D(__out("\n")); - if (*expr != '\0') - PRINT("Unexpected end of string: \"%s\"!\n", expr); - }); - fPosition = expr; -} - - -Expression::~Expression() -{ - delete fTerm; -} - - -Term * -Expression::ParseEquation(char **expr) -{ - skipWhitespace(expr); - - bool nott = false; // note: not is a C++ keyword - if (**expr == '!') { - skipWhitespace(expr, 1); - if (**expr != '(') - return NULL; - - nott = true; + static status_t IndexIteratorFind(IndexIterator* indexIterator, + const void* value, size_t size) + { + return indexIterator->Find((const uint8*)value, size); } - if (**expr == ')') { - // shouldn't be handled here - return NULL; - } else if (**expr == '(') { - skipWhitespace(expr, 1); - - Term *term = ParseOr(expr); - - skipWhitespace(expr); - - if (**expr != ')') { - delete term; - return NULL; - } - - // If the term is negated, we just complement the tree, to get - // rid of the not, a.k.a. DeMorgan's Law. - if (nott) - term->Complement(); - - skipWhitespace(expr, 1); - - return term; + static status_t IndexIteratorGetNextEntry(IndexIterator* indexIterator, + void* value, size_t* _valueLength, size_t bufferSize, Entry** _entry) + { + return indexIterator->GetNextEntry((uint8*)value, _valueLength, _entry); } - Equation *equation = new Equation(expr); - if (equation == NULL || equation->InitCheck() < B_OK) { - delete equation; - return NULL; - } - return equation; -} - - -Term * -Expression::ParseAnd(char **expr) -{ - Term *left = ParseEquation(expr); - if (left == NULL) - return NULL; - - while (IsOperator(expr,'&')) { - Term *right = ParseAnd(expr); - Term *newParent = NULL; - - if (right == NULL || (newParent = new Operator(left, OP_AND, right)) == NULL) { - delete left; - delete right; - - return NULL; - } - left = newParent; + static void IndexIteratorSuspend(IndexIterator* indexIterator) + { + // Nothing to do. } - return left; -} - - -Term * -Expression::ParseOr(char **expr) -{ - Term *left = ParseAnd(expr); - if (left == NULL) - return NULL; - - while (IsOperator(expr,'|')) { - Term *right = ParseAnd(expr); - Term *newParent = NULL; - - if (right == NULL || (newParent = new Operator(left, OP_OR, right)) == NULL) { - delete left; - delete right; - - return NULL; - } - left = newParent; + static void IndexIteratorResume(IndexIterator* indexIterator) + { + // Nothing to do. } - return left; -} + // Node interface - -bool -Expression::IsOperator(char **expr, char op) -{ - char *string = *expr; - - if (*string == op && *(string + 1) == op) { - *expr += 2; - return true; + static const off_t NodeGetSize(Node* node) + { + return node->GetSize(); } - return false; -} + + static time_t NodeGetLastModifiedTime(Node* node) + { + return node->GetMTime(); + } + + static status_t NodeGetAttribute(Node* node, const char* attribute, + void* buffer, size_t* _size, int32* _type) + { + Attribute* attr = NULL; + status_t error = node->FindAttribute(attribute, &attr); + if (error != B_OK) + return error; + + *_type = attr->GetType(); + error = attr->ReadAt(0, buffer, *_size, _size); + + return error; + } + + static Entry* NodeGetFirstReferrer(Node* node) + { + return node->GetFirstReferrer(); + } + + static Entry* NodeGetNextReferrer(Node* node, Entry* entry) + { + return node->GetNextReferrer(entry); + } + + // Volume interface + + static dev_t ContextGetVolumeID(Context* context) + { + return context->fVolume->GetID(); + } +}; -status_t -Expression::InitCheck() -{ - if (fTerm == NULL) - return B_BAD_VALUE; - - return B_OK; -} +// #pragma mark - Query -// #pragma mark - - - -Query::Query(Volume *volume, Expression *expression, uint32 flags) +Query::Query(Volume* volume) : fVolume(volume), - fExpression(expression), - fCurrent(NULL), - fIterator(NULL), - fIndex(volume), - fFlags(flags), - fPort(-1), - fNeedsEntry(false) + fImpl(NULL) { - // if the expression has a valid root pointer, the whole tree has - // already passed the sanity check, so that we don't have to check - // every pointer - if (volume == NULL || expression == NULL || expression->Root() == NULL) - return; - - // create index on the stack and delete it afterwards - fExpression->Root()->CalculateScore(fIndex); - fIndex.Unset(); - - fNeedsEntry = fExpression->Root()->NeedsEntry(); - - Rewind(); - - if (fFlags & B_LIVE_QUERY) - volume->AddQuery(this); } Query::~Query() { - if (fFlags & B_LIVE_QUERY) - fVolume->RemoveQuery(this); + 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() { - // free previous stuff - - fStack.MakeEmpty(); - - delete fIterator; - fIterator = NULL; - fCurrent = NULL; - - // put the whole expression on the stack - - Stack stack; - stack.Push(fExpression->Root()); - - Term *term; - while (stack.Pop(&term)) { - if (term->Op() < OP_EQUATION) { - Operator *op = (Operator *)term; - - if (op->Op() == OP_OR) { - stack.Push(op->Left()); - stack.Push(op->Right()); - } else { - // For OP_AND, we can use the scoring system to decide which path to add - if (op->Right()->Score() > op->Left()->Score()) - stack.Push(op->Right()); - else - stack.Push(op->Left()); - } - } else if (term->Op() == OP_EQUATION || fStack.Push((Equation *)term) < B_OK) - FATAL("Unknown term on stack or stack error"); - } - - return B_OK; + return fImpl->Rewind(); } status_t -Query::GetNextEntry(struct dirent *dirent, size_t size) +Query::GetNextEntry(struct dirent* entry, size_t size) { - // If we don't have an equation to use yet/anymore, get a new one - // from the stack - while (true) { - if (fIterator == NULL) { - if (!fStack.Pop(&fCurrent) - || fCurrent == NULL - || fCurrent->PrepareQuery(fVolume, fIndex, &fIterator, - fFlags & B_QUERY_NON_INDEXED) < B_OK) - return B_ENTRY_NOT_FOUND; - } - if (fCurrent == NULL) - RETURN_ERROR(B_ERROR); - - status_t status = fCurrent->GetNextMatching(fVolume, fIterator, dirent, size); - if (status < B_OK) { - delete fIterator; - fIterator = NULL; - fCurrent = NULL; - } else { - // only return if we have another entry - return B_OK; - } - } + return fImpl->GetNextEntry(entry, size); } void -Query::SetLiveMode(port_id port, int32 token) +Query::LiveUpdate(Entry* entry, Node* node, const char* attribute, int32 type, + const void* oldKey, size_t oldLength, const void* newKey, size_t newLength) { - fPort = port; - fToken = token; + fImpl->LiveUpdate(entry, node, attribute, type, (const uint8*)oldKey, + oldLength, (const uint8*)newKey, newLength); +} - if ((fFlags & B_LIVE_QUERY) == 0) { - // you can decide at any point to set the live query mode, - // only live queries have to be updated by attribute changes - fFlags |= B_LIVE_QUERY; + +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; } - - -static void -send_entry_notification(port_id port, int32 token, Volume* volume, Entry* entry, - bool created) -{ - if (created) { - notify_query_entry_created(port, token, volume->GetID(), - entry->GetParent()->GetID(), entry->GetName(), - entry->GetNode()->GetID()); - } else { - notify_query_entry_removed(port, token, volume->GetID(), - entry->GetParent()->GetID(), entry->GetName(), - entry->GetNode()->GetID()); - } -} - - -void -Query::LiveUpdate(Entry *entry, Node* node, const char *attribute, int32 type, - const uint8 *oldKey, size_t oldLength, const uint8 *newKey, - size_t newLength) -{ -PRINT("%p->Query::LiveUpdate(%p, %p, \"%s\", 0x%lx, %p, %lu, %p, %lu)\n", -this, entry, node, attribute, type, oldKey, oldLength, newKey, newLength); - if (fPort < 0 || fExpression == NULL || node == NULL || attribute == NULL) - return; - - // ToDo: check if the attribute is part of the query at all... - - // If no entry has been supplied, but the we need one for the evaluation - // (i.e. the "name" attribute is used), we invoke ourselves for all entries - // referring to the given node. - if (!entry && fNeedsEntry) { - entry = node->GetFirstReferrer(); - while (entry) { - LiveUpdate(entry, node, attribute, type, oldKey, oldLength, newKey, - newLength); - entry = node->GetNextReferrer(entry); - } - return; - } - - status_t oldStatus = fExpression->Root()->Match(entry, node, attribute, - type, oldKey, oldLength); - status_t newStatus = fExpression->Root()->Match(entry, node, attribute, - type, newKey, newLength); -PRINT(" oldStatus: 0x%lx, newStatus: 0x%lx\n", oldStatus, newStatus); - - bool created; - if (oldStatus == MATCH_OK && newStatus == MATCH_OK) { - // only send out a notification if the name was changed - if (oldKey == NULL || strcmp(attribute,"name")) - return; - - if (entry) { - // entry should actually always be given, when the changed - // attribute is the entry name -PRINT("notification: old: removed\n"); - notify_query_entry_removed(fPort, fToken, fVolume->GetID(), - entry->GetParent()->GetID(), (const char *)oldKey, - entry->GetNode()->GetID()); - } - created = true; - } else if (oldStatus != MATCH_OK && newStatus != MATCH_OK) { - // nothing has changed - return; - } else if (oldStatus == MATCH_OK && newStatus != MATCH_OK) - created = false; - else - created = true; - - // We send a notification for the given entry, if any, or otherwise for - // all entries referring to the node; - if (entry) { -PRINT("notification: new: %s\n", (created ? "created" : "removed")); - send_entry_notification(fPort, fToken, fVolume, entry, created); - } else { - entry = node->GetFirstReferrer(); - while (entry) { - send_entry_notification(fPort, fToken, fVolume, entry, created); - entry = node->GetNextReferrer(entry); - } - } -} - diff --git a/src/add-ons/kernel/file_systems/ramfs/Query.h b/src/add-ons/kernel/file_systems/ramfs/Query.h index 0fadfc4961..2215e2c860 100644 --- a/src/add-ons/kernel/file_systems/ramfs/Query.h +++ b/src/add-ons/kernel/file_systems/ramfs/Query.h @@ -15,115 +15,51 @@ #include #include "Index.h" -#include "Stack.h" #include "ramfs.h" -class Entry; -class Equation; -class IndexIterator; + +namespace QueryParser { + template class Query; +}; + class Node; -class Query; -class Term; class Volume; #define B_QUERY_NON_INDEXED 0x00000002 -// Wraps the RAM FS Index to provide the interface required by the Query -// implementation. At least most of it. -// -// IndexWrapper -class IndexWrapper { -public: - IndexWrapper(Volume *volume); - - status_t SetTo(const char *name); - void Unset(); - - uint32 Type() const; - off_t GetSize() const; - int32 KeySize() const; - -private: - friend class IndexIterator; - - Volume *fVolume; - Index *fIndex; -}; - -// IndexIterator -class IndexIterator { -public: - IndexIterator(IndexWrapper *indexWrapper); - - status_t Find(const uint8 *const key, size_t keyLength); - status_t Rewind(); - status_t GetNextEntry(uint8 *buffer, uint16 *keyLength, size_t bufferSize, - Entry **entry); - -private: - IndexWrapper *fIndexWrapper; - IndexEntryIterator fIterator; - bool fInitialized; -}; - - -class Expression { - public: - Expression(char *expr); - ~Expression(); - - status_t InitCheck(); - const char *Position() const { return fPosition; } - Term *Root() const { return fTerm; } - - protected: - Term *ParseOr(char **expr); - Term *ParseAnd(char **expr); - Term *ParseEquation(char **expr); - - bool IsOperator(char **expr,char op); - - private: - Expression(const Expression &); - Expression &operator=(const Expression &); - // no implementation - - char *fPosition; - Term *fTerm; -}; - class Query : public DoublyLinkedListLinkImpl { - public: - Query(Volume *volume, Expression *expression, uint32 flags); - ~Query(); +public: + ~Query(); - status_t Rewind(); - status_t GetNextEntry(struct dirent *, size_t size); + static status_t Create(Volume* volume, const char* queryString, + uint32 flags, port_id port, uint32 token, + Query*& _query); - void SetLiveMode(port_id port, int32 token); - void LiveUpdate(Entry *entry, Node* node, const char *attribute, - int32 type, const uint8 *oldKey, size_t oldLength, - const uint8 *newKey, size_t newLength); + status_t Rewind(); + status_t GetNextEntry(struct dirent* entry, size_t size); - Expression *GetExpression() const { return fExpression; } + void LiveUpdate(Entry* entry, Node* node, + const char* attribute, int32 type, + const void* oldKey, size_t oldLength, + const void* newKey, size_t newLength); - private: -// void SendNotification(Entry* entry) +private: + struct QueryPolicy; + friend struct QueryPolicy; + typedef QueryParser::Query QueryImpl; - private: - Volume *fVolume; - Expression *fExpression; - Equation *fCurrent; - IndexIterator *fIterator; - IndexWrapper fIndex; - Stack fStack; +private: + Query(Volume* volume); - uint32 fFlags; - port_id fPort; - int32 fToken; - bool fNeedsEntry; + status_t _Init(const char* queryString, uint32 flags, + port_id port, uint32 token); + +private: + Volume* fVolume; + QueryImpl* fImpl; }; + #endif /* QUERY_H */ diff --git a/src/add-ons/kernel/file_systems/ramfs/Stack.h b/src/add-ons/kernel/file_systems/ramfs/Stack.h deleted file mode 100644 index dc832aabf1..0000000000 --- a/src/add-ons/kernel/file_systems/ramfs/Stack.h +++ /dev/null @@ -1,78 +0,0 @@ -/* Stack - a template stack class (plus some handy methods) - * - * Copyright 2001-2005, Axel Dörfler, axeld@pinc-software.de. - * This file may be used under the terms of the MIT License. - */ -#ifndef KERNEL_UTIL_STACK_H -#define KERNEL_UTIL_STACK_H - - -#include - - -template class Stack { - public: - Stack() - : - fArray(NULL), - fUsed(0), - fMax(0) - { - } - - ~Stack() - { - free(fArray); - } - - bool IsEmpty() const - { - return fUsed == 0; - } - - void MakeEmpty() - { - // could also free the memory - fUsed = 0; - } - - status_t Push(T value) - { - if (fUsed >= fMax) { - fMax += 16; - T *newArray = (T *)realloc(fArray, fMax * sizeof(T)); - if (newArray == NULL) - return B_NO_MEMORY; - - fArray = newArray; - } - fArray[fUsed++] = value; - return B_OK; - } - - bool Pop(T *value) - { - if (fUsed == 0) - return false; - - *value = fArray[--fUsed]; - return true; - } - - T *Array() - { - return fArray; - } - - int32 CountItems() const - { - return fUsed; - } - - private: - T *fArray; - int32 fUsed; - int32 fMax; -}; - -#endif /* KERNEL_UTIL_STACK_H */ diff --git a/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp index eed3a2c49d..0a07698f06 100644 --- a/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp @@ -2039,33 +2039,17 @@ ramfs_open_query(fs_volume* _volume, const char *queryString, uint32 flags, Volume* volume = (Volume*)_volume->private_volume; // lock the volume - VolumeReadLocker locker(volume); + VolumeWriteLocker locker(volume); if (!locker.IsLocked()) RETURN_ERROR(B_ERROR); - // parse the query expression - Expression *expression = new Expression((char *)queryString); - if (expression == NULL) - RETURN_ERROR(B_NO_MEMORY); - ObjectDeleter expressionDeleter(expression); - - if (expression->InitCheck() < B_OK) { - WARN("Could not parse query, stopped at: \"%s\"\n", - expression->Position()); - RETURN_ERROR(B_BAD_VALUE); - } - - // create the query - Query *query = new Query(volume, expression, flags); - if (query == NULL) - RETURN_ERROR(B_NO_MEMORY); - expressionDeleter.Detach(); + Query* query; + status_t error = Query::Create(volume, queryString, flags, port, token, query); + if (error != B_OK) + return error; // TODO: The Query references an Index, but nothing prevents the Index // from being deleted, while the Query is in existence. - if (flags & B_LIVE_QUERY) - query->SetLiveMode(port, token); - *_cookie = (void *)query; return B_OK; @@ -2090,14 +2074,12 @@ ramfs_free_query_cookie(fs_volume* _volume, void* _cookie) Volume* volume = (Volume*)_volume->private_volume; // lock the volume - VolumeReadLocker locker(volume); + VolumeWriteLocker locker(volume); if (!locker.IsLocked()) RETURN_ERROR(B_ERROR); Query *query = (Query *)_cookie; - Expression *expression = query->GetExpression(); delete query; - delete expression; return B_OK; }