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