summaryrefslogtreecommitdiffstats
path: root/src/lib/nt/kFsCache.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/nt/kFsCache.h')
-rw-r--r--src/lib/nt/kFsCache.h594
1 files changed, 594 insertions, 0 deletions
diff --git a/src/lib/nt/kFsCache.h b/src/lib/nt/kFsCache.h
new file mode 100644
index 0000000..de0f87c
--- /dev/null
+++ b/src/lib/nt/kFsCache.h
@@ -0,0 +1,594 @@
+/* $Id: kFsCache.h 3381 2020-06-12 11:36:10Z bird $ */
+/** @file
+ * kFsCache.c - NT directory content cache.
+ */
+
+/*
+ * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Alternatively, the content of this file may be used under the terms of the
+ * GPL version 2 or later, or LGPL version 2.1 or later.
+ */
+
+#ifndef ___lib_nt_kFsCache_h___
+#define ___lib_nt_kFsCache_h___
+
+
+#include <k/kHlp.h>
+#include "ntstat.h"
+#ifndef NDEBUG
+# include <stdarg.h>
+#endif
+
+
+/** @def KFSCACHE_CFG_UTF16
+ * Whether to compile in the UTF-16 names support. */
+#define KFSCACHE_CFG_UTF16 1
+/** @def KFSCACHE_CFG_SHORT_NAMES
+ * Whether to compile in the short name support. */
+#define KFSCACHE_CFG_SHORT_NAMES 1
+/** @def KFSCACHE_CFG_PATH_HASH_TAB_SIZE
+ * Size of the path hash table. */
+#define KFSCACHE_CFG_PATH_HASH_TAB_SIZE 99991
+/** The max length paths we consider. */
+#define KFSCACHE_CFG_MAX_PATH 1024
+/** The max ANSI name length. */
+#define KFSCACHE_CFG_MAX_ANSI_NAME (256*3 + 16)
+/** The max UTF-16 name length. */
+#define KFSCACHE_CFG_MAX_UTF16_NAME (256*2 + 16)
+/** Enables locking of the cache and thereby making it thread safe. */
+#define KFSCACHE_CFG_LOCKING 1
+
+
+
+/** Special KFSOBJ::uCacheGen number indicating that it does not apply. */
+#define KFSOBJ_CACHE_GEN_IGNORE KU32_MAX
+
+
+/** @name KFSOBJ_TYPE_XXX - KFSOBJ::bObjType
+ * @{ */
+/** Directory, type KFSDIR. */
+#define KFSOBJ_TYPE_DIR KU8_C(0x01)
+/** Regular file - type KFSOBJ. */
+#define KFSOBJ_TYPE_FILE KU8_C(0x02)
+/** Other file - type KFSOBJ. */
+#define KFSOBJ_TYPE_OTHER KU8_C(0x03)
+/** Caching of a negative result - type KFSOBJ.
+ * @remarks We will allocate enough space for the largest cache node, so this
+ * can metamorph into any other object should it actually turn up. */
+#define KFSOBJ_TYPE_MISSING KU8_C(0x04)
+///** Invalidated entry flag. */
+//#define KFSOBJ_TYPE_F_INVALID KU8_C(0x20)
+/** @} */
+
+/** @name KFSOBJ_F_XXX - KFSOBJ::fFlags
+ * @{ */
+ /** Use custom generation.
+ * @remarks This is given the value 1, as we use it as an index into
+ * KFSCACHE::auGenerations, 0 being the default. */
+#define KFSOBJ_F_USE_CUSTOM_GEN KU32_C(0x00000001)
+
+/** Whether the file system update the modified timestamp of directories
+ * when something is removed from it or added to it.
+ * @remarks They say NTFS is the only windows filesystem doing this. */
+#define KFSOBJ_F_WORKING_DIR_MTIME KU32_C(0x00000002)
+/** NTFS file system volume. */
+#define KFSOBJ_F_NTFS KU32_C(0x80000000)
+/** Flags that are automatically inherited. */
+#define KFSOBJ_F_INHERITED_MASK KU32_C(0xffffffff)
+/** @} */
+
+
+#define IS_ALPHA(ch) ( ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= 'a' && (ch) <= 'z') )
+#define IS_SLASH(ch) ((ch) == '\\' || (ch) == '/')
+
+
+
+
+/** Pointer to a cache. */
+typedef struct KFSCACHE *PKFSCACHE;
+/** Pointer to a core object. */
+typedef struct KFSOBJ *PKFSOBJ;
+/** Pointer to a directory object. */
+typedef struct KFSDIR *PKFSDIR;
+/** Pointer to a directory hash table entry. */
+typedef struct KFSOBJHASH *PKFSOBJHASH;
+
+
+
+/** Pointer to a user data item. */
+typedef struct KFSUSERDATA *PKFSUSERDATA;
+/**
+ * User data item associated with a cache node.
+ */
+typedef struct KFSUSERDATA
+{
+ /** Pointer to the next piece of user data. */
+ PKFSUSERDATA pNext;
+ /** The key identifying this user. */
+ KUPTR uKey;
+ /** The destructor. */
+ void (*pfnDestructor)(PKFSCACHE pCache, PKFSOBJ pObj, PKFSUSERDATA pData);
+} KFSUSERDATA;
+
+
+/**
+ * Storage for name strings for the unlikely event that they should grow in
+ * length after the KFSOBJ was created.
+ */
+typedef struct KFSOBJNAMEALLOC
+{
+ /** Size of the allocation. */
+ KU32 cb;
+ /** The space for names. */
+ char abSpace[1];
+} KFSOBJNAMEALLOC;
+/** Name growth allocation. */
+typedef KFSOBJNAMEALLOC *PKFSOBJNAMEALLOC;
+
+
+/**
+ * Base cache node.
+ */
+typedef struct KFSOBJ
+{
+ /** Magic value (KFSOBJ_MAGIC). */
+ KU32 u32Magic;
+ /** Number of references. */
+ KU32 volatile cRefs;
+ /** The cache generation, see KFSOBJ_CACHE_GEN_IGNORE. */
+ KU32 uCacheGen;
+ /** The object type, KFSOBJ_TYPE_XXX. */
+ KU8 bObjType;
+ /** Set if the Stats member is valid, clear if not. */
+ KBOOL fHaveStats;
+ /** Internal debug field for counting path hash references.
+ * @internal */
+ KU8 cPathHashRefs;
+ /** Index into KFSCACHE::auUserData. */
+ KU8 idxUserDataLock;
+ /** Flags, KFSOBJ_F_XXX. */
+ KU32 fFlags;
+
+ /** Hash value of the name inserted into the parent hash table.
+ * This is 0 if not inserted. Names are only hashed and inserted as they are
+ * first found thru linear searching of its siblings, and which name it is
+ * dependens on the lookup function (W or A) and whether the normal name or
+ * short name seems to have matched.
+ *
+ * @note It was ruled out as too much work to hash and track all four names,
+ * so instead this minimalist approach was choosen in stead. */
+ KU32 uNameHash;
+ /** Pointer to the next child with the same name hash value. */
+ PKFSOBJ pNextNameHash;
+ /** Pointer to the parent (directory).
+ * This is only NULL for a root. */
+ PKFSDIR pParent;
+
+ /** The directory name. (Allocated after the structure.) */
+ const char *pszName;
+ /** The length of pszName. */
+ KU16 cchName;
+ /** The length of the parent path (up to where pszName starts).
+ * @note This is valuable when constructing an absolute path to this node by
+ * means of the parent pointer (no need for recursion). */
+ KU16 cchParent;
+#ifdef KFSCACHE_CFG_UTF16
+ /** The length of pwszName (in wchar_t's). */
+ KU16 cwcName;
+ /** The length of the parent UTF-16 path (in wchar_t's).
+ * @note This is valuable when constructing an absolute path to this node by
+ * means of the parent pointer (no need for recursion). */
+ KU16 cwcParent;
+ /** The UTF-16 object name. (Allocated after the structure.) */
+ const wchar_t *pwszName;
+#endif
+
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ /** The short object name. (Allocated after the structure, could be same
+ * as pszName.) */
+ const char *pszShortName;
+ /** The length of pszShortName. */
+ KU16 cchShortName;
+ /** The length of the short parent path (up to where pszShortName starts). */
+ KU16 cchShortParent;
+# ifdef KFSCACHE_CFG_UTF16
+ /** The length of pwszShortName (in wchar_t's). */
+ KU16 cwcShortName;
+ /** The length of the short parent UTF-16 path (in wchar_t's). */
+ KU16 cwcShortParent;
+ /** The UTF-16 short object name. (Allocated after the structure, possibly
+ * same as pwszName.) */
+ const wchar_t *pwszShortName;
+# endif
+#endif
+
+ /** Allocation for handling name length increases. */
+ PKFSOBJNAMEALLOC pNameAlloc;
+
+ /** Pointer to the first user data item */
+ PKFSUSERDATA pUserDataHead;
+
+ /** Stats - only valid when fHaveStats is set. */
+ BirdStat_T Stats;
+} KFSOBJ;
+
+/** The magic for a KFSOBJ structure (Thelonious Sphere Monk). */
+#define KFSOBJ_MAGIC KU32_C(0x19171010)
+
+
+/**
+ * Directory node in the cache.
+ */
+typedef struct KFSDIR
+{
+ /** The core object information. */
+ KFSOBJ Obj;
+
+ /** Child objects. */
+ PKFSOBJ *papChildren;
+ /** The number of child objects. */
+ KU32 cChildren;
+ /** The allocated size of papChildren. */
+ KU32 cChildrenAllocated;
+
+ /** Pointer to the child hash table. */
+ PKFSOBJ *papHashTab;
+ /** The mask shift of the hash table.
+ * Hash table size is a power of two, this is the size minus one.
+ *
+ * @remarks The hash table is optional and populated by lookup hits. The
+ * assumption being that a lookup is repeated and will choose a good
+ * name to hash on. We've got up to 4 different hashes, so this
+ * was the easy way out. */
+ KU32 fHashTabMask;
+
+ /** Handle to the directory (we generally keep it open). */
+#ifndef DECLARE_HANDLE
+ KUPTR hDir;
+#else
+ HANDLE hDir;
+#endif
+ /** The device number we queried/inherited when opening it. */
+ KU64 uDevNo;
+
+ /** The last write time sampled the last time the directory was refreshed.
+ * @remarks May differ from st_mtim because it will be updated when the
+ * parent directory is refreshed. */
+ KI64 iLastWrite;
+ /** The time that iLastWrite was read. */
+ KI64 iLastPopulated;
+
+ /** Set if populated. */
+ KBOOL fPopulated;
+ /** Set if it needs re-populated. */
+ KBOOL fNeedRePopulating;
+} KFSDIR;
+
+
+/**
+ * Lookup errors.
+ */
+typedef enum KFSLOOKUPERROR
+{
+ /** Lookup was a success. */
+ KFSLOOKUPERROR_SUCCESS = 0,
+ /** A path component was not found. */
+ KFSLOOKUPERROR_PATH_COMP_NOT_FOUND,
+ /** A path component is not a directory. */
+ KFSLOOKUPERROR_PATH_COMP_NOT_DIR,
+ /** The final path entry is not a directory (trailing slash). */
+ KFSLOOKUPERROR_NOT_DIR,
+ /** Not found. */
+ KFSLOOKUPERROR_NOT_FOUND,
+ /** The path is too long. */
+ KFSLOOKUPERROR_PATH_TOO_LONG,
+ /** The path is too short. */
+ KFSLOOKUPERROR_PATH_TOO_SHORT,
+ /** Unsupported path type. */
+ KFSLOOKUPERROR_UNSUPPORTED,
+ /** We're out of memory. */
+ KFSLOOKUPERROR_OUT_OF_MEMORY,
+
+ /** Error opening directory. */
+ KFSLOOKUPERROR_DIR_OPEN_ERROR,
+ /** Error reading directory. */
+ KFSLOOKUPERROR_DIR_READ_ERROR,
+ /** UTF-16 to ANSI conversion error. */
+ KFSLOOKUPERROR_ANSI_CONVERSION_ERROR,
+ /** ANSI to UTF-16 conversion error. */
+ KFSLOOKUPERROR_UTF16_CONVERSION_ERROR,
+ /** Internal error. */
+ KFSLOOKUPERROR_INTERNAL_ERROR
+} KFSLOOKUPERROR;
+
+
+/** Pointer to an ANSI path hash table entry. */
+typedef struct KFSHASHA *PKFSHASHA;
+/**
+ * ANSI file system path hash table entry.
+ * The path hash table allows us to skip parsing and walking a path.
+ */
+typedef struct KFSHASHA
+{
+ /** Next entry with the same hash table slot. */
+ PKFSHASHA pNext;
+ /** Path hash value. */
+ KU32 uHashPath;
+ /** The path length. */
+ KU16 cchPath;
+ /** Set if aboslute path. */
+ KBOOL fAbsolute;
+ /** Index into KFSCACHE:auGenerationsMissing when pFsObj is NULL. */
+ KU8 idxMissingGen;
+ /** The cache generation ID. */
+ KU32 uCacheGen;
+ /** The lookup error (when pFsObj is NULL). */
+ KFSLOOKUPERROR enmError;
+ /** The path. (Allocated after the structure.) */
+ const char *pszPath;
+ /** Pointer to the matching FS object.
+ * This is NULL for negative path entries? */
+ PKFSOBJ pFsObj;
+} KFSHASHA;
+
+
+#ifdef KFSCACHE_CFG_UTF16
+/** Pointer to an UTF-16 path hash table entry. */
+typedef struct KFSHASHW *PKFSHASHW;
+/**
+ * UTF-16 file system path hash table entry. The path hash table allows us
+ * to skip parsing and walking a path.
+ */
+typedef struct KFSHASHW
+{
+ /** Next entry with the same hash table slot. */
+ PKFSHASHW pNext;
+ /** Path hash value. */
+ KU32 uHashPath;
+ /** The path length (in wchar_t units). */
+ KU16 cwcPath;
+ /** Set if aboslute path. */
+ KBOOL fAbsolute;
+ /** Index into KFSCACHE:auGenerationsMissing when pFsObj is NULL. */
+ KU8 idxMissingGen;
+ /** The cache generation ID. */
+ KU32 uCacheGen;
+ /** The lookup error (when pFsObj is NULL). */
+ KFSLOOKUPERROR enmError;
+ /** The path. (Allocated after the structure.) */
+ const wchar_t *pwszPath;
+ /** Pointer to the matching FS object.
+ * This is NULL for negative path entries? */
+ PKFSOBJ pFsObj;
+} KFSHASHW;
+#endif
+
+
+/** @def KFSCACHE_LOCK
+ * Locks the cache exclusively. */
+/** @def KFSCACHE_UNLOCK
+ * Counterpart to KFSCACHE_LOCK. */
+/** @def KFSCACHE_OBJUSERDATA_LOCK
+ * Locks the user data list of an object exclusively. */
+/** @def KFSCACHE_OBJUSERDATA_UNLOCK
+ * Counterpart to KFSCACHE_OBJUSERDATA_LOCK. */
+#ifdef KFSCACHE_CFG_LOCKING
+# define KFSCACHE_LOCK(a_pCache) EnterCriticalSection(&(a_pCache)->u.CritSect)
+# define KFSCACHE_UNLOCK(a_pCache) LeaveCriticalSection(&(a_pCache)->u.CritSect)
+# define KFSCACHE_OBJUSERDATA_LOCK(a_pCache, a_pObj) do { \
+ KU8 idxUserDataLock = (a_pObj)->idxUserDataLock; \
+ if (idxUserDataLock != KU8_MAX) \
+ { /* likely */ } \
+ else \
+ idxUserDataLock = kFsCacheObjGetUserDataLockIndex(a_pCache, a_pObj); \
+ idxUserDataLock &= (KU8)(K_ELEMENTS((a_pCache)->auUserDataLocks) - 1); \
+ EnterCriticalSection(&(a_pCache)->auUserDataLocks[idxUserDataLock].CritSect); \
+ } while (0)
+# define KFSCACHE_OBJUSERDATA_UNLOCK(a_pCache, a_pObj) \
+ LeaveCriticalSection(&(a_pCache)->auUserDataLocks[(a_pObj)->idxUserDataLock & (K_ELEMENTS((a_pCache)->auUserDataLocks) - 1)].CritSect)
+#else
+# define KFSCACHE_LOCK(a_pCache) do { } while (0)
+# define KFSCACHE_UNLOCK(a_pCache) do { } while (0)
+# define KFSCACHE_OBJUSERDATA_LOCK(a_pCache, a_pObj) do { } while (0)
+# define KFSCACHE_OBJUSERDATA_UNLOCK(a_pCache, a_pObj) do { } while (0)
+#endif
+
+
+/** @name KFSCACHE_F_XXX
+ * @{ */
+/** Whether to cache missing directory entries (KFSOBJ_TYPE_MISSING). */
+#define KFSCACHE_F_MISSING_OBJECTS KU32_C(0x00000001)
+/** Whether to cache missing paths. */
+#define KFSCACHE_F_MISSING_PATHS KU32_C(0x00000002)
+/** @} */
+
+
+/**
+ * Directory cache instance.
+ */
+typedef struct KFSCACHE
+{
+ /** Magic value (KFSCACHE_MAGIC). */
+ KU32 u32Magic;
+ /** Cache flags. */
+ KU32 fFlags;
+
+ /** The default and custom cache generations for stuff that exists, indexed by
+ * KFSOBJ_F_USE_CUSTOM_GEN.
+ *
+ * The custom generation can be used to invalidate parts of the file system that
+ * are known to be volatile without triggering refreshing of the more static
+ * parts. Like the 'out' directory in a kBuild setup or a 'TEMP' directory are
+ * expected to change and you need to invalidate the caching of these frequently
+ * to stay on top of things. Whereas the sources, headers, compilers, sdk,
+ * ddks, windows directory and such generally doesn't change all that often.
+ */
+ KU32 auGenerations[2];
+ /** The current cache generation for missing objects, negative results, ++.
+ * This comes with a custom variant too. Indexed by KFSOBJ_F_USE_CUSTOM_GEN. */
+ KU32 auGenerationsMissing[2];
+
+ /** Number of cache objects. */
+ KSIZE cObjects;
+ /** Memory occupied by the cache object structures. */
+ KSIZE cbObjects;
+ /** Number of lookups. */
+ KSIZE cLookups;
+ /** Number of hits in the path hash tables. */
+ KSIZE cPathHashHits;
+ /** Number of hits walking the file system hierarchy. */
+ KSIZE cWalkHits;
+ /** Number of child searches. */
+ KSIZE cChildSearches;
+ /** Number of cChildLookups resolved thru hash table hits. */
+ KSIZE cChildHashHits;
+ /** The number of child hash tables. */
+ KSIZE cChildHashTabs;
+ /** The sum of all child hash table sizes. */
+ KSIZE cChildHashEntriesTotal;
+ /** Number of children inserted into the hash tables. */
+ KSIZE cChildHashed;
+ /** Number of collisions in the child hash tables. */
+ KSIZE cChildHashCollisions;
+ /** Number times a object name changed. */
+ KSIZE cNameChanges;
+ /** Number times a object name grew and needed KFSOBJNAMEALLOC.
+ * (Subset of cNameChanges) */
+ KSIZE cNameGrowths;
+
+ /** The root directory. */
+ KFSDIR RootDir;
+
+#ifdef KFSCACHE_CFG_LOCKING
+ union
+ {
+# ifdef _WINBASE_
+ CRITICAL_SECTION CritSect;
+# endif
+ KU64 abPadding[2 * 4 + 4 * sizeof(void *)];
+ }
+ /** Critical section protecting the cache. */
+ u,
+ /** Critical section protecting the pUserDataHead of objects.
+ * @note Array size must be a power of two. */
+ auUserDataLocks[8];
+ /** The next auUserData index. */
+ KU8 idxUserDataNext;
+#endif
+
+ /** File system hash table for ANSI filename strings. */
+ PKFSHASHA apAnsiPaths[KFSCACHE_CFG_PATH_HASH_TAB_SIZE];
+ /** Number of paths in the apAnsiPaths hash table. */
+ KSIZE cAnsiPaths;
+ /** Number of collisions in the apAnsiPaths hash table. */
+ KSIZE cAnsiPathCollisions;
+ /** Amount of memory used by the path entries. */
+ KSIZE cbAnsiPaths;
+
+#ifdef KFSCACHE_CFG_UTF16
+ /** Number of paths in the apUtf16Paths hash table. */
+ KSIZE cUtf16Paths;
+ /** Number of collisions in the apUtf16Paths hash table. */
+ KSIZE cUtf16PathCollisions;
+ /** Amount of memory used by the UTF-16 path entries. */
+ KSIZE cbUtf16Paths;
+ /** File system hash table for UTF-16 filename strings. */
+ PKFSHASHW apUtf16Paths[KFSCACHE_CFG_PATH_HASH_TAB_SIZE];
+#endif
+} KFSCACHE;
+
+/** Magic value for KFSCACHE::u32Magic (Jon Batiste). */
+#define KFSCACHE_MAGIC KU32_C(0x19861111)
+
+
+/** @def KW_LOG
+ * Generic logging.
+ * @param a Argument list for kFsCacheDbgPrintf */
+#if 1 /*def NDEBUG - enable when needed! */
+# define KFSCACHE_LOG(a) do { } while (0)
+#else
+# define KFSCACHE_LOG(a) kFsCacheDbgPrintf a
+void kFsCacheDbgPrintfV(const char *pszFormat, va_list va);
+void kFsCacheDbgPrintf(const char *pszFormat, ...);
+#endif
+
+
+KBOOL kFsCacheDirEnsurePopuplated(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError);
+KBOOL kFsCacheDirAddChild(PKFSCACHE pCache, PKFSDIR pParent, PKFSOBJ pChild, KFSLOOKUPERROR *penmError);
+PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,
+ char const *pszName, KU16 cchName, wchar_t const *pwszName, KU16 cwcName,
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ char const *pszShortName, KU16 cchShortName, wchar_t const *pwszShortName, KU16 cwcShortName,
+#endif
+ KU8 bObjType, KFSLOOKUPERROR *penmError);
+PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName,
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ wchar_t const *pwszShortName, KU32 cwcShortName,
+#endif
+ KU8 bObjType, KFSLOOKUPERROR *penmError);
+PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError);
+PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError);
+PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags,
+ KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor);
+PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
+ KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor);
+PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError);
+PKFSOBJ kFsCacheLookupWithLengthW(PKFSCACHE pCache, const wchar_t *pwcPath, KSIZE cwcPath, KFSLOOKUPERROR *penmError);
+PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError);
+PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError);
+
+/** @name KFSCACHE_LOOKUP_F_XXX - lookup flags
+ * @{ */
+/** No inserting new cache entries.
+ * This effectively prevent directories from being repopulated too. */
+#define KFSCACHE_LOOKUP_F_NO_INSERT KU32_C(1)
+/** No refreshing cache entries. */
+#define KFSCACHE_LOOKUP_F_NO_REFRESH KU32_C(2)
+/** @} */
+
+KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj);
+KU32 kFsCacheObjReleaseTagged(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere);
+#ifndef NDEBUG /* enable to debug object release. */
+# define kFsCacheObjRelease(a_pCache, a_pObj) kFsCacheObjReleaseTagged(a_pCache, a_pObj, __FUNCTION__)
+#endif
+KU32 kFsCacheObjRetain(PKFSOBJ pObj);
+PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData);
+PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey);
+KU8 kFsCacheObjGetUserDataLockIndex(PKFSCACHE pCache, PKFSOBJ pObj);
+KBOOL kFsCacheObjGetFullPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash);
+KBOOL kFsCacheObjGetFullPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash);
+KBOOL kFsCacheObjGetFullShortPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash);
+KBOOL kFsCacheObjGetFullShortPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash);
+
+KBOOL kFsCacheFileSimpleOpenReadClose(PKFSCACHE pCache, PKFSOBJ pFileObj, KU64 offStart, void *pvBuf, KSIZE cbToRead);
+
+PKFSCACHE kFsCacheCreate(KU32 fFlags);
+void kFsCacheDestroy(PKFSCACHE);
+void kFsCacheInvalidateMissing(PKFSCACHE pCache);
+void kFsCacheInvalidateAll(PKFSCACHE pCache);
+void kFsCacheInvalidateAllAndCloseDirs(PKFSCACHE pCache, KBOOL fIncludingRoot);
+void kFsCacheInvalidateCustomMissing(PKFSCACHE pCache);
+void kFsCacheInvalidateCustomBoth(PKFSCACHE pCache);
+KBOOL kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot);
+KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir);
+
+#endif