1 /* 2 * Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de> 4 * This file may be used under the terms of the MIT License. 5 */ 6 7 /*! Query parsing and evaluation 8 9 The pattern matching is roughly based on code originally written 10 by J. Kercheval, and on code written by Kenneth Almquist, though 11 it shares no code. 12 */ 13 14 #include "Query.h" 15 16 #include <query_private.h> 17 18 #include "BPlusTree.h" 19 #include "bfs.h" 20 #include "Debug.h" 21 #include "Index.h" 22 #include "Inode.h" 23 #include "Volume.h" 24 25 // The parser has a very static design, but it will do what is required. 26 // 27 // ParseOr(), ParseAnd(), ParseEquation() are guarantying the operator 28 // precedence, that is =,!=,>,<,>=,<= .. && .. ||. 29 // Apparently, the "!" (not) can only be used with brackets. 30 // 31 // If you think that there are too few NULL pointer checks in some places 32 // of the code, just read the beginning of the query constructor. 33 // The API is not fully available, just the Query and the Expression class 34 // are. 35 36 37 enum ops { 38 OP_NONE, 39 40 OP_AND, 41 OP_OR, 42 43 OP_EQUATION, 44 // is only used for invalid equations 45 46 OP_EQUAL, 47 OP_UNEQUAL, 48 OP_GREATER_THAN, 49 OP_LESS_THAN, 50 OP_GREATER_THAN_OR_EQUAL, 51 OP_LESS_THAN_OR_EQUAL, 52 }; 53 54 enum match { 55 NO_MATCH = 0, 56 MATCH_OK = 1, 57 58 MATCH_BAD_PATTERN = -2, 59 MATCH_INVALID_CHARACTER 60 }; 61 62 // return values from isValidPattern() 63 enum { 64 PATTERN_INVALID_ESCAPE = -3, 65 PATTERN_INVALID_RANGE, 66 PATTERN_INVALID_SET 67 }; 68 69 union value { 70 int64 Int64; 71 uint64 Uint64; 72 int32 Int32; 73 uint32 Uint32; 74 float Float; 75 double Double; 76 char String[INODE_FILE_NAME_LENGTH]; 77 }; 78 79 // B_MIME_STRING_TYPE is defined in storage/Mime.h, but we 80 // don't need the whole file here; the type can't change anyway 81 #ifndef _MIME_H 82 # define B_MIME_STRING_TYPE 'MIMS' 83 #endif 84 85 /*! Abstract base class for the operator/equation classes. 86 */ 87 class Term { 88 public: 89 Term(int8 op) : fOp(op), fParent(NULL) {} 90 virtual ~Term() {} 91 92 int8 Op() const { return fOp; } 93 94 void SetParent(Term* parent) { fParent = parent; } 95 Term* Parent() const { return fParent; } 96 97 virtual status_t Match(Inode* inode, const char* attribute = NULL, 98 int32 type = 0, const uint8* key = NULL, 99 size_t size = 0) = 0; 100 virtual void Complement() = 0; 101 102 virtual void CalculateScore(Index& index) = 0; 103 virtual int32 Score() const = 0; 104 105 virtual status_t InitCheck() = 0; 106 107 #ifdef DEBUG 108 virtual void PrintToStream() = 0; 109 #endif 110 111 protected: 112 int8 fOp; 113 Term* fParent; 114 }; 115 116 117 /*! An Equation object represents an "attribute-equation operator-value" pair. 118 119 Although an Equation object is quite independent from the volume on which 120 the query is run, there are some dependencies that are produced while 121 querying: 122 The type/size of the value, the score, and if it has an index or not. 123 So you could run more than one query on the same volume, but it might return 124 wrong values when it runs concurrently on another volume. 125 That's not an issue right now, because we run single-threaded and don't use 126 queries more than once. 127 */ 128 class Equation : public Term { 129 public: 130 Equation(char** expression); 131 virtual ~Equation(); 132 133 virtual status_t InitCheck(); 134 135 status_t ParseQuotedString(char** _start, char** _end); 136 char* CopyString(char* start, char* end); 137 138 virtual status_t Match(Inode* inode, const char* attribute = NULL, 139 int32 type = 0, const uint8* key = NULL, 140 size_t size = 0); 141 virtual void Complement(); 142 143 status_t PrepareQuery(Volume* volume, Index& index, 144 TreeIterator** iterator, bool queryNonIndexed); 145 status_t GetNextMatching(Volume* volume, TreeIterator* iterator, 146 struct dirent* dirent, size_t bufferSize); 147 148 virtual void CalculateScore(Index &index); 149 virtual int32 Score() const { return fScore; } 150 151 #ifdef DEBUG 152 virtual void PrintToStream(); 153 #endif 154 155 private: 156 Equation(const Equation& other); 157 Equation& operator=(const Equation& other); 158 // no implementation 159 160 status_t ConvertValue(type_code type); 161 bool CompareTo(const uint8* value, uint16 size); 162 uint8* Value() const { return (uint8*)&fValue; } 163 status_t MatchEmptyString(); 164 165 char* fAttribute; 166 char* fString; 167 union value fValue; 168 type_code fType; 169 size_t fSize; 170 bool fIsPattern; 171 bool fIsSpecialTime; 172 173 int32 fScore; 174 bool fHasIndex; 175 }; 176 177 178 /*! The Operator class does not represent a generic operator, but only those 179 that combine two equations, namely "or", and "and". 180 */ 181 class Operator : public Term { 182 public: 183 Operator(Term* left, int8 op, Term* right); 184 virtual ~Operator(); 185 186 Term* Left() const { return fLeft; } 187 Term* Right() const { return fRight; } 188 189 virtual status_t Match(Inode* inode, const char* attribute = NULL, 190 int32 type = 0, const uint8* key = NULL, 191 size_t size = 0); 192 virtual void Complement(); 193 194 virtual void CalculateScore(Index& index); 195 virtual int32 Score() const; 196 197 virtual status_t InitCheck(); 198 199 #ifdef DEBUG 200 virtual void PrintToStream(); 201 #endif 202 203 private: 204 Operator(const Operator& other); 205 Operator& operator=(const Operator& other); 206 // no implementation 207 208 Term* fLeft; 209 Term* fRight; 210 }; 211 212 213 // #pragma mark - 214 215 216 void 217 skipWhitespace(char** expr, int32 skip = 0) 218 { 219 char* string = (*expr) + skip; 220 while (*string == ' ' || *string == '\t') string++; 221 *expr = string; 222 } 223 224 225 void 226 skipWhitespaceReverse(char** expr, char* stop) 227 { 228 char* string = *expr; 229 while (string > stop && (*string == ' ' || *string == '\t')) 230 string--; 231 *expr = string; 232 } 233 234 235 // #pragma mark - 236 237 238 uint32 239 utf8ToUnicode(char** string) 240 { 241 uint8* bytes = (uint8*)*string; 242 int32 length; 243 uint8 mask = 0x1f; 244 245 switch (bytes[0] & 0xf0) { 246 case 0xc0: 247 case 0xd0: 248 length = 2; 249 break; 250 case 0xe0: 251 length = 3; 252 break; 253 case 0xf0: 254 mask = 0x0f; 255 length = 4; 256 break; 257 default: 258 // valid 1-byte character 259 // and invalid characters 260 (*string)++; 261 return bytes[0]; 262 } 263 uint32 c = bytes[0] & mask; 264 int32 i = 1; 265 for (; i < length && (bytes[i] & 0x80) > 0; i++) 266 c = (c << 6) | (bytes[i] & 0x3f); 267 268 if (i < length) { 269 // invalid character 270 (*string)++; 271 return (uint32)bytes[0]; 272 } 273 *string += length; 274 return c; 275 } 276 277 278 int32 279 getFirstPatternSymbol(char* string) 280 { 281 char c; 282 283 for (int32 index = 0; (c = *string++); index++) { 284 if (c == '*' || c == '?' || c == '[') 285 return index; 286 } 287 return -1; 288 } 289 290 291 bool 292 isPattern(char* string) 293 { 294 return getFirstPatternSymbol(string) >= 0 ? true : false; 295 } 296 297 298 status_t 299 isValidPattern(char* pattern) 300 { 301 while (*pattern) { 302 switch (*pattern++) { 303 case '\\': 304 // the escape character must not be at the end of the pattern 305 if (!*pattern++) 306 return PATTERN_INVALID_ESCAPE; 307 break; 308 309 case '[': 310 if (pattern[0] == ']' || !pattern[0]) 311 return PATTERN_INVALID_SET; 312 313 while (*pattern != ']') { 314 if (*pattern == '\\' && !*++pattern) 315 return PATTERN_INVALID_ESCAPE; 316 317 if (!*pattern) 318 return PATTERN_INVALID_SET; 319 320 if (pattern[0] == '-' && pattern[1] == '-') 321 return PATTERN_INVALID_RANGE; 322 323 pattern++; 324 } 325 break; 326 } 327 } 328 return B_OK; 329 } 330 331 332 /*! Matches the string against the given wildcard pattern. 333 Returns either MATCH_OK, or NO_MATCH when everything went fine, or 334 values < 0 (see enum at the top of Query.cpp) if an error occurs. 335 */ 336 status_t 337 matchString(char* pattern, char* string) 338 { 339 while (*pattern) { 340 // end of string == valid end of pattern? 341 if (!string[0]) { 342 while (pattern[0] == '*') 343 pattern++; 344 return !pattern[0] ? MATCH_OK : NO_MATCH; 345 } 346 347 switch (*pattern++) { 348 case '?': 349 { 350 // match exactly one UTF-8 character; we are 351 // not interested in the result 352 utf8ToUnicode(&string); 353 break; 354 } 355 356 case '*': 357 { 358 // compact pattern 359 while (true) { 360 if (pattern[0] == '?') { 361 if (!*++string) 362 return NO_MATCH; 363 } else if (pattern[0] != '*') 364 break; 365 366 pattern++; 367 } 368 369 // if the pattern is done, we have matched the string 370 if (!pattern[0]) 371 return MATCH_OK; 372 373 while(true) { 374 // we have removed all occurences of '*' and '?' 375 if (pattern[0] == string[0] 376 || pattern[0] == '[' 377 || pattern[0] == '\\') { 378 status_t status = matchString(pattern, string); 379 if (status < B_OK || status == MATCH_OK) 380 return status; 381 } 382 383 // we could be nice here and just jump to the next 384 // UTF-8 character - but we wouldn't gain that much 385 // and it'd be slower (since we're checking for 386 // equality before entering the recursion) 387 if (!*++string) 388 return NO_MATCH; 389 } 390 break; 391 } 392 393 case '[': 394 { 395 bool invert = false; 396 if (pattern[0] == '^' || pattern[0] == '!') { 397 invert = true; 398 pattern++; 399 } 400 401 if (!pattern[0] || pattern[0] == ']') 402 return MATCH_BAD_PATTERN; 403 404 uint32 c = utf8ToUnicode(&string); 405 bool matched = false; 406 407 while (pattern[0] != ']') { 408 if (!pattern[0]) 409 return MATCH_BAD_PATTERN; 410 411 if (pattern[0] == '\\') 412 pattern++; 413 414 uint32 first = utf8ToUnicode(&pattern); 415 416 // Does this character match, or is this a range? 417 if (first == c) { 418 matched = true; 419 break; 420 } else if (pattern[0] == '-' && pattern[1] != ']' 421 && pattern[1]) { 422 pattern++; 423 424 if (pattern[0] == '\\') { 425 pattern++; 426 if (!pattern[0]) 427 return MATCH_BAD_PATTERN; 428 } 429 uint32 last = utf8ToUnicode(&pattern); 430 431 if (c >= first && c <= last) { 432 matched = true; 433 break; 434 } 435 } 436 } 437 438 if (invert) 439 matched = !matched; 440 441 if (matched) { 442 while (pattern[0] != ']') { 443 if (!pattern[0]) 444 return MATCH_BAD_PATTERN; 445 pattern++; 446 } 447 pattern++; 448 break; 449 } 450 return NO_MATCH; 451 } 452 453 case '\\': 454 if (!pattern[0]) 455 return MATCH_BAD_PATTERN; 456 // supposed to fall through 457 default: 458 if (pattern[-1] != string[0]) 459 return NO_MATCH; 460 string++; 461 break; 462 } 463 } 464 465 if (string[0]) 466 return NO_MATCH; 467 468 return MATCH_OK; 469 } 470 471 472 // #pragma mark - 473 474 475 Equation::Equation(char** expr) 476 : Term(OP_EQUATION), 477 fAttribute(NULL), 478 fString(NULL), 479 fType(0), 480 fIsPattern(false) 481 { 482 char* string = *expr; 483 char* start = string; 484 char* end = NULL; 485 486 // Since the equation is the integral part of any query, we're just parsing 487 // the whole thing here. 488 // The whitespace at the start is already removed in 489 // Expression::ParseEquation() 490 491 if (*start == '"' || *start == '\'') { 492 // string is quoted (start has to be on the beginning of a string) 493 if (ParseQuotedString(&start, &end) < B_OK) 494 return; 495 496 // set string to a valid start of the equation symbol 497 string = end + 2; 498 skipWhitespace(&string); 499 if (*string != '=' && *string != '<' && *string != '>' 500 && *string != '!') { 501 *expr = string; 502 return; 503 } 504 } else { 505 // search the (in)equation for the actual equation symbol (and for other operators 506 // in case the equation is malformed) 507 while (*string && *string != '=' && *string != '<' && *string != '>' 508 && *string != '!' && *string != '&' && *string != '|') { 509 string++; 510 } 511 512 // get the attribute string (and trim whitespace), in case 513 // the string was not quoted 514 end = string - 1; 515 skipWhitespaceReverse(&end, start); 516 } 517 518 // attribute string is empty (which is not allowed) 519 if (start > end) 520 return; 521 522 // At this point, "start" points to the beginning of the string, "end" 523 // points to the last character of the string, and "string" points to the 524 // first character of the equation symbol 525 526 // test for the right symbol (as this doesn't need any memory) 527 switch (*string) { 528 case '=': 529 fOp = OP_EQUAL; 530 break; 531 case '>': 532 fOp = *(string + 1) == '=' 533 ? OP_GREATER_THAN_OR_EQUAL : OP_GREATER_THAN; 534 break; 535 case '<': 536 fOp = *(string + 1) == '=' 537 ? OP_LESS_THAN_OR_EQUAL : OP_LESS_THAN; 538 break; 539 case '!': 540 if (*(string + 1) != '=') 541 return; 542 fOp = OP_UNEQUAL; 543 break; 544 545 // any invalid characters will be rejected 546 default: 547 *expr = string; 548 return; 549 } 550 551 // lets change "start" to point to the first character after the symbol 552 if (*(string + 1) == '=') 553 string++; 554 string++; 555 skipWhitespace(&string); 556 557 // allocate & copy the attribute string 558 559 fAttribute = CopyString(start, end); 560 if (fAttribute == NULL) 561 return; 562 563 start = string; 564 if (*start == '"' || *start == '\'') { 565 // string is quoted (start has to be on the beginning of a string) 566 if (ParseQuotedString(&start, &end) < B_OK) 567 return; 568 569 string = end + 2; 570 skipWhitespace(&string); 571 } else { 572 while (*string && *string != '&' && *string != '|' && *string != ')') 573 string++; 574 575 end = string - 1; 576 skipWhitespaceReverse(&end, start); 577 } 578 579 // at this point, "start" will point to the first character of the value, 580 // "end" will point to its last character, and "start" to the first non- 581 // whitespace character after the value string 582 583 fString = CopyString(start, end); 584 if (fString == NULL) 585 return; 586 587 // patterns are only allowed for these operations (and strings) 588 if (fOp == OP_EQUAL || fOp == OP_UNEQUAL) { 589 fIsPattern = isPattern(fString); 590 if (fIsPattern && isValidPattern(fString) < B_OK) { 591 // we only want to have valid patterns; setting fString 592 // to NULL will cause InitCheck() to fail 593 free(fString); 594 fString = NULL; 595 } 596 } 597 598 // The special time flag is set if the time values are shifted 599 // 64-bit values to reduce the number of duplicates. 600 // We have to be able to compare them against unshifted values 601 // later. The only index which needs this is the last_modified 602 // index, but we may want to open that feature for other indices, 603 // too one day. 604 fIsSpecialTime = !strcmp(fAttribute, "last_modified"); 605 606 *expr = string; 607 } 608 609 610 Equation::~Equation() 611 { 612 free(fAttribute); 613 free(fString); 614 } 615 616 617 status_t 618 Equation::InitCheck() 619 { 620 if (fAttribute == NULL || fString == NULL || fOp == OP_NONE) 621 return B_BAD_VALUE; 622 623 return B_OK; 624 } 625 626 627 status_t 628 Equation::ParseQuotedString(char** _start, char** _end) 629 { 630 char* start = *_start; 631 char quote = *start++; 632 char* end = start; 633 634 for (; *end && *end != quote; end++) { 635 if (*end == '\\') 636 end++; 637 } 638 if (*end == '\0') 639 return B_BAD_VALUE; 640 641 *_start = start; 642 *_end = end - 1; 643 644 return B_OK; 645 } 646 647 648 char* 649 Equation::CopyString(char* start, char* end) 650 { 651 // end points to the last character of the string - and the length 652 // also has to include the null-termination 653 int32 length = end + 2 - start; 654 // just to make sure; since that's the max. attribute name length and 655 // the max. string in an index, it make sense to have it that way 656 if (length > INODE_FILE_NAME_LENGTH || length <= 0) 657 return NULL; 658 659 char* copy = (char*)malloc(length); 660 if (copy == NULL) 661 return NULL; 662 663 memcpy(copy, start, length - 1); 664 copy[length - 1] = '\0'; 665 666 return copy; 667 } 668 669 670 status_t 671 Equation::ConvertValue(type_code type) 672 { 673 // Has the type already been converted? 674 if (type == fType) 675 return B_OK; 676 677 char* string = fString; 678 679 switch (type) { 680 case B_MIME_STRING_TYPE: 681 type = B_STRING_TYPE; 682 // supposed to fall through 683 case B_STRING_TYPE: 684 strncpy(fValue.String, string, INODE_FILE_NAME_LENGTH); 685 fValue.String[INODE_FILE_NAME_LENGTH - 1] = '\0'; 686 fSize = strlen(fValue.String); 687 break; 688 case B_INT32_TYPE: 689 fValue.Int32 = strtol(string, &string, 0); 690 fSize = sizeof(int32); 691 break; 692 case B_UINT32_TYPE: 693 fValue.Int32 = strtoul(string, &string, 0); 694 fSize = sizeof(uint32); 695 break; 696 case B_INT64_TYPE: 697 fValue.Int64 = strtoll(string, &string, 0); 698 fSize = sizeof(int64); 699 break; 700 case B_UINT64_TYPE: 701 fValue.Uint64 = strtoull(string, &string, 0); 702 fSize = sizeof(uint64); 703 break; 704 case B_FLOAT_TYPE: 705 fValue.Float = strtod(string, &string); 706 fSize = sizeof(float); 707 break; 708 case B_DOUBLE_TYPE: 709 fValue.Double = strtod(string, &string); 710 fSize = sizeof(double); 711 break; 712 default: 713 FATAL(("query value conversion to 0x%x requested!\n", (int)type)); 714 // should we fail here or just do a safety int32 conversion? 715 return B_ERROR; 716 } 717 718 fType = type; 719 720 // patterns are only allowed for string types 721 if (fType != B_STRING_TYPE && fIsPattern) 722 fIsPattern = false; 723 724 return B_OK; 725 } 726 727 728 /*! Returns true when the key matches the equation. You have to 729 call ConvertValue() before this one. 730 */ 731 bool 732 Equation::CompareTo(const uint8* value, uint16 size) 733 { 734 int32 compare; 735 736 // fIsPattern is only true if it's a string type, and fOp OP_EQUAL, or 737 // OP_UNEQUAL 738 if (fIsPattern) { 739 // we have already validated the pattern, so we don't check for failing 740 // here - if something is broken, and matchString() returns an error, 741 // we just don't match 742 compare = matchString(fValue.String, (char*)value) == MATCH_OK ? 0 : 1; 743 } else if (fIsSpecialTime) { 744 // the index is a shifted int64 index, but we have to match 745 // against an unshifted value (i.e. the last_modified index) 746 int64 timeValue = *(int64*)value >> INODE_TIME_SHIFT; 747 compare = compareKeys(fType, &timeValue, sizeof(int64), &fValue.Int64, 748 sizeof(int64)); 749 } else 750 compare = compareKeys(fType, value, size, Value(), fSize); 751 752 switch (fOp) { 753 case OP_EQUAL: 754 return compare == 0; 755 case OP_UNEQUAL: 756 return compare != 0; 757 case OP_LESS_THAN: 758 return compare < 0; 759 case OP_LESS_THAN_OR_EQUAL: 760 return compare <= 0; 761 case OP_GREATER_THAN: 762 return compare > 0; 763 case OP_GREATER_THAN_OR_EQUAL: 764 return compare >= 0; 765 } 766 FATAL(("Unknown/Unsupported operation: %d\n", fOp)); 767 return false; 768 } 769 770 771 void 772 Equation::Complement() 773 { 774 D(if (fOp <= OP_EQUATION || fOp > OP_LESS_THAN_OR_EQUAL) { 775 FATAL(("op out of range!")); 776 return; 777 }); 778 779 int8 complementOp[] = {OP_UNEQUAL, OP_EQUAL, OP_LESS_THAN_OR_EQUAL, 780 OP_GREATER_THAN_OR_EQUAL, OP_LESS_THAN, OP_GREATER_THAN}; 781 fOp = complementOp[fOp - OP_EQUAL]; 782 } 783 784 785 status_t 786 Equation::MatchEmptyString() 787 { 788 // There is no matching attribute, we will just bail out if we 789 // already know that our value is not of a string type. 790 // If not, it will be converted to a string - and then be compared with "". 791 // That's why we have to call ConvertValue() here - but it will be 792 // a cheap call for the next time 793 // TODO: Should we do this only for OP_UNEQUAL? 794 if (fType != 0 && fType != B_STRING_TYPE) 795 return NO_MATCH; 796 797 status_t status = ConvertValue(B_STRING_TYPE); 798 if (status == B_OK) 799 status = CompareTo((const uint8*)"", fSize) ? MATCH_OK : NO_MATCH; 800 801 return status; 802 } 803 804 805 /*! Matches the inode's attribute value with the equation. 806 Returns MATCH_OK if it matches, NO_MATCH if not, < 0 if something went 807 wrong. 808 */ 809 status_t 810 Equation::Match(Inode* inode, const char* attributeName, int32 type, 811 const uint8* key, size_t size) 812 { 813 // get a pointer to the attribute in question 814 NodeGetter nodeGetter(inode->GetVolume()); 815 union value value; 816 uint8* buffer = (uint8*)&value; 817 bool locked = false; 818 819 // first, check if we are matching for a live query and use that value 820 if (attributeName != NULL && !strcmp(fAttribute, attributeName)) { 821 if (key == NULL) { 822 if (type == B_STRING_TYPE) 823 return MatchEmptyString(); 824 825 return NO_MATCH; 826 } 827 buffer = const_cast<uint8*>(key); 828 } else if (!strcmp(fAttribute, "name")) { 829 // we need to lock before accessing Inode::Name() 830 nodeGetter.SetToNode(inode); 831 832 recursive_lock_lock(&inode->SmallDataLock()); 833 locked = true; 834 835 // if not, check for "fake" attributes ("name", "size", "last_modified") 836 buffer = (uint8*)inode->Name(nodeGetter.Node()); 837 if (buffer == NULL) { 838 recursive_lock_unlock(&inode->SmallDataLock()); 839 return B_ERROR; 840 } 841 842 type = B_STRING_TYPE; 843 size = strlen((const char*)buffer); 844 } else if (!strcmp(fAttribute, "size")) { 845 #ifdef BFS_NATIVE_ENDIAN 846 buffer = (uint8*)&inode->Node().data.size; 847 #else 848 value.Int64 = inode->Size(); 849 #endif 850 type = B_INT64_TYPE; 851 } else if (!strcmp(fAttribute, "last_modified")) { 852 #ifdef BFS_NATIVE_ENDIAN 853 buffer = (uint8*)&inode->Node().last_modified_time; 854 #else 855 value.Int64 = inode->Node().LastModifiedTime(); 856 #endif 857 type = B_INT64_TYPE; 858 } else { 859 // then for attributes in the small_data section, and finally for the 860 // real attributes 861 nodeGetter.SetToNode(inode); 862 Inode* attribute; 863 864 recursive_lock_lock(&inode->SmallDataLock()); 865 small_data* smallData = inode->FindSmallData(nodeGetter.Node(), 866 fAttribute); 867 if (smallData != NULL) { 868 buffer = smallData->Data(); 869 type = smallData->type; 870 size = smallData->data_size; 871 locked = true; 872 } else { 873 // needed to unlock the small_data section as fast as possible 874 recursive_lock_unlock(&inode->SmallDataLock()); 875 nodeGetter.Unset(); 876 877 if (inode->GetAttribute(fAttribute, &attribute) == B_OK) { 878 buffer = (uint8*)&value; 879 type = attribute->Type(); 880 size = attribute->Size(); 881 882 if (size > INODE_FILE_NAME_LENGTH) 883 size = INODE_FILE_NAME_LENGTH; 884 885 if (attribute->ReadAt(0, buffer, &size) < B_OK) { 886 inode->ReleaseAttribute(attribute); 887 return B_IO_ERROR; 888 } 889 inode->ReleaseAttribute(attribute); 890 } else 891 return MatchEmptyString(); 892 } 893 } 894 // prepare own value for use, if it is possible to convert it 895 status_t status = ConvertValue(type); 896 if (status == B_OK) 897 status = CompareTo(buffer, size) ? MATCH_OK : NO_MATCH; 898 899 if (locked) 900 recursive_lock_unlock(&inode->SmallDataLock()); 901 902 RETURN_ERROR(status); 903 } 904 905 906 void 907 Equation::CalculateScore(Index &index) 908 { 909 // As always, these values could be tuned and refined. 910 // And the code could also need some real world testing :-) 911 912 // do we have to operate on a "foreign" index? 913 if (fOp == OP_UNEQUAL || index.SetTo(fAttribute) < B_OK) { 914 fScore = 0; 915 return; 916 } 917 918 // if we have a pattern, how much does it help our search? 919 if (fIsPattern) 920 fScore = getFirstPatternSymbol(fString) << 3; 921 else { 922 // Score by operator 923 if (fOp == OP_EQUAL) 924 // higher than pattern="255 chars+*" 925 fScore = 2048; 926 else 927 // the pattern search is regarded cheaper when you have at 928 // least one character to set your index to 929 fScore = 5; 930 } 931 932 // take index size into account (1024 is the current node size 933 // in our B+trees) 934 // 2048 * 2048 == 4194304 is the maximum score (for an empty 935 // tree, since the header + 1 node are already 2048 bytes) 936 fScore = fScore * ((2048 * 1024LL) / index.Node()->Size()); 937 } 938 939 940 status_t 941 Equation::PrepareQuery(Volume* /*volume*/, Index& index, 942 TreeIterator** iterator, bool queryNonIndexed) 943 { 944 status_t status = index.SetTo(fAttribute); 945 946 // if we should query attributes without an index, we can just proceed here 947 if (status != B_OK && !queryNonIndexed) 948 return B_ENTRY_NOT_FOUND; 949 950 type_code type; 951 952 // Special case for OP_UNEQUAL - it will always operate through the whole 953 // index but we need the call to the original index to get the correct type 954 if (status != B_OK || fOp == OP_UNEQUAL) { 955 // Try to get an index that holds all files (name) 956 // Also sets the default type for all attributes without index 957 // to string. 958 type = status < B_OK ? B_STRING_TYPE : index.Type(); 959 960 if (index.SetTo("name") != B_OK) 961 return B_ENTRY_NOT_FOUND; 962 963 fHasIndex = false; 964 } else { 965 fHasIndex = true; 966 type = index.Type(); 967 } 968 969 if (ConvertValue(type) < B_OK) 970 return B_BAD_VALUE; 971 972 BPlusTree* tree = index.Node()->Tree(); 973 if (tree == NULL) 974 return B_ERROR; 975 976 *iterator = new TreeIterator(tree); 977 if (*iterator == NULL) 978 return B_NO_MEMORY; 979 980 if ((fOp == OP_EQUAL || fOp == OP_GREATER_THAN 981 || fOp == OP_GREATER_THAN_OR_EQUAL || fIsPattern) 982 && fHasIndex) { 983 // set iterator to the exact position 984 985 int32 keySize = index.KeySize(); 986 987 // At this point, fIsPattern is only true if it's a string type, and fOp 988 // is either OP_EQUAL or OP_UNEQUAL 989 if (fIsPattern) { 990 // let's see if we can use the beginning of the key for positioning 991 // the iterator and adjust the key size; if not, just leave the 992 // iterator at the start and return success 993 keySize = getFirstPatternSymbol(fString); 994 if (keySize <= 0) 995 return B_OK; 996 } 997 998 if (keySize == 0) { 999 // B_STRING_TYPE doesn't have a fixed length, so it was set 1000 // to 0 before - we compute the correct value here 1001 if (fType == B_STRING_TYPE) { 1002 keySize = strlen(fValue.String); 1003 1004 // The empty string is a special case - we normally don't check 1005 // for the trailing null byte, in the case for the empty string 1006 // we do it explicitly, because there can't be keys in the 1007 // B+tree with a length of zero 1008 if (keySize == 0) 1009 keySize = 1; 1010 } else 1011 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1012 } 1013 1014 if (fIsSpecialTime) { 1015 // we have to find the first matching shifted value 1016 off_t value = fValue.Int64 << INODE_TIME_SHIFT; 1017 status = (*iterator)->Find((uint8*)&value, keySize); 1018 if (status == B_ENTRY_NOT_FOUND) 1019 return B_OK; 1020 } else { 1021 status = (*iterator)->Find(Value(), keySize); 1022 if (fOp == OP_EQUAL && !fIsPattern) 1023 return status; 1024 else if (status == B_ENTRY_NOT_FOUND 1025 && (fIsPattern || fOp == OP_GREATER_THAN 1026 || fOp == OP_GREATER_THAN_OR_EQUAL)) 1027 return B_OK; 1028 } 1029 1030 RETURN_ERROR(status); 1031 } 1032 1033 return B_OK; 1034 } 1035 1036 1037 status_t 1038 Equation::GetNextMatching(Volume* volume, TreeIterator* iterator, 1039 struct dirent* dirent, size_t bufferSize) 1040 { 1041 while (true) { 1042 union value indexValue; 1043 uint16 keyLength; 1044 uint16 duplicate; 1045 off_t offset; 1046 1047 status_t status = iterator->GetNextEntry(&indexValue, &keyLength, 1048 (uint16)sizeof(indexValue), &offset, &duplicate); 1049 if (status != B_OK) 1050 return status; 1051 1052 // only compare against the index entry when this is the correct 1053 // index for the equation 1054 if (fHasIndex && duplicate < 2 1055 && !CompareTo((uint8*)&indexValue, keyLength)) { 1056 // They aren't equal? Let the operation decide what to do. Since 1057 // we always start at the beginning of the index (or the correct 1058 // position), only some needs to be stopped if the entry doesn't 1059 // fit. 1060 if (fOp == OP_LESS_THAN 1061 || fOp == OP_LESS_THAN_OR_EQUAL 1062 || (fOp == OP_EQUAL && !fIsPattern)) 1063 return B_ENTRY_NOT_FOUND; 1064 1065 if (duplicate > 0) 1066 iterator->SkipDuplicates(); 1067 continue; 1068 } 1069 1070 Vnode vnode(volume, offset); 1071 Inode* inode; 1072 if ((status = vnode.Get(&inode)) != B_OK) { 1073 REPORT_ERROR(status); 1074 FATAL(("could not get inode %" B_PRIdOFF " in index \"%s\"!\n", 1075 offset, fAttribute)); 1076 // try with next 1077 continue; 1078 } 1079 1080 // TODO: check user permissions here - but which one?! 1081 // we could filter out all those where we don't have 1082 // read access... (we should check for every parent 1083 // directory if the X_OK is allowed) 1084 // Although it's quite expensive to open all parents, 1085 // it's likely that the application that runs the 1086 // query will do something similar (and we don't have 1087 // to do it for root, either). 1088 1089 // go up in the tree until a &&-operator is found, and check if the 1090 // inode matches with the rest of the expression - we don't have to 1091 // check ||-operators for that 1092 Term* term = this; 1093 status = MATCH_OK; 1094 1095 if (!fHasIndex) 1096 status = Match(inode); 1097 1098 while (term != NULL && status == MATCH_OK) { 1099 Operator* parent = (Operator*)term->Parent(); 1100 if (parent == NULL) 1101 break; 1102 1103 if (parent->Op() == OP_AND) { 1104 // choose the other child of the parent 1105 Term* other = parent->Right(); 1106 if (other == term) 1107 other = parent->Left(); 1108 1109 if (other == NULL) { 1110 FATAL(("&&-operator has only one child... (parent = %p)\n", 1111 parent)); 1112 break; 1113 } 1114 status = other->Match(inode); 1115 if (status < 0) { 1116 REPORT_ERROR(status); 1117 status = NO_MATCH; 1118 } 1119 } 1120 term = (Term*)parent; 1121 } 1122 1123 if (status == MATCH_OK) { 1124 dirent->d_dev = volume->ID(); 1125 dirent->d_ino = offset; 1126 dirent->d_pdev = volume->ID(); 1127 dirent->d_pino = volume->ToVnode(inode->Parent()); 1128 1129 if (inode->GetName(dirent->d_name) < B_OK) { 1130 FATAL(("inode %" B_PRIdOFF " in query has no name!\n", 1131 inode->BlockNumber())); 1132 } 1133 1134 dirent->d_reclen = sizeof(struct dirent) + strlen(dirent->d_name); 1135 } 1136 1137 if (status == MATCH_OK) 1138 return B_OK; 1139 } 1140 RETURN_ERROR(B_ERROR); 1141 } 1142 1143 1144 // #pragma mark - 1145 1146 1147 Operator::Operator(Term* left, int8 op, Term* right) 1148 : Term(op), 1149 fLeft(left), 1150 fRight(right) 1151 { 1152 if (left) 1153 left->SetParent(this); 1154 if (right) 1155 right->SetParent(this); 1156 } 1157 1158 1159 Operator::~Operator() 1160 { 1161 delete fLeft; 1162 delete fRight; 1163 } 1164 1165 1166 status_t 1167 Operator::Match(Inode* inode, const char* attribute, int32 type, 1168 const uint8* key, size_t size) 1169 { 1170 if (fOp == OP_AND) { 1171 status_t status = fLeft->Match(inode, attribute, type, key, size); 1172 if (status != MATCH_OK) 1173 return status; 1174 1175 return fRight->Match(inode, attribute, type, key, size); 1176 } else { 1177 // choose the term with the better score for OP_OR 1178 Term* first; 1179 Term* second; 1180 if (fRight->Score() > fLeft->Score()) { 1181 first = fLeft; 1182 second = fRight; 1183 } else { 1184 first = fRight; 1185 second = fLeft; 1186 } 1187 1188 status_t status = first->Match(inode, attribute, type, key, size); 1189 if (status != NO_MATCH) 1190 return status; 1191 1192 return second->Match(inode, attribute, type, key, size); 1193 } 1194 } 1195 1196 1197 void 1198 Operator::Complement() 1199 { 1200 if (fOp == OP_AND) 1201 fOp = OP_OR; 1202 else 1203 fOp = OP_AND; 1204 1205 fLeft->Complement(); 1206 fRight->Complement(); 1207 } 1208 1209 1210 void 1211 Operator::CalculateScore(Index &index) 1212 { 1213 fLeft->CalculateScore(index); 1214 fRight->CalculateScore(index); 1215 } 1216 1217 1218 int32 1219 Operator::Score() const 1220 { 1221 if (fOp == OP_AND) { 1222 // return the one with the better score 1223 if (fRight->Score() > fLeft->Score()) 1224 return fRight->Score(); 1225 1226 return fLeft->Score(); 1227 } 1228 1229 // for OP_OR, be honest, and return the one with the worse score 1230 if (fRight->Score() < fLeft->Score()) 1231 return fRight->Score(); 1232 1233 return fLeft->Score(); 1234 } 1235 1236 1237 status_t 1238 Operator::InitCheck() 1239 { 1240 if ((fOp != OP_AND && fOp != OP_OR) 1241 || fLeft == NULL || fLeft->InitCheck() < B_OK 1242 || fRight == NULL || fRight->InitCheck() < B_OK) 1243 return B_ERROR; 1244 1245 return B_OK; 1246 } 1247 1248 1249 #if 0 1250 Term* 1251 Operator::Copy() const 1252 { 1253 if (fEquation != NULL) { 1254 Equation* equation = new Equation(*fEquation); 1255 if (equation == NULL) 1256 return NULL; 1257 1258 Term* term = new Term(equation); 1259 if (term == NULL) 1260 delete equation; 1261 1262 return term; 1263 } 1264 1265 Term* left = NULL; 1266 Term* right = NULL; 1267 1268 if (fLeft != NULL && (left = fLeft->Copy()) == NULL) 1269 return NULL; 1270 if (fRight != NULL && (right = fRight->Copy()) == NULL) { 1271 delete left; 1272 return NULL; 1273 } 1274 1275 Term* term = new Term(left, fOp, right); 1276 if (term == NULL) { 1277 delete left; 1278 delete right; 1279 return NULL; 1280 } 1281 return term; 1282 } 1283 #endif 1284 1285 1286 // #pragma mark - 1287 1288 #ifdef DEBUG 1289 1290 void 1291 Operator::PrintToStream() 1292 { 1293 D(__out("( ")); 1294 if (fLeft != NULL) 1295 fLeft->PrintToStream(); 1296 1297 const char* op; 1298 switch (fOp) { 1299 case OP_OR: op = "OR"; break; 1300 case OP_AND: op = "AND"; break; 1301 default: op = "?"; break; 1302 } 1303 D(__out(" %s ", op)); 1304 1305 if (fRight != NULL) 1306 fRight->PrintToStream(); 1307 1308 D(__out(" )")); 1309 } 1310 1311 1312 void 1313 Equation::PrintToStream() 1314 { 1315 const char* symbol = "???"; 1316 switch (fOp) { 1317 case OP_EQUAL: symbol = "=="; break; 1318 case OP_UNEQUAL: symbol = "!="; break; 1319 case OP_GREATER_THAN: symbol = ">"; break; 1320 case OP_GREATER_THAN_OR_EQUAL: symbol = ">="; break; 1321 case OP_LESS_THAN: symbol = "<"; break; 1322 case OP_LESS_THAN_OR_EQUAL: symbol = "<="; break; 1323 } 1324 D(__out("[\"%s\" %s \"%s\"]", fAttribute, symbol, fString)); 1325 } 1326 1327 #endif // DEBUG 1328 1329 // #pragma mark - 1330 1331 1332 Expression::Expression(char* expr) 1333 { 1334 if (expr == NULL) 1335 return; 1336 1337 fTerm = ParseOr(&expr); 1338 if (fTerm != NULL && fTerm->InitCheck() < B_OK) { 1339 FATAL(("Corrupt tree in expression!\n")); 1340 delete fTerm; 1341 fTerm = NULL; 1342 } 1343 D(if (fTerm != NULL) { 1344 fTerm->PrintToStream(); 1345 D(__out("\n")); 1346 if (*expr != '\0') 1347 PRINT(("Unexpected end of string: \"%s\"!\n", expr)); 1348 }); 1349 fPosition = expr; 1350 } 1351 1352 1353 Expression::~Expression() 1354 { 1355 delete fTerm; 1356 } 1357 1358 1359 Term* 1360 Expression::ParseEquation(char** expr) 1361 { 1362 skipWhitespace(expr); 1363 1364 bool _not = false; 1365 if (**expr == '!') { 1366 skipWhitespace(expr, 1); 1367 if (**expr != '(') 1368 return NULL; 1369 1370 _not = true; 1371 } 1372 1373 if (**expr == ')') { 1374 // shouldn't be handled here 1375 return NULL; 1376 } else if (**expr == '(') { 1377 skipWhitespace(expr, 1); 1378 1379 Term* term = ParseOr(expr); 1380 1381 skipWhitespace(expr); 1382 1383 if (**expr != ')') { 1384 delete term; 1385 return NULL; 1386 } 1387 1388 // If the term is negated, we just complement the tree, to get 1389 // rid of the not, a.k.a. DeMorgan's Law. 1390 if (_not) 1391 term->Complement(); 1392 1393 skipWhitespace(expr, 1); 1394 1395 return term; 1396 } 1397 1398 Equation* equation = new Equation(expr); 1399 if (equation == NULL || equation->InitCheck() < B_OK) { 1400 delete equation; 1401 return NULL; 1402 } 1403 return equation; 1404 } 1405 1406 1407 Term* 1408 Expression::ParseAnd(char** expr) 1409 { 1410 Term* left = ParseEquation(expr); 1411 if (left == NULL) 1412 return NULL; 1413 1414 while (IsOperator(expr, '&')) { 1415 Term* right = ParseAnd(expr); 1416 Term* newParent = NULL; 1417 1418 if (right == NULL 1419 || (newParent = new Operator(left, OP_AND, right)) == NULL) { 1420 delete left; 1421 delete right; 1422 1423 return NULL; 1424 } 1425 left = newParent; 1426 } 1427 1428 return left; 1429 } 1430 1431 1432 Term* 1433 Expression::ParseOr(char** expr) 1434 { 1435 Term* left = ParseAnd(expr); 1436 if (left == NULL) 1437 return NULL; 1438 1439 while (IsOperator(expr, '|')) { 1440 Term* right = ParseAnd(expr); 1441 Term* newParent = NULL; 1442 1443 if (right == NULL 1444 || (newParent = new Operator(left, OP_OR, right)) == NULL) { 1445 delete left; 1446 delete right; 1447 1448 return NULL; 1449 } 1450 left = newParent; 1451 } 1452 1453 return left; 1454 } 1455 1456 1457 bool 1458 Expression::IsOperator(char** expr, char op) 1459 { 1460 char* string = *expr; 1461 1462 if (*string == op && *(string + 1) == op) { 1463 *expr += 2; 1464 return true; 1465 } 1466 return false; 1467 } 1468 1469 1470 status_t 1471 Expression::InitCheck() 1472 { 1473 if (fTerm == NULL) 1474 return B_BAD_VALUE; 1475 1476 return B_OK; 1477 } 1478 1479 1480 // #pragma mark - 1481 1482 1483 Query::Query(Volume* volume, Expression* expression, uint32 flags) 1484 : 1485 fVolume(volume), 1486 fExpression(expression), 1487 fCurrent(NULL), 1488 fIterator(NULL), 1489 fIndex(volume), 1490 fFlags(flags), 1491 fPort(-1) 1492 { 1493 // If the expression has a valid root pointer, the whole tree has 1494 // already passed the sanity check, so that we don't have to check 1495 // every pointer 1496 if (volume == NULL || expression == NULL || expression->Root() == NULL) 1497 return; 1498 1499 // create index on the stack and delete it afterwards 1500 fExpression->Root()->CalculateScore(fIndex); 1501 fIndex.Unset(); 1502 1503 Rewind(); 1504 1505 if ((fFlags & B_LIVE_QUERY) != 0) 1506 volume->AddQuery(this); 1507 } 1508 1509 1510 Query::~Query() 1511 { 1512 if ((fFlags & B_LIVE_QUERY) != 0) 1513 fVolume->RemoveQuery(this); 1514 } 1515 1516 1517 status_t 1518 Query::Rewind() 1519 { 1520 // free previous stuff 1521 1522 fStack.MakeEmpty(); 1523 1524 delete fIterator; 1525 fIterator = NULL; 1526 fCurrent = NULL; 1527 1528 // put the whole expression on the stack 1529 1530 Stack<Term*> stack; 1531 stack.Push(fExpression->Root()); 1532 1533 Term* term; 1534 while (stack.Pop(&term)) { 1535 if (term->Op() < OP_EQUATION) { 1536 Operator* op = (Operator*)term; 1537 1538 if (op->Op() == OP_OR) { 1539 stack.Push(op->Left()); 1540 stack.Push(op->Right()); 1541 } else { 1542 // For OP_AND, we can use the scoring system to decide which 1543 // path to add 1544 if (op->Right()->Score() > op->Left()->Score()) 1545 stack.Push(op->Right()); 1546 else 1547 stack.Push(op->Left()); 1548 } 1549 } else if (term->Op() == OP_EQUATION 1550 || fStack.Push((Equation*)term) != B_OK) 1551 FATAL(("Unknown term on stack or stack error")); 1552 } 1553 1554 return B_OK; 1555 } 1556 1557 1558 status_t 1559 Query::GetNextEntry(struct dirent* dirent, size_t size) 1560 { 1561 // If we don't have an equation to use yet/anymore, get a new one 1562 // from the stack 1563 while (true) { 1564 if (fIterator == NULL) { 1565 if (!fStack.Pop(&fCurrent) 1566 || fCurrent == NULL) 1567 return B_ENTRY_NOT_FOUND; 1568 1569 status_t status = fCurrent->PrepareQuery(fVolume, fIndex, 1570 &fIterator, fFlags & B_QUERY_NON_INDEXED); 1571 if (status == B_ENTRY_NOT_FOUND) { 1572 // try next equation 1573 continue; 1574 } 1575 1576 if (status != B_OK) 1577 return status; 1578 } 1579 if (fCurrent == NULL) 1580 RETURN_ERROR(B_ERROR); 1581 1582 status_t status = fCurrent->GetNextMatching(fVolume, fIterator, dirent, 1583 size); 1584 if (status != B_OK) { 1585 delete fIterator; 1586 fIterator = NULL; 1587 fCurrent = NULL; 1588 } else { 1589 // only return if we have another entry 1590 return B_OK; 1591 } 1592 } 1593 } 1594 1595 1596 void 1597 Query::SetLiveMode(port_id port, int32 token) 1598 { 1599 fPort = port; 1600 fToken = token; 1601 1602 if ((fFlags & B_LIVE_QUERY) == 0) { 1603 // you can decide at any point to set the live query mode, 1604 // only live queries have to be updated by attribute changes 1605 fFlags |= B_LIVE_QUERY; 1606 fVolume->AddQuery(this); 1607 } 1608 } 1609 1610 1611 void 1612 Query::LiveUpdate(Inode* inode, const char* attribute, int32 type, 1613 const uint8* oldKey, size_t oldLength, const uint8* newKey, 1614 size_t newLength) 1615 { 1616 if (fPort < 0 || fExpression == NULL || attribute == NULL) 1617 return; 1618 1619 // TODO: check if the attribute is part of the query at all... 1620 1621 status_t oldStatus = fExpression->Root()->Match(inode, attribute, type, 1622 oldKey, oldLength); 1623 status_t newStatus = fExpression->Root()->Match(inode, attribute, type, 1624 newKey, newLength); 1625 1626 bool entryCreated = false; 1627 bool stillInQuery = false; 1628 1629 if (oldStatus != MATCH_OK) { 1630 if (newStatus != MATCH_OK) { 1631 // nothing has changed 1632 return; 1633 } 1634 entryCreated = true; 1635 } else if (newStatus != MATCH_OK) { 1636 // entry got removed 1637 entryCreated = false; 1638 } else if ((fFlags & B_ATTR_CHANGE_NOTIFICATION) != 0) { 1639 // The entry stays in the query 1640 stillInQuery = true; 1641 } else 1642 return; 1643 1644 // we may need to get the name of the inode 1645 1646 char nameBuffer[B_FILE_NAME_LENGTH]; 1647 const char* name; 1648 1649 if (strcmp(attribute, "name")) { 1650 if (inode->GetName(nameBuffer) != B_OK) 1651 nameBuffer[0] = '\0'; 1652 name = nameBuffer; 1653 } else { 1654 // a shortcut to prevent having to scan the attribute section 1655 name = (const char*)newKey; 1656 } 1657 1658 // notify query listeners 1659 1660 if (stillInQuery) 1661 notify_query_attr_changed(fPort, fToken, fVolume->ID(), 1662 fVolume->ToVnode(inode->Parent()), name, inode->ID()); 1663 else if (entryCreated) { 1664 notify_query_entry_created(fPort, fToken, fVolume->ID(), 1665 fVolume->ToVnode(inode->Parent()), name, inode->ID()); 1666 } else { 1667 notify_query_entry_removed(fPort, fToken, fVolume->ID(), 1668 fVolume->ToVnode(inode->Parent()), name, inode->ID()); 1669 } 1670 } 1671 1672 1673 void 1674 Query::LiveUpdateRenameMove(Inode* inode, ino_t oldDirectoryID, 1675 const char* oldName, size_t oldLength, ino_t newDirectoryID, 1676 const char* newName, size_t newLength) 1677 { 1678 if (fPort < 0 || fExpression == NULL) 1679 return; 1680 1681 // TODO: check if the attribute is part of the query at all... 1682 1683 status_t oldStatus = fExpression->Root()->Match(inode, "name", 1684 B_STRING_TYPE, (const uint8*)oldName, oldLength); 1685 status_t newStatus = fExpression->Root()->Match(inode, "name", 1686 B_STRING_TYPE, (const uint8*)newName, newLength); 1687 1688 if (oldStatus != MATCH_OK || oldStatus != newStatus) 1689 return; 1690 1691 // The entry stays in the query, notify query listeners about the rename 1692 // or move 1693 1694 notify_query_entry_removed(fPort, fToken, fVolume->ID(), 1695 oldDirectoryID, oldName, inode->ID()); 1696 1697 notify_query_entry_created(fPort, fToken, fVolume->ID(), 1698 newDirectoryID, newName, inode->ID()); 1699 } 1700