Changeset 2853 for trunk/src/lib/nt
- Timestamp:
- Aug 31, 2016, 10:56:48 PM (9 years ago)
- Location:
- trunk/src/lib/nt
- Files:
-
- 1 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/lib/nt/kFsCache.h
r2852 r2853 1 1 /* $Id$ */ 2 2 /** @file 3 * ntdircache.c - NT directory content cache.3 * kFsCache.c - NT directory content cache. 4 4 */ 5 5 … … 29 29 */ 30 30 31 32 /********************************************************************************************************************************* 33 * Header Files * 34 *********************************************************************************************************************************/ 31 #ifndef ___lib_nt_kFsCache_h___ 32 #define ___lib_nt_kFsCache_h___ 33 34 35 35 #include <k/kHlp.h> 36 37 #include "nthlp.h"38 36 #include "ntstat.h" 39 40 #include <stdio.h> 41 #include <mbstring.h> 42 #include <wchar.h> 43 //#include <intrin.h> 44 //#include <setjmp.h> 45 //#include <ctype.h> 46 47 48 //#include <Windows.h> 49 //#include <winternl.h> 50 51 52 53 /********************************************************************************************************************************* 54 * Defined Constants And Macros * 55 *********************************************************************************************************************************/ 37 #ifndef NDEBUG 38 # include <stdarg.h> 39 #endif 40 41 56 42 /** @def KFSCACHE_CFG_UTF16 57 43 * Whether to compile in the UTF-16 names support. */ … … 73 59 74 60 /** Special KFSOBJ::uCacheGen number indicating that it does not apply. */ 75 #define KFSWOBJ_CACHE_GEN_IGNORE KU32_MAX 76 77 /** @def KW_LOG 78 * Generic logging. 79 * @param a Argument list for kFsCacheDbgPrintf */ 80 #ifndef NDEBUG 81 # define KFSCACHE_LOG(a) kFsCacheDbgPrintf a 82 #else 83 # define KFSCACHE_LOG(a) do { } while (0) 84 #endif 61 #define KFSOBJ_CACHE_GEN_IGNORE KU32_MAX 85 62 86 63 … … 116 93 117 94 118 /********************************************************************************************************************************* 119 * Structures and Typedefs * 120 *********************************************************************************************************************************/ 95 96 121 97 /** Pointer to a core object. */ 122 98 typedef struct KFSOBJ *PKFSOBJ; … … 151 127 /** Number of references. */ 152 128 KU32 volatile cRefs; 153 /** The cache generation, see KFS WOBJ_CACHE_GEN_IGNORE. */129 /** The cache generation, see KFSOBJ_CACHE_GEN_IGNORE. */ 154 130 KU32 uCacheGen; 155 131 /** The object type, KFSOBJ_TYPE_XXX. */ … … 397 373 398 374 399 /********************************************************************************************************************************* 400 * Internal Functions * 401 *********************************************************************************************************************************/ 402 KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj); 403 404 405 /** 406 * Retains a reference to a cache object, internal version. 407 * 408 * @returns pObj 409 * @param pObj The object. 410 */ 411 K_INLINE PKFSOBJ kFsCacheObjRetainInternal(PKFSOBJ pObj) 412 { 413 KU32 cRefs = ++pObj->cRefs; 414 kHlpAssert(cRefs < 16384); 415 K_NOREF(cRefs); 416 return pObj; 417 } 418 419 420 #ifndef NDEBUG 421 422 /** 423 * Debug printing. 424 * @param pszFormat Debug format string. 425 * @param ... Format argument. 426 */ 427 static void kFsCacheDbgPrintfV(const char *pszFormat, va_list va) 428 { 429 if (1) 430 { 431 DWORD const dwSavedErr = GetLastError(); 432 433 fprintf(stderr, "debug: "); 434 vfprintf(stderr, pszFormat, va); 435 436 SetLastError(dwSavedErr); 437 } 438 } 439 440 441 /** 442 * Debug printing. 443 * @param pszFormat Debug format string. 444 * @param ... Format argument. 445 */ 446 static void kFsCacheDbgPrintf(const char *pszFormat, ...) 447 { 448 if (1) 449 { 450 va_list va; 451 va_start(va, pszFormat); 452 kFsCacheDbgPrintfV(pszFormat, va); 453 va_end(va); 454 } 455 } 456 457 #endif /* !NDEBUG */ 458 459 460 461 /** 462 * Hashes a string. 463 * 464 * @returns 32-bit string hash. 465 * @param pszString String to hash. 466 */ 467 static KU32 kFsCacheStrHash(const char *pszString) 468 { 469 /* This algorithm was created for sdbm (a public-domain reimplementation of 470 ndbm) database library. it was found to do well in scrambling bits, 471 causing better distribution of the keys and fewer splits. it also happens 472 to be a good general hashing function with good distribution. the actual 473 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below 474 is the faster version used in gawk. [there is even a faster, duff-device 475 version] the magic constant 65599 was picked out of thin air while 476 experimenting with different constants, and turns out to be a prime. 477 this is one of the algorithms used in berkeley db (see sleepycat) and 478 elsewhere. */ 479 KU32 uHash = 0; 480 KU32 uChar; 481 while ((uChar = (unsigned char)*pszString++) != 0) 482 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash; 483 return uHash; 484 } 485 486 487 /** 488 * Hashes a string. 489 * 490 * @returns The string length. 491 * @param pszString String to hash. 492 * @param puHash Where to return the 32-bit string hash. 493 */ 494 static KSIZE kFsCacheStrHashEx(const char *pszString, KU32 *puHash) 495 { 496 const char * const pszStart = pszString; 497 KU32 uHash = 0; 498 KU32 uChar; 499 while ((uChar = (unsigned char)*pszString) != 0) 500 { 501 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash; 502 pszString++; 503 } 504 *puHash = uHash; 505 return pszString - pszStart; 506 } 507 508 509 /** 510 * Hashes a string. 511 * 512 * @returns The string length in wchar_t units. 513 * @param pwszString String to hash. 514 * @param puHash Where to return the 32-bit string hash. 515 */ 516 static KSIZE kFsCacheUtf16HashEx(const wchar_t *pwszString, KU32 *puHash) 517 { 518 const wchar_t * const pwszStart = pwszString; 519 KU32 uHash = 0; 520 KU32 uChar; 521 while ((uChar = *pwszString) != 0) 522 { 523 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash; 524 pwszString++; 525 } 526 *puHash = uHash; 527 return pwszString - pwszStart; 528 } 529 530 #if 0 531 532 /** 533 * Converts the given string to unicode. 534 * 535 * @returns Length of the resulting string in wchar_t's. 536 * @param pszSrc The source string. 537 * @param pwszDst The destination buffer. 538 * @param cwcDst The size of the destination buffer in wchar_t's. 539 */ 540 static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst) 541 { 542 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */ 543 KSIZE offDst = 0; 544 while (offDst < cwcDst) 545 { 546 char ch = *pszSrc++; 547 pwszDst[offDst++] = ch; 548 if (!ch) 549 return offDst - 1; 550 kHlpAssert((unsigned)ch < 127); 551 } 552 553 pwszDst[offDst - 1] = '\0'; 554 return offDst; 555 } 556 557 558 /** 559 * Converts the given UTF-16 to a normal string. 560 * 561 * @returns Length of the resulting string. 562 * @param pwszSrc The source UTF-16 string. 563 * @param pszDst The destination buffer. 564 * @param cbDst The size of the destination buffer in bytes. 565 */ 566 static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst) 567 { 568 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */ 569 KSIZE offDst = 0; 570 while (offDst < cbDst) 571 { 572 wchar_t wc = *pwszSrc++; 573 pszDst[offDst++] = (char)wc; 574 if (!wc) 575 return offDst - 1; 576 kHlpAssert((unsigned)wc < 127); 577 } 578 579 pszDst[offDst - 1] = '\0'; 580 return offDst; 581 } 582 583 584 585 /** UTF-16 string length. */ 586 static KSIZE kwUtf16Len(wchar_t const *pwsz) 587 { 588 KSIZE cwc = 0; 589 while (*pwsz != '\0') 590 cwc++, pwsz++; 591 return cwc; 592 } 593 594 /** 595 * Copy out the UTF-16 string following the convension of GetModuleFileName 596 */ 597 static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst) 598 { 599 KSIZE cwcSrc = kwUtf16Len(pwszSrc); 600 if (cwcSrc + 1 <= cwcDst) 601 { 602 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t)); 603 return (DWORD)cwcSrc; 604 } 605 if (cwcDst > 0) 606 { 607 KSIZE cwcDstTmp = cwcDst - 1; 608 pwszDst[cwcDstTmp] = '\0'; 609 if (cwcDstTmp > 0) 610 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp); 611 } 612 SetLastError(ERROR_INSUFFICIENT_BUFFER); 613 return (DWORD)cwcDst; 614 } 615 616 617 /** 618 * Copy out the ANSI string following the convension of GetModuleFileName 619 */ 620 static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst) 621 { 622 KSIZE cchSrc = kHlpStrLen(pszSrc); 623 if (cchSrc + 1 <= cbDst) 624 { 625 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1); 626 return (DWORD)cchSrc; 627 } 628 if (cbDst > 0) 629 { 630 KSIZE cbDstTmp = cbDst - 1; 631 pszDst[cbDstTmp] = '\0'; 632 if (cbDstTmp > 0) 633 kHlpMemCopy(pszDst, pszSrc, cbDstTmp); 634 } 635 SetLastError(ERROR_INSUFFICIENT_BUFFER); 636 return (DWORD)cbDst; 637 } 638 639 640 /** 641 * Normalizes the path so we get a consistent hash. 642 * 643 * @returns status code. 644 * @param pszPath The path. 645 * @param pszNormPath The output buffer. 646 * @param cbNormPath The size of the output buffer. 647 */ 648 static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath) 649 { 650 char *pchSlash; 651 KSIZE cchNormPath; 652 653 /* 654 * We hash these to speed stuff up (nt_fullpath isn't cheap and we're 655 * gonna have many repeat queries and assume nobody do case changes to 656 * anything essential while kmk is running). 657 */ 658 KU32 uHashPath; 659 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath); 660 KU32 const idxHashTab = uHashPath % K_ELEMENTS(g_apFsNormalizedPathsA); 661 PKFSNORMHASHA pHashEntry = g_apFsNormalizedPathsA[idxHashTab]; 662 if (pHashEntry) 663 { 664 do 665 { 666 if ( pHashEntry->uHashPath == uHashPath 667 && pHashEntry->cchPath == cchPath 668 && kHlpMemComp(pHashEntry->pszPath, pszPath, cchPath) == 0) 669 { 670 if (cbNormPath > pHashEntry->cchNormPath) 671 { 672 KFSCACHE_LOG(("kwPathNormalize(%s) - hit\n", pszPath)); 673 kHlpMemCopy(pszNormPath, pHashEntry->szNormPath, pHashEntry->cchNormPath + 1); 674 return 0; 675 } 676 return KERR_BUFFER_OVERFLOW; 677 } 678 pHashEntry = pHashEntry->pNext; 679 } while (pHashEntry); 680 } 681 682 /* 683 * Do it the slow way. 684 */ 685 nt_fullpath(pszPath, pszNormPath, cbNormPath); 686 /** @todo nt_fullpath overflow handling?!?!? */ 687 688 pchSlash = kHlpStrChr(pszNormPath, '/'); 689 while (pchSlash) 690 { 691 *pchSlash = '\\'; 692 pchSlash = kHlpStrChr(pchSlash + 1, '/'); 693 } 694 695 /* 696 * Create a new hash table entry (ignore failures). 697 */ 698 cchNormPath = kHlpStrLen(pszNormPath); 699 if (cchNormPath < KU16_MAX && cchPath < KU16_MAX) 700 { 701 pHashEntry = (PKFSNORMHASHA)kHlpAlloc(sizeof(*pHashEntry) + cchNormPath + 1 + cchPath + 1); 702 if (pHashEntry) 703 { 704 pHashEntry->cchNormPath = (KU16)cchNormPath; 705 pHashEntry->cchPath = (KU16)cchPath; 706 pHashEntry->uHashPath = uHashPath; 707 pHashEntry->pszPath = (char *)kHlpMemCopy(&pHashEntry->szNormPath[cchNormPath + 1], pszPath, cchPath + 1); 708 kHlpMemCopy(pHashEntry->szNormPath, pszNormPath, cchNormPath + 1); 709 710 pHashEntry->pNext = g_apFsNormalizedPathsA[idxHashTab]; 711 g_apFsNormalizedPathsA[idxHashTab] = pHashEntry; 712 } 713 } 714 715 return 0; 716 } 717 718 719 /** 720 * Get the pointer to the filename part of the path. 721 * 722 * @returns Pointer to where the filename starts within the string pointed to by pszFilename. 723 * @returns Pointer to the terminator char if no filename. 724 * @param pszPath The path to parse. 725 */ 726 static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath) 727 { 728 const wchar_t *pwszLast = NULL; 729 for (;;) 730 { 731 wchar_t wc = *pwszPath; 732 #if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS 733 if (wc == '/' || wc == '\\' || wc == ':') 734 { 735 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':') 736 /* nothing */; 737 pwszLast = pwszPath; 738 } 375 /** @def KW_LOG 376 * Generic logging. 377 * @param a Argument list for kFsCacheDbgPrintf */ 378 #ifdef NDEBUG 379 # define KFSCACHE_LOG(a) do { } while (0) 739 380 #else 740 if (wc == '/') 741 { 742 while ((wc = *++pszFilename) == '/') 743 /* betsuni */; 744 pwszLast = pwszPath; 745 } 746 #endif 747 if (!wc) 748 return (wchar_t *)(pwszLast ? pwszLast : pwszPath); 749 pwszPath++; 750 } 751 } 752 753 754 /** 755 * Check if the path leads to a regular file (that exists). 756 * 757 * @returns K_TRUE / K_FALSE 758 * @param pszPath Path to the file to check out. 759 */ 760 static KBOOL kwLdrModuleIsRegularFile(const char *pszPath) 761 { 762 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */ 763 KSIZE cchPath = kHlpStrLen(pszPath); 764 if ( cchPath > 3 765 && pszPath[cchPath - 4] == '.' 766 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D') 767 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L') 768 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') ) 769 { 770 PKFSOBJ pFsObj = kFsCacheLookupA(pszPath); 771 if (pFsObj) 772 { 773 if (!(pFsObj->fAttribs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE))) /* also checks invalid */ 774 return K_TRUE; 775 } 776 } 777 else 778 { 779 BirdStat_T Stat; 780 int rc = birdStatFollowLink(pszPath, &Stat); 781 if (rc == 0) 782 { 783 if (S_ISREG(Stat.st_mode)) 784 return K_TRUE; 785 } 786 } 787 return K_FALSE; 788 } 789 790 791 792 793 794 795 /** 796 * Helper for getting the extension of a UTF-16 path. 797 * 798 * @returns Pointer to the extension or the terminator. 799 * @param pwszPath The path. 800 * @param pcwcExt Where to return the length of the extension. 801 */ 802 static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt) 803 { 804 wchar_t const *pwszName = pwszPath; 805 wchar_t const *pwszExt = NULL; 806 for (;;) 807 { 808 wchar_t const wc = *pwszPath++; 809 if (wc == '.') 810 pwszExt = pwszPath; 811 else if (wc == '/' || wc == '\\' || wc == ':') 812 { 813 pwszName = pwszPath; 814 pwszExt = NULL; 815 } 816 else if (wc == '\0') 817 { 818 if (pwszExt) 819 { 820 *pcwcExt = pwszPath - pwszExt - 1; 821 return pwszExt; 822 } 823 *pcwcExt = 0; 824 return pwszPath - 1; 825 } 826 } 827 } 828 #endif 829 830 831 /** 832 * Looks for '..' in the path. 833 * 834 * @returns K_TRUE if '..' component found, K_FALSE if not. 835 * @param pszPath The path. 836 * @param cchPath The length of the path. 837 */ 838 static KBOOL kFsCacheHasDotDotA(const char *pszPath, KSIZE cchPath) 839 { 840 const char *pchDot = (const char *)kHlpMemChr(pszPath, '.', cchPath); 841 while (pchDot) 842 { 843 if (pchDot[1] != '.') 844 pchDot = (const char *)kHlpMemChr(pchDot + 1, '.', &pszPath[cchPath] - pchDot - 1); 845 else 846 { 847 char ch; 848 if ( (ch = pchDot[2]) == '\0' 849 && IS_SLASH(ch)) 850 { 851 if (pchDot == pszPath) 852 return K_TRUE; 853 ch = pchDot[-1]; 854 if ( IS_SLASH(ch) 855 || ch == ':') 856 return K_TRUE; 857 } 858 pchDot = (const char *)kHlpMemChr(pchDot + 2, '.', &pszPath[cchPath] - pchDot - 2); 859 } 860 } 861 862 return K_FALSE; 863 } 864 865 866 /** 867 * Looks for '..' in the path. 868 * 869 * @returns K_TRUE if '..' component found, K_FALSE if not. 870 * @param pwszPath The path. 871 * @param cwcPath The length of the path (in wchar_t's). 872 */ 873 static KBOOL kFsCacheHasDotDotW(const wchar_t *pwszPath, KSIZE cwcPath) 874 { 875 const wchar_t *pwcDot = wmemchr(pwszPath, '.', cwcPath); 876 while (pwcDot) 877 { 878 if (pwcDot[1] != '.') 879 pwcDot = wmemchr(pwcDot + 1, '.', &pwszPath[cwcPath] - pwcDot - 1); 880 else 881 { 882 wchar_t wch; 883 if ( (wch = pwcDot[2]) == '\0' 884 && IS_SLASH(wch)) 885 { 886 if (pwcDot == pwszPath) 887 return K_TRUE; 888 wch = pwcDot[-1]; 889 if ( IS_SLASH(wch) 890 || wch == ':') 891 return K_TRUE; 892 } 893 pwcDot = wmemchr(pwcDot + 2, '.', &pwszPath[cwcPath] - pwcDot - 2); 894 } 895 } 896 897 return K_FALSE; 898 } 899 900 901 /** 902 * Creates an ANSI hash table entry for the given path. 903 * 904 * @returns The hash table entry or NULL if out of memory. 905 * @param pCache The hash 906 * @param pFsObj The resulting object. 907 * @param pszPath The path. 908 * @param cchPath The length of the path. 909 * @param uHashPath The hash of the path. 910 * @param idxHashTab The hash table index of the path. 911 * @param enmError The lookup error. 912 */ 913 static PKFSHASHA kFsCacheCreatePathHashTabEntryA(PKFSCACHE pCache, PKFSOBJ pFsObj, const char *pszPath, KU32 cchPath, 914 KU32 uHashPath, KU32 idxHashTab, KFSLOOKUPERROR enmError) 915 { 916 PKFSHASHA pHashEntry = (PKFSHASHA)kHlpAlloc(sizeof(*pHashEntry) + cchPath + 1); 917 if (pHashEntry) 918 { 919 pHashEntry->uHashPath = uHashPath; 920 pHashEntry->cchPath = cchPath; 921 pHashEntry->pszPath = (const char *)kHlpMemCopy(pHashEntry + 1, pszPath, cchPath + 1); 922 pHashEntry->pFsObj = pFsObj; 923 pHashEntry->enmError = enmError; 924 if (pFsObj) 925 pHashEntry->uCacheGen = pCache->uGeneration; 926 else if (enmError != KFSLOOKUPERROR_UNSUPPORTED) 927 pHashEntry->uCacheGen = pCache->uGenerationMissing; 928 else 929 pHashEntry->uCacheGen = KFSWOBJ_CACHE_GEN_IGNORE; 930 931 pHashEntry->pNext = pCache->apAnsiPaths[idxHashTab]; 932 pCache->apAnsiPaths[idxHashTab] = pHashEntry; 933 934 pCache->cbAnsiPaths += sizeof(*pHashEntry) + cchPath + 1; 935 pCache->cAnsiPaths++; 936 if (pHashEntry->pNext) 937 pCache->cAnsiPathCollisions++; 938 } 939 return pHashEntry; 940 } 941 942 943 /** 944 * Creates an UTF-16 hash table entry for the given path. 945 * 946 * @returns The hash table entry or NULL if out of memory. 947 * @param pCache The hash 948 * @param pFsObj The resulting object. 949 * @param pwszPath The path. 950 * @param cwcPath The length of the path (in wchar_t's). 951 * @param uHashPath The hash of the path. 952 * @param idxHashTab The hash table index of the path. 953 * @param enmError The lookup error. 954 */ 955 static PKFSHASHW kFsCacheCreatePathHashTabEntryW(PKFSCACHE pCache, PKFSOBJ pFsObj, const wchar_t *pwszPath, KU32 cwcPath, 956 KU32 uHashPath, KU32 idxHashTab, KFSLOOKUPERROR enmError) 957 { 958 PKFSHASHW pHashEntry = (PKFSHASHW)kHlpAlloc(sizeof(*pHashEntry) + (cwcPath + 1) * sizeof(wchar_t)); 959 if (pHashEntry) 960 { 961 pHashEntry->uHashPath = uHashPath; 962 pHashEntry->cwcPath = cwcPath; 963 pHashEntry->pwszPath = (const wchar_t *)kHlpMemCopy(pHashEntry + 1, pwszPath, (cwcPath + 1) * sizeof(wchar_t)); 964 pHashEntry->pFsObj = pFsObj; 965 pHashEntry->enmError = enmError; 966 if (pFsObj) 967 pHashEntry->uCacheGen = pCache->uGeneration; 968 else if (enmError != KFSLOOKUPERROR_UNSUPPORTED) 969 pHashEntry->uCacheGen = pCache->uGenerationMissing; 970 else 971 pHashEntry->uCacheGen = KFSWOBJ_CACHE_GEN_IGNORE; 972 973 pHashEntry->pNext = pCache->apUtf16Paths[idxHashTab]; 974 pCache->apUtf16Paths[idxHashTab] = pHashEntry; 975 976 pCache->cbUtf16Paths += sizeof(*pHashEntry) + (cwcPath + 1) * sizeof(wchar_t); 977 pCache->cUtf16Paths++; 978 if (pHashEntry->pNext) 979 pCache->cAnsiPathCollisions++; 980 } 981 return pHashEntry; 982 } 983 984 985 /** 986 * Links the child in under the parent. 987 * 988 * @returns K_TRUE on success, K_FALSE if out of memory. 989 * @param pParent The parent node. 990 * @param pChild The child node. 991 */ 992 static KBOOL kFsCacheDirAddChild(PKFSCACHE pCache, PKFSDIR pParent, PKFSOBJ pChild, KFSLOOKUPERROR *penmError) 993 { 994 if ((pParent->cChildren % 16) == 0) 995 { 996 void *pvNew = kHlpRealloc(pParent->papChildren, (pParent->cChildren + 16) * sizeof(pParent->papChildren[0])); 997 if (!pvNew) 998 return K_FALSE; 999 pParent->papChildren = (PKFSOBJ *)pvNew; 1000 pCache->cbObjects += 16 * sizeof(pParent->papChildren[0]); 1001 } 1002 pParent->papChildren[pParent->cChildren++] = pChild; 1003 return K_TRUE; 1004 } 1005 1006 1007 /** 1008 * Creates a new cache object. 1009 * 1010 * @returns Pointer (with 1 reference) to the new object. The object will not 1011 * be linked to the parent directory yet. 1012 * 1013 * NULL if we're out of memory. 1014 * 1015 * @param pCache The cache. 1016 * @param pParent The parent directory. 1017 * @param pszName The ANSI name. 1018 * @param cchName The length of the ANSI name. 1019 * @param pwszName The UTF-16 name. 1020 * @param cwcName The length of the UTF-16 name. 1021 * @param pszShortName The ANSI short name, NULL if none. 1022 * @param cchShortName The length of the ANSI short name, 0 if none. 1023 * @param pwszShortName The UTF-16 short name, NULL if none. 1024 * @param cwcShortName The length of the UTF-16 short name, 0 if none. 1025 * @param bObjType The objct type. 1026 * @param penmError Where to explain failures. 1027 */ 1028 static PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent, 1029 char const *pszName, KU16 cchName, wchar_t const *pwszName, KU16 cwcName, 381 # define KFSCACHE_LOG(a) kFsCacheDbgPrintf a 382 void kFsCacheDbgPrintfV(const char *pszFormat, va_list va); 383 void kFsCacheDbgPrintf(const char *pszFormat, ...); 384 #endif 385 386 387 KBOOL kFsCacheDirAddChild(PKFSCACHE pCache, PKFSDIR pParent, PKFSOBJ pChild, KFSLOOKUPERROR *penmError); 388 PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent, 389 char const *pszName, KU16 cchName, wchar_t const *pwszName, KU16 cwcName, 1030 390 #ifdef KFSCACHE_CFG_SHORT_NAMES 1031 char const *pszShortName, KU16 cchShortName, wchar_t const *pwszShortName, KU16 cwcShortName, 1032 #endif 1033 KU8 bObjType, KFSLOOKUPERROR *penmError) 1034 { 1035 /* 1036 * Allocate the object. 1037 */ 1038 KBOOL const fDirish = bObjType != KFSOBJ_TYPE_FILE && bObjType == KFSOBJ_TYPE_OTHER; 1039 KSIZE const cbObj = (fDirish ? sizeof(KFSDIR) : sizeof(KFSOBJ)) 1040 + (cwcName + 1) * sizeof(wchar_t) + cchName + 1 391 char const *pszShortName, KU16 cchShortName, wchar_t const *pwszShortName, KU16 cwcShortName, 392 #endif 393 KU8 bObjType, KFSLOOKUPERROR *penmError); 394 PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName, 1041 395 #ifdef KFSCACHE_CFG_SHORT_NAMES 1042 + (cwcShortName > 0 ? (cwcShortName + 1) * sizeof(wchar_t) + cchShortName + 1 : 0) 1043 #endif 1044 ; 1045 PKFSOBJ pObj = (PKFSOBJ)kHlpAlloc(cbObj); 1046 if (pObj) 1047 { 1048 KU8 *pbExtra = (KU8 *)(pObj + 1); 1049 1050 pCache->cbObjects += cbObj; 1051 pCache->cObjects++; 1052 1053 /* 1054 * Initialize the object. 1055 */ 1056 pObj->u32Magic = KFSOBJ_MAGIC; 1057 pObj->cRefs = 1; 1058 pObj->uCacheGen = bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing; 1059 pObj->bObjType = bObjType; 1060 pObj->fHaveStats = K_FALSE; 1061 pObj->abUnused[0] = K_FALSE; 1062 pObj->abUnused[1] = K_FALSE; 1063 pObj->fFlags = pParent->Obj.fFlags; 1064 pObj->pParent = pParent; 1065 1066 #ifdef KFSCACHE_CFG_UTF16 1067 pObj->cwcParent = pParent->Obj.cwcParent + pParent->Obj.cwcName + !!pParent->Obj.cwcName; 1068 pObj->pwszName = (wchar_t *)kHlpMemCopy(pbExtra, pwszName, cwcName * sizeof(wchar_t)); 1069 pbExtra += cwcName * sizeof(wchar_t); 1070 *pbExtra++ = '\0'; 1071 *pbExtra++ = '\0'; 1072 # ifdef KFSCACHE_CFG_SHORT_NAMES 1073 pObj->cwcShortParent = pParent->Obj.cwcShortParent + pParent->Obj.cwcShortName + !!pParent->Obj.cwcShortName; 1074 if (cwcShortName) 1075 { 1076 pObj->pwszShortName = (wchar_t *)kHlpMemCopy(pbExtra, pwszShortName, cwcShortName * sizeof(wchar_t)); 1077 pbExtra += cwcShortName * sizeof(wchar_t); 1078 *pbExtra++ = '\0'; 1079 *pbExtra++ = '\0'; 1080 } 1081 else 1082 { 1083 pObj->pwszShortName = pObj->pwszName; 1084 pObj->cwcShortName = pObj->cwcName; 1085 } 1086 # endif 1087 #endif 1088 pObj->cchParent = pParent->Obj.cchParent + pParent->Obj.cchName + !!pParent->Obj.cchName; 1089 pObj->pszName = (char *)kHlpMemCopy(pbExtra, pszName, cchName); 1090 pbExtra += cchName; 1091 *pbExtra++ = '\0'; 1092 # ifdef KFSCACHE_CFG_SHORT_NAMES 1093 pObj->cchShortParent = pParent->Obj.cchShortParent + pParent->Obj.cchShortName + !!pParent->Obj.cchShortName; 1094 if (cchShortName) 1095 { 1096 pObj->pszShortName = (char *)kHlpMemCopy(pbExtra, pszShortName, cchShortName); 1097 pbExtra += cchShortName; 1098 *pbExtra++ = '\0'; 1099 } 1100 else 1101 { 1102 pObj->pszShortName = pObj->pszName; 1103 pObj->cchShortName = pObj->cchName; 1104 } 1105 #endif 1106 kHlpAssert(pbExtra - (KU8 *)pObj == cbObj); 1107 1108 /* 1109 * Type specific initilization. 1110 */ 1111 if (fDirish) 1112 { 1113 PKFSDIR pDirObj = (PKFSDIR)pObj; 1114 pDirObj->cChildren = 0; 1115 pDirObj->papChildren = NULL; 1116 pDirObj->cHashTab = 0; 1117 pDirObj->paHashTab = NULL; 1118 pDirObj->hDir = INVALID_HANDLE_VALUE; 1119 pDirObj->uDevNo = pParent->uDevNo; 1120 pDirObj->fPopulated = K_FALSE; 1121 } 1122 } 1123 else 1124 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY; 1125 return pObj; 1126 } 1127 1128 1129 /** 1130 * Creates a new object given wide char names. 1131 * 1132 * This function just converts the paths and calls kFsCacheCreateObject. 1133 * 1134 * 1135 * @returns Pointer (with 1 reference) to the new object. The object will not 1136 * be linked to the parent directory yet. 1137 * 1138 * NULL if we're out of memory. 1139 * 1140 * @param pCache The cache. 1141 * @param pParent The parent directory. 1142 * @param pszName The ANSI name. 1143 * @param cchName The length of the ANSI name. 1144 * @param pwszName The UTF-16 name. 1145 * @param cwcName The length of the UTF-16 name. 1146 * @param pwszShortName The UTF-16 short name, NULL if none. 1147 * @param cwcShortName The length of the UTF-16 short name, 0 if none. 1148 * @param bObjType The objct type. 1149 * @param penmError Where to explain failures. 1150 */ 1151 static PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName, 1152 #ifdef KFSCACHE_CFG_SHORT_NAMES 1153 wchar_t const *pwszShortName, KU32 cwcShortName, 1154 #endif 1155 KU8 bObjType, KFSLOOKUPERROR *penmError) 1156 { 1157 /* Convert names to ANSI first so we know their lengths. */ 1158 char szName[KFSCACHE_CFG_MAX_ANSI_NAME]; 1159 int cchName = WideCharToMultiByte(CP_ACP, 0, pwszName, cwcName, szName, sizeof(szName) - 1, NULL, NULL); 1160 if (cchName >= 0) 1161 { 1162 #ifdef KFSCACHE_CFG_SHORT_NAMES 1163 char szShortName[12*3 + 1]; 1164 int cchShortName = 0; 1165 if ( cwcShortName == 0 1166 || (cchShortName = WideCharToMultiByte(CP_ACP, 0, pwszShortName, cwcShortName, 1167 szShortName, sizeof(szShortName) - 1, NULL, NULL)) > 0) 1168 #endif 1169 { 1170 return kFsCacheCreateObject(pCache, pParent, 1171 szName, cchName, pwszName, cwcName, 1172 #ifdef KFSCACHE_CFG_SHORT_NAMES 1173 szShortName, cchShortName, pwszShortName, cwcShortName, 1174 #endif 1175 bObjType, penmError); 1176 } 1177 } 1178 *penmError = KFSLOOKUPERROR_ANSI_CONVERSION_ERROR; 1179 return NULL; 1180 } 1181 1182 1183 /** 1184 * Creates a missing object. 1185 * 1186 * This is used for caching negative results. 1187 * 1188 * @returns Pointer to the newly created object on success (already linked into 1189 * pParent). No reference. 1190 * 1191 * NULL on failure. 1192 * 1193 * @param pCache The cache. 1194 * @param pParent The parent directory. 1195 * @param pchName The name. 1196 * @param cchName The length of the name. 1197 * @param penmError Where to return failure explanations. 1198 */ 1199 static PKFSOBJ kFsCacheCreateMissingA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName, 1200 KFSLOOKUPERROR *penmError) 1201 { 1202 /* 1203 * Just convert the name to UTF-16 and call kFsCacheCreateObject to do the job. 1204 */ 1205 wchar_t wszName[KFSCACHE_CFG_MAX_PATH]; 1206 int cwcName = MultiByteToWideChar(CP_ACP, 0, pchName, cchName, wszName, KFSCACHE_CFG_MAX_UTF16_NAME - 1); 1207 if (cwcName > 0) 1208 { 1209 /** @todo check that it actually doesn't exists before we add it. We should not 1210 * trust the directory enumeration here, or maybe we should?? */ 1211 1212 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, pParent, pchName, cchName, wszName, cwcName, 1213 #ifdef KFSCACHE_CFG_SHORT_NAMES 1214 NULL, 0, NULL, 0, 1215 #endif 1216 KFSOBJ_TYPE_MISSING, penmError); 1217 if (pMissing) 1218 { 1219 KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError); 1220 kFsCacheObjRelease(pCache, pMissing); 1221 return fRc ? pMissing : NULL; 1222 } 1223 return NULL; 1224 } 1225 *penmError = KFSLOOKUPERROR_UTF16_CONVERSION_ERROR; 1226 return NULL; 1227 } 1228 1229 1230 /** 1231 * Creates a missing object, UTF-16 version. 1232 * 1233 * This is used for caching negative results. 1234 * 1235 * @returns Pointer to the newly created object on success (already linked into 1236 * pParent). No reference. 1237 * 1238 * NULL on failure. 1239 * 1240 * @param pCache The cache. 1241 * @param pParent The parent directory. 1242 * @param pwcName The name. 1243 * @param cwcName The length of the name. 1244 * @param penmError Where to return failure explanations. 1245 */ 1246 static PKFSOBJ kFsCacheCreateMissingW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName, 1247 KFSLOOKUPERROR *penmError) 1248 { 1249 /** @todo check that it actually doesn't exists before we add it. We should not 1250 * trust the directory enumeration here, or maybe we should?? */ 1251 PKFSOBJ pMissing = kFsCacheCreateObjectW(pCache, pParent, pwcName, cwcName, 1252 #ifdef KFSCACHE_CFG_SHORT_NAMES 1253 NULL, 0, 1254 #endif 1255 KFSOBJ_TYPE_MISSING, penmError); 1256 if (pMissing) 1257 { 1258 KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError); 1259 kFsCacheObjRelease(pCache, pMissing); 1260 return fRc ? pMissing : NULL; 1261 } 1262 return NULL; 1263 } 1264 1265 1266 /** 1267 * Does the initial directory populating or refreshes it if it has been 1268 * invalidated. 1269 * 1270 * This assumes the parent directory is opened. 1271 * 1272 * @returns K_TRUE on success, K_FALSE on error. 1273 * @param pCache The cache. 1274 * @param pDir The directory. 1275 * @param penmError Where to store K_FALSE explanation. 1276 */ 1277 static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError) 1278 { 1279 KBOOL fRefreshing = K_FALSE; 1280 /** @todo will have to make this more flexible wrt information classes since 1281 * older windows versions (XP, w2K) might not correctly support the 1282 * ones with file ID on all file systems. */ 1283 #ifdef KFSCACHE_CFG_SHORT_NAMES 1284 MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdBothDirectoryInformation; 1285 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation; 1286 #else 1287 MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdFullDirectoryInformation; 1288 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation; 1289 #endif 1290 MY_NTSTATUS rcNt; 1291 MY_IO_STATUS_BLOCK Ios; 1292 union 1293 { 1294 /* Include the structures for better alignment. */ 1295 MY_FILE_ID_BOTH_DIR_INFORMATION WithId; 1296 MY_FILE_ID_FULL_DIR_INFORMATION NoId; 1297 /* Buffer padding. We're using a 56KB buffer here to avoid size troubles with CIFS and such. */ 1298 KU8 abBuf[56*1024]; 1299 } uBuf; 1300 1301 /* 1302 * Open the directory. 1303 */ 1304 if (pDir->hDir == INVALID_HANDLE_VALUE) 1305 { 1306 MY_OBJECT_ATTRIBUTES ObjAttr; 1307 MY_UNICODE_STRING UniStr; 1308 1309 kHlpAssert(!pDir->fPopulated); 1310 1311 Ios.Information = -1; 1312 Ios.u.Status = -1; 1313 1314 UniStr.Buffer = (wchar_t *)pDir->Obj.pwszName; 1315 UniStr.Length = (USHORT)(pDir->Obj.cwcName * sizeof(wchar_t)); 1316 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t); 1317 1318 kHlpAssertStmtReturn(pDir->Obj.pParent, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE); 1319 kHlpAssertStmtReturn(pDir->Obj.pParent->hDir != INVALID_HANDLE_VALUE, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE); 1320 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pDir->Obj.pParent->hDir, NULL /*pSecAttr*/); 1321 1322 /** @todo FILE_OPEN_REPARSE_POINT? */ 1323 rcNt = g_pfnNtCreateFile(&pDir->hDir, 1324 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE, 1325 &ObjAttr, 1326 &Ios, 1327 NULL, /*cbFileInitialAlloc */ 1328 FILE_ATTRIBUTE_NORMAL, 1329 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1330 FILE_OPEN, 1331 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, 1332 NULL, /*pEaBuffer*/ 1333 0); /*cbEaBuffer*/ 1334 if (MY_NT_SUCCESS(rcNt)) 1335 { /* likely */ } 1336 else 1337 { 1338 pDir->hDir = INVALID_HANDLE_VALUE; 1339 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR; 1340 return K_FALSE; 1341 } 1342 } 1343 else if (pDir->fPopulated) 1344 { 1345 /** @todo refreshing directories. */ 1346 __debugbreak(); 1347 fRefreshing = K_TRUE; 1348 } 1349 1350 1351 /* 1352 * Enumerate the directory content. 1353 */ 1354 Ios.Information = -1; 1355 Ios.u.Status = -1; 1356 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir, 1357 NULL, /* hEvent */ 1358 NULL, /* pfnApcComplete */ 1359 NULL, /* pvApcCompleteCtx */ 1360 &Ios, 1361 &uBuf, 1362 sizeof(uBuf), 1363 enmInfoClass, 1364 FALSE, /* fReturnSingleEntry */ 1365 NULL, /* Filter / restart pos. */ 1366 TRUE); /* fRestartScan */ 1367 while (MY_NT_SUCCESS(rcNt)) 1368 { 1369 /* 1370 * Process the entries in the buffer. 1371 */ 1372 KSIZE offBuf = 0; 1373 for (;;) 1374 { 1375 union 1376 { 1377 KU8 *pb; 1378 #ifdef KFSCACHE_CFG_SHORT_NAMES 1379 MY_FILE_ID_BOTH_DIR_INFORMATION *pWithId; 1380 MY_FILE_BOTH_DIR_INFORMATION *pNoId; 1381 #else 1382 MY_FILE_ID_FULL_DIR_INFORMATION *pWithId; 1383 MY_FILE_FULL_DIR_INFORMATION *pNoId; 1384 #endif 1385 } uPtr; 1386 PKFSOBJ pCur; 1387 KU32 offNext; 1388 KU32 cbMinCur; 1389 wchar_t *pwszFilename; 1390 1391 /* ASSUME only the FileName member differs between the two structures. */ 1392 uPtr.pb = &uBuf.abBuf[offBuf]; 1393 if (enmInfoClass == enmInfoClassWithId) 1394 { 1395 pwszFilename = &uPtr.pWithId->FileName[0]; 1396 cbMinCur = (KU32)((uintptr_t)&uPtr.pWithId->FileName[0] - (uintptr_t)uPtr.pWithId); 1397 cbMinCur += uPtr.pNoId->FileNameLength; 1398 } 1399 else 1400 { 1401 pwszFilename = &uPtr.pNoId->FileName[0]; 1402 cbMinCur = (KU32)((uintptr_t)&uPtr.pNoId->FileName[0] - (uintptr_t)uPtr.pNoId); 1403 cbMinCur += uPtr.pNoId->FileNameLength; 1404 } 1405 1406 /* 1407 * Create the entry (not linked yet). 1408 */ 1409 pCur = kFsCacheCreateObjectW(pCache, pDir, pwszFilename, uPtr.pNoId->FileNameLength / sizeof(wchar_t), 1410 #ifdef KFSCACHE_CFG_SHORT_NAMES 1411 uPtr.pNoId->ShortName, uPtr.pNoId->ShortNameLength / sizeof(wchar_t), 1412 #endif 1413 uPtr.pNoId->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR 1414 : uPtr.pNoId->FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT) 1415 ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE, 1416 penmError); 1417 if (!pCur) 1418 return K_FALSE; 1419 kHlpAssert(pCur->cRefs == 1); 1420 1421 #ifdef KFSCACHE_CFG_SHORT_NAMES 1422 if (enmInfoClass == enmInfoClassWithId) 1423 birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName); 1424 else 1425 birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName); 1426 #else 1427 if (enmInfoClass == enmInfoClassWithId) 1428 birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName); 1429 else 1430 birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName); 1431 #endif 1432 pCur->Stats.st_dev = pDir->uDevNo; 1433 1434 /* 1435 * If we're updating we have to check the data. 1436 */ 1437 if (fRefreshing) 1438 { 1439 __debugbreak(); 1440 } 1441 1442 /* 1443 * If we've still got pCur, add it to the directory. 1444 */ 1445 if (pCur) 1446 { 1447 KBOOL fRc = kFsCacheDirAddChild(pCache, pDir, pCur, penmError); 1448 kFsCacheObjRelease(pCache, pCur); 1449 if (fRc) 1450 { /* likely */ } 1451 else 1452 return K_FALSE; 1453 } 1454 1455 /* 1456 * Advance. 1457 */ 1458 offNext = uPtr.pNoId->NextEntryOffset; 1459 if ( offNext >= cbMinCur 1460 && offNext < sizeof(uBuf)) 1461 offBuf += offNext; 1462 else 1463 break; 1464 } 1465 1466 /* 1467 * Read the next chunk. 1468 */ 1469 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir, 1470 NULL, /* hEvent */ 1471 NULL, /* pfnApcComplete */ 1472 NULL, /* pvApcCompleteCtx */ 1473 &Ios, 1474 &uBuf, 1475 sizeof(uBuf), 1476 enmInfoClass, 1477 FALSE, /* fReturnSingleEntry */ 1478 NULL, /* Filter / restart pos. */ 1479 FALSE); /* fRestartScan */ 1480 } 1481 1482 if (rcNt == MY_STATUS_NO_MORE_FILES) 1483 return K_TRUE; 1484 kHlpAssertMsgFailed(("%#x\n", rcNt)); 1485 *penmError = KFSLOOKUPERROR_DIR_READ_ERROR; 1486 return K_TRUE; 1487 } 1488 1489 1490 static KBOOL kFsCacheRefreshMissing(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError) 1491 { 1492 return K_TRUE; 1493 } 1494 1495 1496 static KBOOL kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError) 1497 { 1498 return K_TRUE; 1499 } 1500 1501 1502 static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError) 1503 { 1504 return K_FALSE; 1505 } 1506 1507 1508 1509 /** 1510 * Looks up a drive letter. 1511 * 1512 * Will enter the drive if necessary. 1513 * 1514 * @returns Pointer to the root directory of the drive or an update-to-date 1515 * missing node. 1516 * @param pCache The cache. 1517 * @param chLetter The uppercased drive letter. 1518 * @param penmError Where to return details as to why the lookup 1519 * failed. 1520 */ 1521 static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError) 1522 { 1523 KU32 const uHash = chLetter - 'A'; 1524 KU32 cLeft; 1525 PKFSOBJ *ppCur; 1526 1527 MY_UNICODE_STRING NtPath; 1528 wchar_t wszTmp[8]; 1529 MY_NTSTATUS rcNt; 1530 char szTmp[4]; 1531 1532 /* 1533 * Custom drive letter hashing. 1534 */ 1535 if (pCache->RootDir.paHashTab) 1536 { 1537 /** @todo PKFSOBJHASH pHash = */ 1538 } 1539 1540 /* 1541 * Special cased lookup. 1542 */ 1543 cLeft = pCache->RootDir.cChildren; 1544 ppCur = pCache->RootDir.papChildren; 1545 while (cLeft-- > 0) 1546 { 1547 PKFSOBJ pCur = *ppCur++; 1548 if ( pCur->cchName == 2 1549 && pCur->pszName[0] == chLetter 1550 && pCur->pszName[1] == ':') 1551 { 1552 if (pCur->bObjType == KFSOBJ_TYPE_DIR) 1553 return pCur; 1554 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING); 1555 if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError)) 1556 return pCur; 1557 return NULL; 1558 } 1559 } 1560 1561 /* 1562 * Need to add it. We always keep the drive letters open for the benefit 1563 * of kFsCachePopuplateOrRefreshDir and others. 1564 */ 1565 wszTmp[0] = szTmp[0] = chLetter; 1566 wszTmp[1] = szTmp[1] = ':'; 1567 wszTmp[2] = szTmp[2] = '\\'; 1568 wszTmp[3] = '.'; 1569 wszTmp[4] = '\0'; 1570 szTmp[2] = '\0'; 1571 1572 NtPath.Buffer = NULL; 1573 NtPath.Length = 0; 1574 NtPath.MaximumLength = 0; 1575 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL)) 1576 { 1577 HANDLE hDir; 1578 rcNt = birdOpenFileUniStr(&NtPath, 1579 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE, 1580 FILE_ATTRIBUTE_NORMAL, 1581 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 1582 FILE_OPEN, 1583 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, 1584 OBJ_CASE_INSENSITIVE, 1585 &hDir); 1586 birdFreeNtPath(&NtPath); 1587 if (MY_NT_SUCCESS(rcNt)) 1588 { 1589 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2, 1590 #ifdef KFSCACHE_CFG_SHORT_NAMES 1591 NULL, 0, NULL, 0, 1592 #endif 1593 KFSOBJ_TYPE_DIR, penmError); 1594 if (pDir) 1595 { 1596 /* 1597 * We need a little bit of extra info for a drive root. These things are typically 1598 * inherited by subdirectories down the tree, so, we do it all here for till that changes. 1599 */ 1600 union 1601 { 1602 MY_FILE_FS_VOLUME_INFORMATION VolInfo; 1603 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo; 1604 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512]; 1605 } uBuf; 1606 MY_IO_STATUS_BLOCK Ios; 1607 KBOOL fRc; 1608 1609 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE); 1610 pDir->hDir = hDir; 1611 1612 /* Get the device number. */ 1613 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo); 1614 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt)); 1615 1616 /* Get the file system. */ 1617 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME); 1618 Ios.Information = -1; 1619 Ios.u.Status = -1; 1620 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf), 1621 MyFileFsAttributeInformation); 1622 if (MY_NT_SUCCESS(rcNt)) 1623 rcNt = Ios.u.Status; 1624 if (MY_NT_SUCCESS(rcNt)) 1625 { 1626 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N' 1627 && uBuf.FsAttrInfo.FileSystemName[1] == 'T' 1628 && uBuf.FsAttrInfo.FileSystemName[2] == 'F' 1629 && uBuf.FsAttrInfo.FileSystemName[3] == 'S' 1630 && uBuf.FsAttrInfo.FileSystemName[4] == '\0') 1631 { 1632 DWORD dwDriveType = GetDriveTypeW(wszTmp); 1633 if ( dwDriveType == DRIVE_FIXED 1634 || dwDriveType == DRIVE_RAMDISK) 1635 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME; 1636 } 1637 } 1638 1639 /* 1640 * Link the new drive letter into the root dir. 1641 */ 1642 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError); 1643 kFsCacheObjRelease(pCache, &pDir->Obj); 1644 return fRc ? &pDir->Obj : NULL; 1645 } 1646 1647 g_pfnNtClose(hDir); 1648 return NULL; 1649 } 1650 1651 /* Assume it doesn't exist if this happens... This may be a little to 1652 restrictive wrt status code checks. */ 1653 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND 1654 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND 1655 || rcNt == MY_STATUS_OBJECT_PATH_INVALID 1656 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD, 1657 ("%#x\n", rcNt), 1658 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR, 1659 NULL); 1660 } 1661 else 1662 { 1663 kHlpAssertFailed(); 1664 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY; 1665 return NULL; 1666 } 1667 1668 /* 1669 * Maybe create a missing entry. 1670 */ 1671 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS) 1672 { 1673 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2, 1674 #ifdef KFSCACHE_CFG_SHORT_NAMES 1675 NULL, 0, NULL, 0, 1676 #endif 1677 KFSOBJ_TYPE_MISSING, penmError); 1678 if (pMissing) 1679 { 1680 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError); 1681 kFsCacheObjRelease(pCache, pMissing); 1682 return fRc ? pMissing : NULL; 1683 } 1684 } 1685 else 1686 { 1687 /** @todo this isn't necessary correct for a root spec. */ 1688 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; 1689 } 1690 return NULL; 1691 } 1692 1693 1694 /** 1695 * Look up a child node, ANSI version. 1696 * 1697 * @returns Pointer to the child if found, NULL if not. 1698 * @param pCache The cache. 1699 * @param pParent The parent directory to search. 1700 * @param pchName The child name to search for (not terminated). 1701 * @param cchName The length of the child name. 1702 */ 1703 static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName) 1704 { 1705 /* Check for '.' first. */ 1706 if (cchName != 1 || *pchName != '.') 1707 { 1708 KU32 cLeft; 1709 PKFSOBJ *ppCur; 1710 1711 if (pParent->paHashTab != NULL) 1712 { 1713 /** @todo directory hash table lookup. */ 1714 } 1715 1716 /* Linear search. */ 1717 cLeft = pParent->cChildren; 1718 ppCur = pParent->papChildren; 1719 while (cLeft-- > 0) 1720 { 1721 PKFSOBJ pCur = *ppCur++; 1722 if ( ( pCur->cchName == cchName 1723 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0) 1724 #ifdef KFSCACHE_CFG_SHORT_NAMES 1725 || ( pCur->cchShortName == cchName 1726 && pCur->pszShortName != pCur->pszName 1727 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0) 1728 #endif 1729 ) 1730 return pCur; 1731 } 1732 return NULL; 1733 } 1734 return &pParent->Obj; 1735 } 1736 1737 1738 /** 1739 * For use when kFsCacheIAreEqualW hit's something non-trivial. 1740 * 1741 * @returns K_TRUE if equal, K_FALSE if different. 1742 * @param pwcName1 The first string. 1743 * @param pwcName2 The second string. 1744 * @param cwcName The length of the two strings (in wchar_t's). 1745 */ 1746 KBOOL kFsCacheIAreEqualSlowW(const wchar_t *pwcName1, const wchar_t *pwcName2, KU16 cwcName) 1747 { 1748 MY_UNICODE_STRING UniStr1 = { cwcName * sizeof(wchar_t), cwcName * sizeof(wchar_t), (wchar_t *)pwcName1 }; 1749 MY_UNICODE_STRING UniStr2 = { cwcName * sizeof(wchar_t), cwcName * sizeof(wchar_t), (wchar_t *)pwcName2 }; 1750 return g_pfnRtlEqualUnicodeString(&UniStr1, &UniStr2, TRUE /*fCaseInsensitive*/); 1751 } 1752 1753 1754 /** 1755 * Compares two UTF-16 strings in a case-insensitive fashion. 1756 * 1757 * You would think we should be using _wscnicmp here instead, however it is 1758 * locale dependent and defaults to ASCII upper/lower handling setlocale hasn't 1759 * been called. 1760 * 1761 * @returns K_TRUE if equal, K_FALSE if different. 1762 * @param pwcName1 The first string. 1763 * @param pwcName2 The second string. 1764 * @param cwcName The length of the two strings (in wchar_t's). 1765 */ 1766 K_INLINE KBOOL kFsCacheIAreEqualW(const wchar_t *pwcName1, const wchar_t *pwcName2, KU32 cwcName) 1767 { 1768 while (cwcName > 0) 1769 { 1770 wchar_t wc1 = *pwcName1; 1771 wchar_t wc2 = *pwcName2; 1772 if (wc1 == wc2) 1773 { /* not unlikely */ } 1774 else if ( (KU16)wc1 < (KU16)0xc0 /* U+00C0 is the first upper/lower letter after 'z'. */ 1775 && (KU16)wc2 < (KU16)0xc0) 1776 { 1777 /* ASCII upper case. */ 1778 if ((KU16)wc1 - (KU16)0x61 < (KU16)26) 1779 wc1 &= ~(wchar_t)0x20; 1780 if ((KU16)wc2 - (KU16)0x61 < (KU16)26) 1781 wc2 &= ~(wchar_t)0x20; 1782 if (wc1 != wc2) 1783 return K_FALSE; 1784 } 1785 else 1786 return kFsCacheIAreEqualSlowW(pwcName1, pwcName2, (KU16)cwcName); 1787 1788 pwcName2++; 1789 pwcName1++; 1790 cwcName--; 1791 } 1792 1793 return K_TRUE; 1794 } 1795 1796 1797 /** 1798 * Look up a child node, UTF-16 version. 1799 * 1800 * @returns Pointer to the child if found, NULL if not. 1801 * @param pCache The cache. 1802 * @param pParent The parent directory to search. 1803 * @param pwcName The child name to search for (not terminated). 1804 * @param cwcName The length of the child name (in wchar_t's). 1805 */ 1806 static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName) 1807 { 1808 /* Check for '.' first. */ 1809 if (cwcName != 1 || *pwcName != '.') 1810 { 1811 KU32 cLeft; 1812 PKFSOBJ *ppCur; 1813 1814 if (pParent->paHashTab != NULL) 1815 { 1816 /** @todo directory hash table lookup. */ 1817 } 1818 1819 /* Linear search. */ 1820 cLeft = pParent->cChildren; 1821 ppCur = pParent->papChildren; 1822 while (cLeft-- > 0) 1823 { 1824 PKFSOBJ pCur = *ppCur++; 1825 if ( ( pCur->cwcName == cwcName 1826 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName)) 1827 #ifdef KFSCACHE_CFG_SHORT_NAMES 1828 || ( pCur->cwcShortName == cwcName 1829 && pCur->pwszShortName != pCur->pwszName 1830 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName)) 1831 #endif 1832 ) 1833 return pCur; 1834 } 1835 return NULL; 1836 } 1837 return &pParent->Obj; 1838 } 1839 1840 1841 /** 1842 * Looks up a UNC share, ANSI version. 1843 * 1844 * We keep both the server and share in the root directory entry. This means we 1845 * have to clean up the entry name before we can insert it. 1846 * 1847 * @returns Pointer to the share root directory or an update-to-date missing 1848 * node. 1849 * @param pCache The cache. 1850 * @param pszPath The path. 1851 * @param poff Where to return the root dire. 1852 * @param penmError Where to return details as to why the lookup 1853 * failed. 1854 */ 1855 static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError) 1856 { 1857 #if 0 /* later */ 1858 KU32 offStartServer; 1859 KU32 offEndServer; 1860 KU32 offStartShare; 1861 1862 KU32 offEnd = 2; 1863 while (IS_SLASH(pszPath[offEnd])) 1864 offEnd++; 1865 1866 offStartServer = offEnd; 1867 while ( (ch = pszPath[offEnd]) != '\0' 1868 && !IS_SLASH(ch)) 1869 offEnd++; 1870 offEndServer = offEnd; 1871 1872 if (ch != '\0') 1873 { /* likely */ } 1874 else 1875 { 1876 *penmError = KFSLOOKUPERROR_NOT_FOUND; 1877 return NULL; 1878 } 1879 1880 while (IS_SLASH(pszPath[offEnd])) 1881 offEnd++; 1882 offStartServer = offEnd; 1883 while ( (ch = pszPath[offEnd]) != '\0' 1884 && !IS_SLASH(ch)) 1885 offEnd++; 1886 #endif 1887 *penmError = KFSLOOKUPERROR_UNSUPPORTED; 1888 return NULL; 1889 } 1890 1891 1892 /** 1893 * Looks up a UNC share, UTF-16 version. 1894 * 1895 * We keep both the server and share in the root directory entry. This means we 1896 * have to clean up the entry name before we can insert it. 1897 * 1898 * @returns Pointer to the share root directory or an update-to-date missing 1899 * node. 1900 * @param pCache The cache. 1901 * @param pwszPath The path. 1902 * @param poff Where to return the root dire. 1903 * @param penmError Where to return details as to why the lookup 1904 * failed. 1905 */ 1906 static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError) 1907 { 1908 #if 0 /* later */ 1909 KU32 offStartServer; 1910 KU32 offEndServer; 1911 KU32 offStartShare; 1912 1913 KU32 offEnd = 2; 1914 while (IS_SLASH(pwszPath[offEnd])) 1915 offEnd++; 1916 1917 offStartServer = offEnd; 1918 while ( (ch = pwszPath[offEnd]) != '\0' 1919 && !IS_SLASH(ch)) 1920 offEnd++; 1921 offEndServer = offEnd; 1922 1923 if (ch != '\0') 1924 { /* likely */ } 1925 else 1926 { 1927 *penmError = KFSLOOKUPERROR_NOT_FOUND; 1928 return NULL; 1929 } 1930 1931 while (IS_SLASH(pwszPath[offEnd])) 1932 offEnd++; 1933 offStartServer = offEnd; 1934 while ( (ch = pwszPath[offEnd]) != '\0' 1935 && !IS_SLASH(ch)) 1936 offEnd++; 1937 #endif 1938 *penmError = KFSLOOKUPERROR_UNSUPPORTED; 1939 return NULL; 1940 } 1941 1942 1943 /** 1944 * Walk the file system tree for the given absolute path, entering it into the 1945 * hash table. 1946 * 1947 * This will create any missing nodes while walking. 1948 * 1949 * The caller will have to do the path hash table insertion of the result. 1950 * 1951 * @returns Pointer to the tree node corresponding to @a pszPath. 1952 * NULL on lookup failure, see @a penmError for details. 1953 * @param pCache The cache. 1954 * @param pszPath The path to walk. 1955 * @param cchPath The length of the path. 1956 * @param penmError Where to return details as to why the lookup 1957 * failed. 1958 */ 1959 static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError) 1960 { 1961 PKFSDIR pParent = &pCache->RootDir; 1962 PKFSOBJ pChild; 1963 KU32 off; 1964 KU32 cchSlashes; 1965 KU32 offEnd; 1966 1967 KFSCACHE_LOG(("kFsCacheLookupAbsoluteA(%s)\n", pszPath)); 1968 1969 /* 1970 * The root "directory" needs special handling, so we keep it outside the 1971 * main search loop. (Special: Cannot enumerate it, UNCs, ++.) 1972 */ 1973 cchSlashes = 0; 1974 off = 0; 1975 if ( pszPath[1] == ':' 1976 && IS_ALPHA(pszPath[0])) 1977 { 1978 /* Drive letter. */ 1979 offEnd = 2; 1980 kHlpAssert(IS_SLASH(pszPath[2])); 1981 pChild = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError); 1982 } 1983 else if ( IS_SLASH(pszPath[0]) 1984 && IS_SLASH(pszPath[1]) ) 1985 pChild = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError); 1986 else 1987 { 1988 *penmError = KFSLOOKUPERROR_UNSUPPORTED; 1989 return NULL; 1990 } 1991 if (pChild) 1992 { /* likely */ } 1993 else 1994 return NULL; 1995 1996 /* Count slashes trailing the root spec. */ 1997 if (offEnd < cchPath) 1998 { 1999 kHlpAssert(IS_SLASH(pszPath[offEnd])); 2000 do 2001 cchSlashes++; 2002 while (IS_SLASH(pszPath[offEnd + cchSlashes])); 2003 } 2004 2005 /* Done already? */ 2006 if (offEnd >= cchPath) 2007 { 2008 if ( pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2009 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing) 2010 || kFsCacheRefreshObj(pCache, pChild, penmError)) 2011 return kFsCacheObjRetainInternal(pChild); 2012 return NULL; 2013 } 2014 2015 /* Check that we've got a valid result and not a cached negative one. */ 2016 if (pChild->bObjType == KFSOBJ_TYPE_DIR) 2017 { /* likely */ } 2018 else 2019 { 2020 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING); 2021 kHlpAssert(pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing); 2022 return pChild; 2023 } 2024 2025 /* Next component. */ 2026 pParent = (PKFSDIR)pChild; 2027 off = offEnd + cchSlashes; 2028 2029 2030 /* 2031 * Walk loop. 2032 */ 2033 for (;;) 2034 { 2035 /* 2036 * Find the end of the component, counting trailing slashes. 2037 */ 2038 char ch; 2039 cchSlashes = 0; 2040 offEnd = off + 1; 2041 while ((ch = pszPath[offEnd]) != '\0') 2042 { 2043 if (!IS_SLASH(ch)) 2044 offEnd++; 2045 else 2046 { 2047 do 2048 cchSlashes++; 2049 while (IS_SLASH(pszPath[offEnd + cchSlashes])); 2050 break; 2051 } 2052 } 2053 2054 /* 2055 * Do we need to populate or refresh this directory first? 2056 */ 2057 if ( pParent->fPopulated 2058 && ( pParent->Obj.uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2059 || pParent->Obj.uCacheGen == pCache->uGeneration) ) 2060 { /* likely */ } 2061 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError)) 2062 { /* likely */ } 2063 else 2064 return NULL; 2065 2066 /* 2067 * Search the current node for the name. 2068 * 2069 * If we don't find it, we may insert a missing node depending on 2070 * the cache configuration. 2071 */ 2072 pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off); 2073 if (pChild != NULL) 2074 { /* probably likely */ } 2075 else 2076 { 2077 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS) 2078 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError); 2079 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath) 2080 { 2081 if (pChild) 2082 return pChild; 2083 *penmError = KFSLOOKUPERROR_NOT_FOUND; 2084 } 2085 else 2086 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; 2087 return NULL; 2088 } 2089 2090 /* Advance off and check if we're done already. */ 2091 off = offEnd + cchSlashes; 2092 if ( cchSlashes == 0 2093 || off >= cchPath) 2094 { 2095 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING 2096 || pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2097 || pChild->uCacheGen == pCache->uGenerationMissing 2098 || kFsCacheRefreshMissing(pCache, pChild, penmError) ) 2099 { /* likely */ } 2100 else 2101 return NULL; 2102 return pChild; 2103 } 2104 2105 /* 2106 * Check that it's a directory. If a missing entry, we may have to 2107 * refresh it and re-examin it. 2108 */ 2109 if (pChild->bObjType == KFSOBJ_TYPE_DIR) 2110 pParent = (PKFSDIR)pChild; 2111 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING) 2112 { 2113 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR; 2114 return NULL; 2115 } 2116 else if ( pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2117 || pChild->uCacheGen == pCache->uGenerationMissing) 2118 { 2119 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; 2120 return NULL; 2121 } 2122 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError)) 2123 pParent = (PKFSDIR)pChild; 2124 else 2125 return NULL; 2126 } 2127 2128 return NULL; 2129 } 2130 2131 2132 /** 2133 * Walk the file system tree for the given absolute path, UTF-16 version. 2134 * 2135 * This will create any missing nodes while walking. 2136 * 2137 * The caller will have to do the path hash table insertion of the result. 2138 * 2139 * @returns Pointer to the tree node corresponding to @a pszPath. 2140 * NULL on lookup failure, see @a penmError for details. 2141 * @param pCache The cache. 2142 * @param pwszPath The path to walk. 2143 * @param cwcPath The length of the path (in wchar_t's). 2144 * @param penmError Where to return details as to why the lookup 2145 * failed. 2146 */ 2147 static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KFSLOOKUPERROR *penmError) 2148 { 2149 PKFSDIR pParent = &pCache->RootDir; 2150 PKFSOBJ pChild; 2151 KU32 off; 2152 KU32 cwcSlashes; 2153 KU32 offEnd; 2154 2155 KFSCACHE_LOG(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath)); 2156 2157 /* 2158 * The root "directory" needs special handling, so we keep it outside the 2159 * main search loop. (Special: Cannot enumerate it, UNCs, ++.) 2160 */ 2161 cwcSlashes = 0; 2162 off = 0; 2163 if ( pwszPath[1] == ':' 2164 && IS_ALPHA(pwszPath[0])) 2165 { 2166 /* Drive letter. */ 2167 offEnd = 2; 2168 kHlpAssert(IS_SLASH(pwszPath[2])); 2169 pChild = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError); 2170 } 2171 else if ( IS_SLASH(pwszPath[0]) 2172 && IS_SLASH(pwszPath[1]) ) 2173 pChild = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError); 2174 else 2175 { 2176 *penmError = KFSLOOKUPERROR_UNSUPPORTED; 2177 return NULL; 2178 } 2179 if (pChild) 2180 { /* likely */ } 2181 else 2182 return NULL; 2183 2184 /* Count slashes trailing the root spec. */ 2185 if (offEnd < cwcPath) 2186 { 2187 kHlpAssert(IS_SLASH(pwszPath[offEnd])); 2188 do 2189 cwcSlashes++; 2190 while (IS_SLASH(pwszPath[offEnd + cwcSlashes])); 2191 } 2192 2193 /* Done already? */ 2194 if (offEnd >= cwcPath) 2195 { 2196 if ( pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2197 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing) 2198 || kFsCacheRefreshObj(pCache, pChild, penmError)) 2199 return kFsCacheObjRetainInternal(pChild); 2200 return NULL; 2201 } 2202 2203 /* Check that we've got a valid result and not a cached negative one. */ 2204 if (pChild->bObjType == KFSOBJ_TYPE_DIR) 2205 { /* likely */ } 2206 else 2207 { 2208 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING); 2209 kHlpAssert(pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing); 2210 return pChild; 2211 } 2212 2213 /* Next component. */ 2214 pParent = (PKFSDIR)pChild; 2215 off = offEnd + cwcSlashes; 2216 2217 2218 /* 2219 * Walk loop. 2220 */ 2221 for (;;) 2222 { 2223 /* 2224 * Find the end of the component, counting trailing slashes. 2225 */ 2226 wchar_t wc; 2227 cwcSlashes = 0; 2228 offEnd = off + 1; 2229 while ((wc = pwszPath[offEnd]) != '\0') 2230 { 2231 if (!IS_SLASH(wc)) 2232 offEnd++; 2233 else 2234 { 2235 do 2236 cwcSlashes++; 2237 while (IS_SLASH(pwszPath[offEnd + cwcSlashes])); 2238 break; 2239 } 2240 } 2241 2242 /* 2243 * Do we need to populate or refresh this directory first? 2244 */ 2245 if ( pParent->fPopulated 2246 && ( pParent->Obj.uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2247 || pParent->Obj.uCacheGen == pCache->uGeneration) ) 2248 { /* likely */ } 2249 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError)) 2250 { /* likely */ } 2251 else 2252 return NULL; 2253 2254 /* 2255 * Search the current node for the name. 2256 * 2257 * If we don't find it, we may insert a missing node depending on 2258 * the cache configuration. 2259 */ 2260 pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off); 2261 if (pChild != NULL) 2262 { /* probably likely */ } 2263 else 2264 { 2265 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS) 2266 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError); 2267 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath) 2268 { 2269 if (pChild) 2270 return pChild; 2271 *penmError = KFSLOOKUPERROR_NOT_FOUND; 2272 } 2273 else 2274 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; 2275 return NULL; 2276 } 2277 2278 /* Advance off and check if we're done already. */ 2279 off = offEnd + cwcSlashes; 2280 if ( cwcSlashes == 0 2281 || off >= cwcPath) 2282 { 2283 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING 2284 || pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2285 || pChild->uCacheGen == pCache->uGenerationMissing 2286 || kFsCacheRefreshMissing(pCache, pChild, penmError) ) 2287 { /* likely */ } 2288 else 2289 return NULL; 2290 return pChild; 2291 } 2292 2293 /* 2294 * Check that it's a directory. If a missing entry, we may have to 2295 * refresh it and re-examin it. 2296 */ 2297 if (pChild->bObjType == KFSOBJ_TYPE_DIR) 2298 pParent = (PKFSDIR)pChild; 2299 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING) 2300 { 2301 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR; 2302 return NULL; 2303 } 2304 else if ( pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2305 || pChild->uCacheGen == pCache->uGenerationMissing) 2306 { 2307 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; 2308 return NULL; 2309 } 2310 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError)) 2311 pParent = (PKFSDIR)pChild; 2312 else 2313 return NULL; 2314 } 2315 2316 return NULL; 2317 } 2318 2319 2320 /** 2321 * This deals with paths that are relative and paths that contains '..' 2322 * elements, ANSI version. 2323 * 2324 * @returns Pointer to object corresponding to @a pszPath on success. 2325 * NULL if this isn't a path we care to cache. 2326 * 2327 * @param pCache The cache. 2328 * @param pszPath The path. 2329 * @param cchPath The length of the path. 2330 * @param penmError Where to return details as to why the lookup 2331 * failed. 2332 */ 2333 static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError) 2334 { 2335 /* 2336 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd 2337 * ends up calling it anyway. 2338 */ 2339 char szFull[KFSCACHE_CFG_MAX_PATH]; 2340 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL); 2341 if ( cchFull >= 3 2342 && cchFull < sizeof(szFull)) 2343 { 2344 PKFSOBJ pFsObj; 2345 KFSCACHE_LOG(("kFsCacheLookupSlowA(%s)\n", pszPath)); 2346 pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError); 2347 2348 #if 0 /* No need to do this until it's actually queried. */ 2349 /* Cache the resulting path. */ 2350 if ( pFsObj 2351 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS) 2352 || *penmError == KFSLOOKUPERROR_UNSUPPORTED) 2353 { 2354 KU32 uHashPath = kFsCacheStrHash(szFull); 2355 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath, 2356 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError); 2357 } 2358 #endif 2359 return pFsObj; 2360 } 2361 2362 /* The path is too long! */ 2363 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull)); 2364 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG; 2365 return NULL; 2366 } 2367 2368 2369 /** 2370 * This deals with paths that are relative and paths that contains '..' 2371 * elements, UTF-16 version. 2372 * 2373 * @returns Pointer to object corresponding to @a pszPath on success. 2374 * NULL if this isn't a path we care to cache. 2375 * 2376 * @param pCache The cache. 2377 * @param pwszPath The path. 2378 * @param cwcPath The length of the path (in wchar_t's). 2379 * @param penmError Where to return details as to why the lookup 2380 * failed. 2381 */ 2382 static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KFSLOOKUPERROR *penmError) 2383 { 2384 /* 2385 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd 2386 * ends up calling it anyway. 2387 */ 2388 wchar_t wszFull[KFSCACHE_CFG_MAX_PATH]; 2389 UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL); 2390 if ( cwcFull >= 3 2391 && cwcFull < KFSCACHE_CFG_MAX_PATH) 2392 { 2393 PKFSOBJ pFsObj; 2394 KFSCACHE_LOG(("kFsCacheLookupSlowA(%ls)\n", pwszPath)); 2395 pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError); 2396 2397 #if 0 /* No need to do this until it's actually queried. */ 2398 /* Cache the resulting path. */ 2399 if ( pFsObj 2400 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS) 2401 || *penmError == KFSLOOKUPERROR_UNSUPPORTED) 2402 { 2403 KU32 uHashPath = kFsCacheStrHash(szFull); 2404 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath, 2405 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError); 2406 } 2407 #endif 2408 return pFsObj; 2409 } 2410 2411 /* The path is too long! */ 2412 kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull)); 2413 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG; 2414 return NULL; 2415 } 2416 2417 2418 /** 2419 * Refreshes a path hash that has expired, ANSI version. 2420 * 2421 * @returns pHash on success, NULL if removed. 2422 * @param pCache The cache. 2423 * @param pHashEntry The path hash. 2424 * @param idxHashTab The hash table entry. 2425 */ 2426 static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab) 2427 { 2428 /** @todo implement once we've start inserting uCacheGen nodes. */ 2429 __debugbreak(); 2430 K_NOREF(pCache); 2431 K_NOREF(idxHashTab); 2432 return pHashEntry; 2433 } 2434 2435 2436 /** 2437 * Refreshes a path hash that has expired, UTF-16 version. 2438 * 2439 * @returns pHash on success, NULL if removed. 2440 * @param pCache The cache. 2441 * @param pHashEntry The path hash. 2442 * @param idxHashTab The hash table entry. 2443 */ 2444 static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab) 2445 { 2446 /** @todo implement once we've start inserting uCacheGen nodes. */ 2447 __debugbreak(); 2448 K_NOREF(pCache); 2449 K_NOREF(idxHashTab); 2450 return pHashEntry; 2451 } 2452 2453 2454 /** 2455 * Looks up a KFSOBJ for the given ANSI path. 2456 * 2457 * This will first try the hash table. If not in the hash table, the file 2458 * system cache tree is walked, missing bits filled in and finally a hash table 2459 * entry is created. 2460 * 2461 * Only drive letter paths are cachable. We don't do any UNC paths at this 2462 * point. 2463 * 2464 * @returns Reference to object corresponding to @a pszPath on success, this 2465 * must be released by kwFsCacheRelease. 2466 * NULL if not a path we care to cache. 2467 * @param pCache The cache. 2468 * @param pszPath The path to lookup. 2469 * @param penmError Where to return details as to why the lookup 2470 * failed. 2471 */ 2472 PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError) 2473 { 2474 /* 2475 * Do hash table lookup of the path. 2476 */ 2477 KU32 uHashPath; 2478 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath); 2479 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths); 2480 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab]; 2481 if (pHashEntry) 2482 { 2483 do 2484 { 2485 if ( pHashEntry->uHashPath == uHashPath 2486 && pHashEntry->cchPath == cchPath 2487 && kHlpMemComp(pHashEntry->pszPath, pszPath, cchPath) == 0) 2488 { 2489 if ( pHashEntry->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2490 || pHashEntry->uCacheGen == pCache->uGeneration 2491 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) ) 2492 { 2493 pCache->cLookups++; 2494 pCache->cPathHashHits++; 2495 KFSCACHE_LOG(("kFsCacheLookupA(%s) - hit %p\n", pszPath, pHashEntry->pFsObj)); 2496 *penmError = pHashEntry->enmError; 2497 if (pHashEntry->pFsObj) 2498 return kFsCacheObjRetainInternal(pHashEntry->pFsObj); 2499 return NULL; 2500 } 2501 break; 2502 } 2503 pHashEntry = pHashEntry->pNext; 2504 } while (pHashEntry); 2505 } 2506 2507 /* 2508 * Create an entry for it by walking the file system cache and filling in the blanks. 2509 */ 2510 if ( cchPath > 0 2511 && cchPath < KFSCACHE_CFG_MAX_PATH) 2512 { 2513 PKFSOBJ pFsObj; 2514 2515 /* Is absolute without any '..' bits? */ 2516 if ( cchPath >= 3 2517 && ( ( pszPath[1] == ':' /* Drive letter */ 2518 && IS_SLASH(pszPath[2]) 2519 && IS_ALPHA(pszPath[0]) ) 2520 || ( IS_SLASH(pszPath[0]) /* UNC */ 2521 && IS_SLASH(pszPath[1]) ) ) 2522 && !kFsCacheHasDotDotA(pszPath, cchPath) ) 2523 pFsObj = kFsCacheLookupAbsoluteA(pCache, pszPath, cchPath, penmError); 2524 else 2525 pFsObj = kFsCacheLookupSlowA(pCache, pszPath, cchPath, penmError); 2526 if ( pFsObj 2527 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS) 2528 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG) 2529 || *penmError == KFSLOOKUPERROR_UNSUPPORTED ) 2530 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath, idxHashTab, *penmError); 2531 2532 pCache->cLookups++; 2533 if (pFsObj) 2534 pCache->cWalkHits++; 2535 return pFsObj; 2536 } 2537 2538 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG; 2539 return NULL; 2540 } 2541 2542 2543 /** 2544 * Looks up a KFSOBJ for the given UTF-16 path. 2545 * 2546 * This will first try the hash table. If not in the hash table, the file 2547 * system cache tree is walked, missing bits filled in and finally a hash table 2548 * entry is created. 2549 * 2550 * Only drive letter paths are cachable. We don't do any UNC paths at this 2551 * point. 2552 * 2553 * @returns Reference to object corresponding to @a pszPath on success, this 2554 * must be released by kwFsCacheRelease. 2555 * NULL if not a path we care to cache. 2556 * @param pCache The cache. 2557 * @param pwszPath The path to lookup. 2558 * @param penmError Where to return details as to why the lookup 2559 * failed. 2560 */ 2561 PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError) 2562 { 2563 /* 2564 * Do hash table lookup of the path. 2565 */ 2566 KU32 uHashPath; 2567 KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath); 2568 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths); 2569 PKFSHASHW pHashEntry = pCache->apUtf16Paths[idxHashTab]; 2570 if (pHashEntry) 2571 { 2572 do 2573 { 2574 if ( pHashEntry->uHashPath == uHashPath 2575 && pHashEntry->cwcPath == cwcPath 2576 && kHlpMemComp(pHashEntry->pwszPath, pwszPath, cwcPath) == 0) 2577 { 2578 if ( pHashEntry->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE 2579 || pHashEntry->uCacheGen == pCache->uGeneration 2580 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) ) 2581 { 2582 pCache->cLookups++; 2583 pCache->cPathHashHits++; 2584 KFSCACHE_LOG(("kFsCacheLookupW(%ls) - hit %p\n", pwszPath, pHashEntry->pFsObj)); 2585 *penmError = pHashEntry->enmError; 2586 if (pHashEntry->pFsObj) 2587 return kFsCacheObjRetainInternal(pHashEntry->pFsObj); 2588 return NULL; 2589 } 2590 break; 2591 } 2592 pHashEntry = pHashEntry->pNext; 2593 } while (pHashEntry); 2594 } 2595 2596 /* 2597 * Create an entry for it by walking the file system cache and filling in the blanks. 2598 */ 2599 if ( cwcPath > 0 2600 && cwcPath < KFSCACHE_CFG_MAX_PATH) 2601 { 2602 PKFSOBJ pFsObj; 2603 2604 /* Is absolute without any '..' bits? */ 2605 if ( cwcPath >= 3 2606 && ( ( pwszPath[1] == ':' /* Drive letter */ 2607 && IS_SLASH(pwszPath[2]) 2608 && IS_ALPHA(pwszPath[0]) ) 2609 || ( IS_SLASH(pwszPath[0]) /* UNC */ 2610 && IS_SLASH(pwszPath[1]) ) ) 2611 && !kFsCacheHasDotDotW(pwszPath, cwcPath) ) 2612 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwszPath, cwcPath, penmError); 2613 else 2614 pFsObj = kFsCacheLookupSlowW(pCache, pwszPath, cwcPath, penmError); 2615 if ( pFsObj 2616 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS) 2617 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG) 2618 || *penmError == KFSLOOKUPERROR_UNSUPPORTED ) 2619 kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwszPath, cwcPath, uHashPath, idxHashTab, *penmError); 2620 2621 pCache->cLookups++; 2622 if (pFsObj) 2623 pCache->cWalkHits++; 2624 return pFsObj; 2625 } 2626 2627 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG; 2628 return NULL; 2629 } 2630 2631 2632 /** 2633 * Destroys a cache object which has a zero reference count. 2634 * 2635 * @returns 0 2636 * @param pCache The cache. 2637 * @param pObj The object. 2638 */ 2639 KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj) 2640 { 2641 kHlpAssert(pObj->cRefs == 0); 2642 kHlpAssert(pObj->pParent == NULL); 2643 2644 switch (pObj->bObjType) 2645 { 2646 case KFSOBJ_TYPE_MISSING: 2647 //case KFSOBJ_TYPE_MISSING | KFSOBJ_TYPE_F_INVALID: 2648 /* nothing else to do here */ 2649 pCache->cbObjects -= sizeof(*pObj); 2650 break; 2651 2652 case KFSOBJ_TYPE_DIR: 2653 //case KFSOBJ_TYPE_DIR | KFSOBJ_TYPE_F_INVALID: 2654 case KFSOBJ_TYPE_FILE: 2655 //case KFSOBJ_TYPE_FILE | KFSOBJ_TYPE_F_INVALID: 2656 kHlpAssertFailed(); 2657 break; 2658 default: 2659 kHlpAssertFailed(); 2660 } 2661 pCache->cObjects--; 2662 free(pObj); 2663 return 0; 2664 } 2665 2666 2667 /** 2668 * Releases a reference to a cache object. 2669 * 2670 * @returns New reference count. 2671 * @param pCache The cache. 2672 * @param pObj The object. 2673 */ 2674 KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj) 2675 { 2676 KU32 cRefs = --pObj->cRefs; 2677 if (cRefs) 2678 return cRefs; 2679 return kFsCacheObjDestroy(pCache, pObj); 2680 } 2681 2682 2683 /** 2684 * Retains a reference to a cahce object. 2685 * 2686 * @returns New reference count. 2687 * @param pObj The object. 2688 */ 2689 KU32 kFsCacheObjRetain(PKFSOBJ pObj) 2690 { 2691 KU32 cRefs = ++pObj->cRefs; 2692 kHlpAssert(cRefs < 16384); 2693 return cRefs; 2694 } 2695 2696 2697 2698 PKFSCACHE kFsCacheCreate(KU32 fFlags) 2699 { 2700 PKFSCACHE pCache; 2701 birdResolveImports(); 2702 2703 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache)); 2704 if (pCache) 2705 { 2706 /* Dummy root dir entry. */ 2707 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC; 2708 pCache->RootDir.Obj.cRefs = 1; 2709 pCache->RootDir.Obj.uCacheGen = KFSWOBJ_CACHE_GEN_IGNORE; 2710 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR; 2711 pCache->RootDir.Obj.fHaveStats = K_FALSE; 2712 pCache->RootDir.Obj.pParent = NULL; 2713 pCache->RootDir.Obj.pszName = ""; 2714 pCache->RootDir.Obj.cchName = 0; 2715 pCache->RootDir.Obj.cchParent = 0; 2716 #ifdef KFSCACHE_CFG_UTF16 2717 pCache->RootDir.Obj.cwcName = 0; 2718 pCache->RootDir.Obj.cwcParent = 0; 2719 pCache->RootDir.Obj.pwszName = L""; 2720 #endif 2721 2722 #ifdef KFSCACHE_CFG_SHORT_NAMES 2723 pCache->RootDir.Obj.pszShortName = NULL; 2724 pCache->RootDir.Obj.cchShortName = 0; 2725 pCache->RootDir.Obj.cchShortParent = 0; 2726 # ifdef KFSCACHE_CFG_UTF16 2727 pCache->RootDir.Obj.cwcShortName; 2728 pCache->RootDir.Obj.cwcShortParent; 2729 pCache->RootDir.Obj.pwszShortName; 2730 # endif 2731 #endif 2732 pCache->RootDir.cChildren = 0; 2733 pCache->RootDir.papChildren = NULL; 2734 pCache->RootDir.hDir = INVALID_HANDLE_VALUE; 2735 pCache->RootDir.cHashTab = 251; 2736 pCache->RootDir.paHashTab = (PKFSOBJHASH)kHlpAllocZ( pCache->RootDir.cHashTab 2737 * sizeof(pCache->RootDir.paHashTab[0])); 2738 if (pCache->RootDir.paHashTab) 2739 { 2740 /* The cache itself. */ 2741 pCache->u32Magic = KFSCACHE_MAGIC; 2742 pCache->fFlags = fFlags; 2743 pCache->uGeneration = 1; 2744 pCache->cObjects = 1; 2745 pCache->cbObjects = sizeof(pCache->RootDir) + pCache->RootDir.cHashTab * sizeof(pCache->RootDir.paHashTab[0]); 2746 pCache->cPathHashHits = 0; 2747 pCache->cWalkHits = 0; 2748 pCache->cAnsiPaths = 0; 2749 pCache->cAnsiPathCollisions = 0; 2750 pCache->cbAnsiPaths = 0; 2751 #ifdef KFSCACHE_CFG_UTF16 2752 pCache->cUtf16Paths = 0; 2753 pCache->cUtf16PathCollisions = 0; 2754 pCache->cbUtf16Paths = 0; 2755 #endif 2756 return pCache; 2757 } 2758 2759 kHlpFree(pCache); 2760 } 2761 return NULL; 2762 } 2763 396 wchar_t const *pwszShortName, KU32 cwcShortName, 397 #endif 398 KU8 bObjType, KFSLOOKUPERROR *penmError); 399 PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError); 400 PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError); 401 402 KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj); 403 KU32 kFsCacheObjRetain(PKFSOBJ pObj); 404 405 PKFSCACHE kFsCacheCreate(KU32 fFlags); 406 void kFsCacheDestroy(PKFSCACHE); 407 408 #endif -
trunk/src/lib/nt/ntdircache.c
r2852 r2853 49 49 //#include <winternl.h> 50 50 51 52 53 /********************************************************************************************************************************* 54 * Defined Constants And Macros * 55 *********************************************************************************************************************************/ 56 /** @def KFSCACHE_CFG_UTF16 57 * Whether to compile in the UTF-16 names support. */ 58 #define KFSCACHE_CFG_UTF16 1 59 /** @def KFSCACHE_CFG_SHORT_NAMES 60 * Whether to compile in the short name support. */ 61 #define KFSCACHE_CFG_SHORT_NAMES 1 62 /** @def KFSCACHE_CFG_PATH_HASH_TAB_SIZE 63 * Size of the path hash table. */ 64 #define KFSCACHE_CFG_PATH_HASH_TAB_SIZE 16381 65 /** The max length paths we consider. */ 66 #define KFSCACHE_CFG_MAX_PATH 1024 67 /** The max ANSI name length. */ 68 #define KFSCACHE_CFG_MAX_ANSI_NAME (256*3 + 16) 69 /** The max UTF-16 name length. */ 70 #define KFSCACHE_CFG_MAX_UTF16_NAME (256*2 + 16) 71 72 73 74 /** Special KFSOBJ::uCacheGen number indicating that it does not apply. */ 75 #define KFSWOBJ_CACHE_GEN_IGNORE KU32_MAX 76 77 /** @def KW_LOG 78 * Generic logging. 79 * @param a Argument list for kFsCacheDbgPrintf */ 80 #ifndef NDEBUG 81 # define KFSCACHE_LOG(a) kFsCacheDbgPrintf a 82 #else 83 # define KFSCACHE_LOG(a) do { } while (0) 84 #endif 85 86 87 /** @name KFSOBJ_TYPE_XXX - KFSOBJ::bObjType 88 * @{ */ 89 /** Directory, type KFSDIR. */ 90 #define KFSOBJ_TYPE_DIR KU8_C(0x01) 91 /** Regular file - type KFSOBJ. */ 92 #define KFSOBJ_TYPE_FILE KU8_C(0x02) 93 /** Other file - type KFSOBJ. */ 94 #define KFSOBJ_TYPE_OTHER KU8_C(0x03) 95 /** Caching of a negative result - type KFSOBJ. 96 * @remarks We will allocate enough space for the largest cache node, so this 97 * can metamorph into any other object should it actually turn up. */ 98 #define KFSOBJ_TYPE_MISSING KU8_C(0x04) 99 ///** Invalidated entry flag. */ 100 //#define KFSOBJ_TYPE_F_INVALID KU8_C(0x20) 101 /** @} */ 102 103 /** @name KFSOBJ_F_XXX - KFSOBJ::fFlags 104 * @{ */ 105 /** Whether the file system update the modified timestamp of directories 106 * when something is removed from it or added to it. 107 * @remarks They say NTFS is the only windows filesystem doing this. */ 108 #define KFSOBJ_F_WORKING_DIR_MTIME KU32_C(0x00000001) 109 /** NTFS file system volume. */ 110 #define KFSOBJ_F_NTFS KU32_C(0x80000000) 111 /** @} */ 112 113 114 #define IS_ALPHA(ch) ( ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= 'a' && (ch) <= 'z') ) 115 #define IS_SLASH(ch) ((ch) == '\\' || (ch) == '/') 116 117 118 /********************************************************************************************************************************* 119 * Structures and Typedefs * 120 *********************************************************************************************************************************/ 121 /** Pointer to a core object. */ 122 typedef struct KFSOBJ *PKFSOBJ; 123 /** Pointer to a directory object. */ 124 typedef struct KFSDIR *PKFSDIR; 125 /** Pointer to a directory hash table entry. */ 126 typedef struct KFSOBJHASH *PKFSOBJHASH; 127 128 129 /** 130 * Directory hash table entry. 131 * 132 * There can be two of these per directory entry when the short name differs 133 * from the long name. 134 */ 135 typedef struct KFSOBJHASH 136 { 137 /** Pointer to the next entry with the same hash. */ 138 PKFSOBJHASH pNext; 139 /** Pointer to the object. */ 140 PKFSOBJ pObj; 141 } KFSOBJHASH; 142 143 144 /** 145 * Base cache node. 146 */ 147 typedef struct KFSOBJ 148 { 149 /** Magic value (KFSOBJ_MAGIC). */ 150 KU32 u32Magic; 151 /** Number of references. */ 152 KU32 volatile cRefs; 153 /** The cache generation, see KFSWOBJ_CACHE_GEN_IGNORE. */ 154 KU32 uCacheGen; 155 /** The object type, KFSOBJ_TYPE_XXX. */ 156 KU8 bObjType; 157 /** Set if the Stats member is valid, clear if not. */ 158 KBOOL fHaveStats; 159 /** Unused flags. */ 160 KBOOL abUnused[2]; 161 /** Flags, KFSOBJ_F_XXX. */ 162 KU32 fFlags; 163 164 /** Pointer to the parent (directory). 165 * This is only NULL for a root. */ 166 PKFSDIR pParent; 167 168 /** The directory name. (Allocated after the structure.) */ 169 const char *pszName; 170 /** The length of pszName. */ 171 KU16 cchName; 172 /** The length of the parent path (up to where pszName starts). 173 * @note This is valuable when constructing an absolute path to this node by 174 * means of the parent pointer (no need for recursion). */ 175 KU16 cchParent; 176 #ifdef KFSCACHE_CFG_UTF16 177 /** The length of pwszName (in wchar_t's). */ 178 KU16 cwcName; 179 /** The length of the parent UTF-16 path (in wchar_t's). 180 * @note This is valuable when constructing an absolute path to this node by 181 * means of the parent pointer (no need for recursion). */ 182 KU16 cwcParent; 183 /** The UTF-16 object name. (Allocated after the structure.) */ 184 const wchar_t *pwszName; 185 #endif 186 187 #ifdef KFSCACHE_CFG_SHORT_NAMES 188 /** The short object name. (Allocated after the structure, could be same 189 * as pszName.) */ 190 const char *pszShortName; 191 /** The length of pszShortName. */ 192 KU16 cchShortName; 193 /** The length of the short parent path (up to where pszShortName starts). */ 194 KU16 cchShortParent; 195 # ifdef KFSCACHE_CFG_UTF16 196 /** The length of pwszShortName (in wchar_t's). */ 197 KU16 cwcShortName; 198 /** The length of the short parent UTF-16 path (in wchar_t's). */ 199 KU16 cwcShortParent; 200 /** The UTF-16 short object name. (Allocated after the structure, possibly 201 * same as pwszName.) */ 202 const wchar_t *pwszShortName; 203 # endif 204 #endif 205 206 /** Stats - only valid when fHaveStats is set. */ 207 BirdStat_T Stats; 208 } KFSOBJ; 209 210 /** The magic for a KFSOBJ structure (Thelonious Sphere Monk). */ 211 #define KFSOBJ_MAGIC KU32_C(0x19171010) 212 213 214 /** 215 * Directory node in the cache. 216 */ 217 typedef struct KFSDIR 218 { 219 /** The core object information. */ 220 KFSOBJ Obj; 221 222 /** Child objects. */ 223 PKFSOBJ *papChildren; 224 /** The number of child objects. */ 225 KU32 cChildren; 226 227 /** The size of the hash table. 228 * @remarks The hash table is optional and only used when there are a lot of 229 * entries in the directory. */ 230 KU32 cHashTab; 231 /** Pointer to the hash table. 232 * @todo this isn't quite there yet, structure wise. sigh. */ 233 PKFSOBJHASH paHashTab; 234 235 /** Handle to the directory (we generally keep it open). */ 236 HANDLE hDir; 237 /** The device number we queried/inherited when opening it. */ 238 KU64 uDevNo; 239 240 /** Set if populated. */ 241 KBOOL fPopulated; 242 } KFSDIR; 243 244 245 /** 246 * Lookup errors. 247 */ 248 typedef enum KFSLOOKUPERROR 249 { 250 /** Lookup was a success. */ 251 KFSLOOKUPERROR_SUCCESS = 0, 252 /** A path component was not found. */ 253 KFSLOOKUPERROR_PATH_COMP_NOT_FOUND, 254 /** A path component is not a directory. */ 255 KFSLOOKUPERROR_PATH_COMP_NOT_DIR, 256 /** The final path entry is not a directory (trailing slash). */ 257 KFSLOOKUPERROR_NOT_DIR, 258 /** Not found. */ 259 KFSLOOKUPERROR_NOT_FOUND, 260 /** The path is too long. */ 261 KFSLOOKUPERROR_PATH_TOO_LONG, 262 /** Unsupported path type. */ 263 KFSLOOKUPERROR_UNSUPPORTED, 264 /** We're out of memory. */ 265 KFSLOOKUPERROR_OUT_OF_MEMORY, 266 267 /** Error opening directory. */ 268 KFSLOOKUPERROR_DIR_OPEN_ERROR, 269 /** Error reading directory. */ 270 KFSLOOKUPERROR_DIR_READ_ERROR, 271 /** UTF-16 to ANSI conversion error. */ 272 KFSLOOKUPERROR_ANSI_CONVERSION_ERROR, 273 /** ANSI to UTF-16 conversion error. */ 274 KFSLOOKUPERROR_UTF16_CONVERSION_ERROR, 275 /** Internal error. */ 276 KFSLOOKUPERROR_INTERNAL_ERROR 277 } KFSLOOKUPERROR; 278 279 280 /** Pointer to an ANSI path hash table entry. */ 281 typedef struct KFSHASHA *PKFSHASHA; 282 /** 283 * ANSI file system path hash table entry. 284 * The path hash table allows us to skip parsing and walking a path. 285 */ 286 typedef struct KFSHASHA 287 { 288 /** Next entry with the same hash table slot. */ 289 PKFSHASHA pNext; 290 /** Path hash value. */ 291 KU32 uHashPath; 292 /** The path length. */ 293 KU32 cchPath; 294 /** The cache generation ID. */ 295 KU32 uCacheGen; 296 /** The lookup error (when pFsObj is NULL). */ 297 KFSLOOKUPERROR enmError; 298 /** The path. (Allocated after the structure.) */ 299 const char *pszPath; 300 /** Pointer to the matching FS object. 301 * This is NULL for negative path entries? */ 302 PKFSOBJ pFsObj; 303 } KFSHASHA; 304 305 306 #ifdef KFSCACHE_CFG_UTF16 307 /** Pointer to an UTF-16 path hash table entry. */ 308 typedef struct KFSHASHW *PKFSHASHW; 309 /** 310 * UTF-16 file system path hash table entry. The path hash table allows us 311 * to skip parsing and walking a path. 312 */ 313 typedef struct KFSHASHW 314 { 315 /** Next entry with the same hash table slot. */ 316 PKFSHASHW pNext; 317 /** Path hash value. */ 318 KU32 uHashPath; 319 /** The path length (in wchar_t units). */ 320 KU32 cwcPath; 321 /** The cache generation ID. */ 322 KU32 uCacheGen; 323 /** The lookup error (when pFsObj is NULL). */ 324 KFSLOOKUPERROR enmError; 325 /** The path. (Allocated after the structure.) */ 326 const wchar_t *pwszPath; 327 /** Pointer to the matching FS object. 328 * This is NULL for negative path entries? */ 329 PKFSOBJ pFsObj; 330 } KFSHASHW; 331 #endif 332 333 334 /** @name KFSCACHE_F_XXX 335 * @{ */ 336 /** Whether to cache missing directory entries (KFSOBJ_TYPE_MISSING). */ 337 #define KFSCACHE_F_MISSING_OBJECTS KU32_C(0x00000001) 338 /** Whether to cache missing paths. */ 339 #define KFSCACHE_F_MISSING_PATHS KU32_C(0x00000002) 340 /** @} */ 341 342 343 /** Pointer to a cache. */ 344 typedef struct KFSCACHE *PKFSCACHE; 345 /** 346 * Directory cache instance. 347 */ 348 typedef struct KFSCACHE 349 { 350 /** Magic value (KFSCACHE_MAGIC). */ 351 KU32 u32Magic; 352 /** Cache flags. */ 353 KU32 fFlags; 354 355 /** The current cache generation for objects that already exists. */ 356 KU32 uGeneration; 357 /** The current cache generation for missing objects, negative results, ++. */ 358 KU32 uGenerationMissing; 359 360 /** Number of cache objects. */ 361 KSIZE cObjects; 362 /** Memory occupied by the cache object structures. */ 363 KSIZE cbObjects; 364 /** Number of lookups. */ 365 KSIZE cLookups; 366 /** Number of hits in the path hash tables. */ 367 KSIZE cPathHashHits; 368 /** Number of hits walking the file system hierarchy. */ 369 KSIZE cWalkHits; 370 371 /** The root directory. */ 372 KFSDIR RootDir; 373 374 /** File system hash table for ANSI filename strings. */ 375 PKFSHASHA apAnsiPaths[KFSCACHE_CFG_PATH_HASH_TAB_SIZE]; 376 /** Number of paths in the apAnsiPaths hash table. */ 377 KSIZE cAnsiPaths; 378 /** Number of collisions in the apAnsiPaths hash table. */ 379 KSIZE cAnsiPathCollisions; 380 /** Amount of memory used by the path entries. */ 381 KSIZE cbAnsiPaths; 382 383 #ifdef KFSCACHE_CFG_UTF16 384 /** Number of paths in the apUtf16Paths hash table. */ 385 KSIZE cUtf16Paths; 386 /** Number of collisions in the apUtf16Paths hash table. */ 387 KSIZE cUtf16PathCollisions; 388 /** Amount of memory used by the UTF-16 path entries. */ 389 KSIZE cbUtf16Paths; 390 /** File system hash table for UTF-16 filename strings. */ 391 PKFSHASHW apUtf16Paths[KFSCACHE_CFG_PATH_HASH_TAB_SIZE]; 392 #endif 393 } KFSCACHE; 394 395 /** Magic value for KFSCACHE::u32Magic (Jon Batiste). */ 396 #define KFSCACHE_MAGIC KU32_C(0x19861111) 397 398 399 /********************************************************************************************************************************* 400 * Internal Functions * 401 *********************************************************************************************************************************/ 402 KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj); 51 #include "kFsCache.h" 52 53 54 403 55 404 56 … … 425 77 * @param ... Format argument. 426 78 */ 427 staticvoid kFsCacheDbgPrintfV(const char *pszFormat, va_list va)79 void kFsCacheDbgPrintfV(const char *pszFormat, va_list va) 428 80 { 429 81 if (1) … … 444 96 * @param ... Format argument. 445 97 */ 446 staticvoid kFsCacheDbgPrintf(const char *pszFormat, ...)98 void kFsCacheDbgPrintf(const char *pszFormat, ...) 447 99 { 448 100 if (1) … … 927 579 pHashEntry->uCacheGen = pCache->uGenerationMissing; 928 580 else 929 pHashEntry->uCacheGen = KFS WOBJ_CACHE_GEN_IGNORE;581 pHashEntry->uCacheGen = KFSOBJ_CACHE_GEN_IGNORE; 930 582 931 583 pHashEntry->pNext = pCache->apAnsiPaths[idxHashTab]; … … 969 621 pHashEntry->uCacheGen = pCache->uGenerationMissing; 970 622 else 971 pHashEntry->uCacheGen = KFS WOBJ_CACHE_GEN_IGNORE;623 pHashEntry->uCacheGen = KFSOBJ_CACHE_GEN_IGNORE; 972 624 973 625 pHashEntry->pNext = pCache->apUtf16Paths[idxHashTab]; … … 1026 678 * @param penmError Where to explain failures. 1027 679 */ 1028 staticPKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,1029 680 PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent, 681 char const *pszName, KU16 cchName, wchar_t const *pwszName, KU16 cwcName, 1030 682 #ifdef KFSCACHE_CFG_SHORT_NAMES 1031 1032 #endif 1033 683 char const *pszShortName, KU16 cchShortName, wchar_t const *pwszShortName, KU16 cwcShortName, 684 #endif 685 KU8 bObjType, KFSLOOKUPERROR *penmError) 1034 686 { 1035 687 /* … … 1149 801 * @param penmError Where to explain failures. 1150 802 */ 1151 staticPKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName,803 PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName, 1152 804 #ifdef KFSCACHE_CFG_SHORT_NAMES 1153 1154 #endif 1155 805 wchar_t const *pwszShortName, KU32 cwcShortName, 806 #endif 807 KU8 bObjType, KFSLOOKUPERROR *penmError) 1156 808 { 1157 809 /* Convert names to ANSI first so we know their lengths. */ … … 2006 1658 if (offEnd >= cchPath) 2007 1659 { 2008 if ( pChild->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE1660 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2009 1661 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing) 2010 1662 || kFsCacheRefreshObj(pCache, pChild, penmError)) … … 2019 1671 { 2020 1672 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING); 2021 kHlpAssert(pChild->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);1673 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing); 2022 1674 return pChild; 2023 1675 } … … 2056 1708 */ 2057 1709 if ( pParent->fPopulated 2058 && ( pParent->Obj.uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE1710 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2059 1711 || pParent->Obj.uCacheGen == pCache->uGeneration) ) 2060 1712 { /* likely */ } … … 2094 1746 { 2095 1747 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING 2096 || pChild->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE1748 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2097 1749 || pChild->uCacheGen == pCache->uGenerationMissing 2098 1750 || kFsCacheRefreshMissing(pCache, pChild, penmError) ) … … 2114 1766 return NULL; 2115 1767 } 2116 else if ( pChild->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE1768 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2117 1769 || pChild->uCacheGen == pCache->uGenerationMissing) 2118 1770 { … … 2194 1846 if (offEnd >= cwcPath) 2195 1847 { 2196 if ( pChild->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE1848 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2197 1849 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing) 2198 1850 || kFsCacheRefreshObj(pCache, pChild, penmError)) … … 2207 1859 { 2208 1860 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING); 2209 kHlpAssert(pChild->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);1861 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing); 2210 1862 return pChild; 2211 1863 } … … 2244 1896 */ 2245 1897 if ( pParent->fPopulated 2246 && ( pParent->Obj.uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE1898 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2247 1899 || pParent->Obj.uCacheGen == pCache->uGeneration) ) 2248 1900 { /* likely */ } … … 2282 1934 { 2283 1935 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING 2284 || pChild->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE1936 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2285 1937 || pChild->uCacheGen == pCache->uGenerationMissing 2286 1938 || kFsCacheRefreshMissing(pCache, pChild, penmError) ) … … 2302 1954 return NULL; 2303 1955 } 2304 else if ( pChild->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE1956 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2305 1957 || pChild->uCacheGen == pCache->uGenerationMissing) 2306 1958 { … … 2487 2139 && kHlpMemComp(pHashEntry->pszPath, pszPath, cchPath) == 0) 2488 2140 { 2489 if ( pHashEntry->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE2141 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2490 2142 || pHashEntry->uCacheGen == pCache->uGeneration 2491 2143 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) ) … … 2576 2228 && kHlpMemComp(pHashEntry->pwszPath, pwszPath, cwcPath) == 0) 2577 2229 { 2578 if ( pHashEntry->uCacheGen == KFS WOBJ_CACHE_GEN_IGNORE2230 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2579 2231 || pHashEntry->uCacheGen == pCache->uGeneration 2580 2232 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) ) … … 2707 2359 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC; 2708 2360 pCache->RootDir.Obj.cRefs = 1; 2709 pCache->RootDir.Obj.uCacheGen = KFS WOBJ_CACHE_GEN_IGNORE;2361 pCache->RootDir.Obj.uCacheGen = KFSOBJ_CACHE_GEN_IGNORE; 2710 2362 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR; 2711 2363 pCache->RootDir.Obj.fHaveStats = K_FALSE;
Note:
See TracChangeset
for help on using the changeset viewer.