1 /* $Id: kFsCache.c 3082 2017-10-02 19:23:17Z bird $ */
2 /** @file
3  * ntdircache.c - NT directory content cache.
4  */
5 
6 /*
7  * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25  * IN THE SOFTWARE.
26  *
27  * Alternatively, the content of this file may be used under the terms of the
28  * GPL version 2 or later, or LGPL version 2.1 or later.
29  */
30 
31 
32 /*********************************************************************************************************************************
33 *   Header Files                                                                                                                 *
34 *********************************************************************************************************************************/
35 #include <k/kHlp.h>
36 
37 #include "nthlp.h"
38 #include "ntstat.h"
39 
40 #include <stdio.h>
41 #include <mbstring.h>
42 #include <wchar.h>
43 #ifdef _MSC_VER
44 # include <intrin.h>
45 #endif
46 //#include <setjmp.h>
47 //#include <ctype.h>
48 
49 
50 //#include <Windows.h>
51 //#include <winternl.h>
52 
53 #include "kFsCache.h"
54 
55 
56 /*********************************************************************************************************************************
57 *   Defined Constants And Macros                                                                                                 *
58 *********************************************************************************************************************************/
59 /** @def KFSCACHE_LOG2
60  * More logging. */
61 #if 0
62 # define KFSCACHE_LOG2(a) KFSCACHE_LOG(a)
63 #else
64 # define KFSCACHE_LOG2(a) do { } while (0)
65 #endif
66 
67 
68 /*********************************************************************************************************************************
69 *   Structures and Typedefs                                                                                                      *
70 *********************************************************************************************************************************/
71 /**
72  * Used by the code re-populating a directory.
73  */
74 typedef struct KFSDIRREPOP
75 {
76     /** The old papChildren array. */
77     PKFSOBJ    *papOldChildren;
78     /** Number of children in the array. */
79     KU32        cOldChildren;
80     /** The index into papOldChildren we expect to find the next entry.  */
81     KU32        iNextOldChild;
82     /** Add this to iNextOldChild . */
83     KI32        cNextOldChildInc;
84     /** Pointer to the cache (name changes). */
85     PKFSCACHE   pCache;
86 } KFSDIRREPOP;
87 /** Pointer to directory re-population data. */
88 typedef KFSDIRREPOP *PKFSDIRREPOP;
89 
90 
91 
92 /*********************************************************************************************************************************
93 *   Internal Functions                                                                                                           *
94 *********************************************************************************************************************************/
95 static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError);
96 
97 
98 /**
99  * Retains a reference to a cache object, internal version.
100  *
101  * @returns pObj
102  * @param   pObj                The object.
103  */
kFsCacheObjRetainInternal(PKFSOBJ pObj)104 K_INLINE PKFSOBJ kFsCacheObjRetainInternal(PKFSOBJ pObj)
105 {
106     KU32 cRefs = ++pObj->cRefs;
107     kHlpAssert(cRefs < 16384);
108     K_NOREF(cRefs);
109     return pObj;
110 }
111 
112 
113 #ifndef NDEBUG
114 
115 /**
116  * Debug printing.
117  * @param   pszFormat           Debug format string.
118  * @param   ...                 Format argument.
119  */
kFsCacheDbgPrintfV(const char * pszFormat,va_list va)120 void kFsCacheDbgPrintfV(const char *pszFormat, va_list va)
121 {
122     if (1)
123     {
124         DWORD const dwSavedErr = GetLastError();
125 
126         fprintf(stderr, "debug: ");
127         vfprintf(stderr, pszFormat, va);
128 
129         SetLastError(dwSavedErr);
130     }
131 }
132 
133 
134 /**
135  * Debug printing.
136  * @param   pszFormat           Debug format string.
137  * @param   ...                 Format argument.
138  */
kFsCacheDbgPrintf(const char * pszFormat,...)139 void kFsCacheDbgPrintf(const char *pszFormat, ...)
140 {
141     if (1)
142     {
143         va_list va;
144         va_start(va, pszFormat);
145         kFsCacheDbgPrintfV(pszFormat, va);
146         va_end(va);
147     }
148 }
149 
150 #endif /* !NDEBUG */
151 
152 
153 
154 /**
155  * Hashes a string.
156  *
157  * @returns 32-bit string hash.
158  * @param   pszString           String to hash.
159  */
kFsCacheStrHash(const char * pszString)160 static KU32 kFsCacheStrHash(const char *pszString)
161 {
162     /* This algorithm was created for sdbm (a public-domain reimplementation of
163        ndbm) database library. it was found to do well in scrambling bits,
164        causing better distribution of the keys and fewer splits. it also happens
165        to be a good general hashing function with good distribution. the actual
166        function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
167        is the faster version used in gawk. [there is even a faster, duff-device
168        version] the magic constant 65599 was picked out of thin air while
169        experimenting with different constants, and turns out to be a prime.
170        this is one of the algorithms used in berkeley db (see sleepycat) and
171        elsewhere. */
172     KU32 uHash = 0;
173     KU32 uChar;
174     while ((uChar = (unsigned char)*pszString++) != 0)
175         uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
176     return uHash;
177 }
178 
179 
180 /**
181  * Hashes a string.
182  *
183  * @returns The string length.
184  * @param   pszString           String to hash.
185  * @param   puHash              Where to return the 32-bit string hash.
186  */
kFsCacheStrHashEx(const char * pszString,KU32 * puHash)187 static KSIZE kFsCacheStrHashEx(const char *pszString, KU32 *puHash)
188 {
189     const char * const pszStart = pszString;
190     KU32 uHash = 0;
191     KU32 uChar;
192     while ((uChar = (unsigned char)*pszString) != 0)
193     {
194         uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
195         pszString++;
196     }
197     *puHash = uHash;
198     return pszString - pszStart;
199 }
200 
201 
202 /**
203  * Hashes a substring.
204  *
205  * @returns 32-bit substring hash.
206  * @param   pchString           Pointer to the substring (not terminated).
207  * @param   cchString           The length of the substring.
208  */
kFsCacheStrHashN(const char * pchString,KSIZE cchString)209 static KU32 kFsCacheStrHashN(const char *pchString, KSIZE cchString)
210 {
211     KU32 uHash = 0;
212     while (cchString-- > 0)
213     {
214         KU32 uChar = (unsigned char)*pchString++;
215         uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
216     }
217     return uHash;
218 }
219 
220 
221 /**
222  * Hashes a UTF-16 string.
223  *
224  * @returns The string length in wchar_t units.
225  * @param   pwszString          String to hash.
226  * @param   puHash              Where to return the 32-bit string hash.
227  */
kFsCacheUtf16HashEx(const wchar_t * pwszString,KU32 * puHash)228 static KSIZE kFsCacheUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
229 {
230     const wchar_t * const pwszStart = pwszString;
231     KU32 uHash = 0;
232     KU32 uChar;
233     while ((uChar = *pwszString) != 0)
234     {
235         uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
236         pwszString++;
237     }
238     *puHash = uHash;
239     return pwszString - pwszStart;
240 }
241 
242 
243 /**
244  * Hashes a UTF-16 substring.
245  *
246  * @returns 32-bit substring hash.
247  * @param   pwcString           Pointer to the substring (not terminated).
248  * @param   cchString           The length of the substring (in wchar_t's).
249  */
kFsCacheUtf16HashN(const wchar_t * pwcString,KSIZE cwcString)250 static KU32 kFsCacheUtf16HashN(const wchar_t *pwcString, KSIZE cwcString)
251 {
252     KU32 uHash = 0;
253     while (cwcString-- > 0)
254     {
255         KU32 uChar = *pwcString++;
256         uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
257     }
258     return uHash;
259 }
260 
261 
262 /**
263  * For use when kFsCacheIAreEqualW hit's something non-trivial.
264  *
265  * @returns K_TRUE if equal, K_FALSE if different.
266  * @param   pwcName1            The first string.
267  * @param   pwcName2            The second string.
268  * @param   cwcName             The length of the two strings (in wchar_t's).
269  */
kFsCacheIAreEqualSlowW(const wchar_t * pwcName1,const wchar_t * pwcName2,KU16 cwcName)270 KBOOL kFsCacheIAreEqualSlowW(const wchar_t *pwcName1, const wchar_t *pwcName2, KU16 cwcName)
271 {
272     MY_UNICODE_STRING UniStr1 = { cwcName * sizeof(wchar_t), cwcName * sizeof(wchar_t), (wchar_t *)pwcName1 };
273     MY_UNICODE_STRING UniStr2 = { cwcName * sizeof(wchar_t), cwcName * sizeof(wchar_t), (wchar_t *)pwcName2 };
274     return g_pfnRtlEqualUnicodeString(&UniStr1, &UniStr2, TRUE /*fCaseInsensitive*/);
275 }
276 
277 
278 /**
279  * Compares two UTF-16 strings in a case-insensitive fashion.
280  *
281  * You would think we should be using _wscnicmp here instead, however it is
282  * locale dependent and defaults to ASCII upper/lower handling setlocale hasn't
283  * been called.
284  *
285  * @returns K_TRUE if equal, K_FALSE if different.
286  * @param   pwcName1            The first string.
287  * @param   pwcName2            The second string.
288  * @param   cwcName             The length of the two strings (in wchar_t's).
289  */
kFsCacheIAreEqualW(const wchar_t * pwcName1,const wchar_t * pwcName2,KU32 cwcName)290 K_INLINE KBOOL kFsCacheIAreEqualW(const wchar_t *pwcName1, const wchar_t *pwcName2, KU32 cwcName)
291 {
292     while (cwcName > 0)
293     {
294         wchar_t wc1 = *pwcName1;
295         wchar_t wc2 = *pwcName2;
296         if (wc1 == wc2)
297         { /* not unlikely */ }
298         else if (  (KU16)wc1 < (KU16)0xc0 /* U+00C0 is the first upper/lower letter after 'z'. */
299                 && (KU16)wc2 < (KU16)0xc0)
300         {
301             /* ASCII upper case. */
302             if ((KU16)wc1 - (KU16)0x61 < (KU16)26)
303                 wc1 &= ~(wchar_t)0x20;
304             if ((KU16)wc2 - (KU16)0x61 < (KU16)26)
305                 wc2 &= ~(wchar_t)0x20;
306             if (wc1 != wc2)
307                 return K_FALSE;
308         }
309         else
310             return kFsCacheIAreEqualSlowW(pwcName1, pwcName2, (KU16)cwcName);
311 
312         pwcName2++;
313         pwcName1++;
314         cwcName--;
315     }
316 
317     return K_TRUE;
318 }
319 
320 
321 /**
322  * Looks for '..' in the path.
323  *
324  * @returns K_TRUE if '..' component found, K_FALSE if not.
325  * @param   pszPath             The path.
326  * @param   cchPath             The length of the path.
327  */
kFsCacheHasDotDotA(const char * pszPath,KSIZE cchPath)328 static KBOOL kFsCacheHasDotDotA(const char *pszPath, KSIZE cchPath)
329 {
330     const char *pchDot = (const char *)kHlpMemChr(pszPath, '.', cchPath);
331     while (pchDot)
332     {
333         if (pchDot[1] != '.')
334         {
335             pchDot++;
336             pchDot = (const char *)kHlpMemChr(pchDot, '.', &pszPath[cchPath] - pchDot);
337         }
338         else
339         {
340             char ch;
341             if (   (ch = pchDot[2]) != '\0'
342                 && IS_SLASH(ch))
343             {
344                 if (pchDot == pszPath)
345                     return K_TRUE;
346                 ch = pchDot[-1];
347                 if (   IS_SLASH(ch)
348                     || ch == ':')
349                     return K_TRUE;
350             }
351             pchDot = (const char *)kHlpMemChr(pchDot + 2, '.', &pszPath[cchPath] - pchDot - 2);
352         }
353     }
354 
355     return K_FALSE;
356 }
357 
358 
359 /**
360  * Looks for '..' in the path.
361  *
362  * @returns K_TRUE if '..' component found, K_FALSE if not.
363  * @param   pwszPath            The path.
364  * @param   cwcPath             The length of the path (in wchar_t's).
365  */
kFsCacheHasDotDotW(const wchar_t * pwszPath,KSIZE cwcPath)366 static KBOOL kFsCacheHasDotDotW(const wchar_t *pwszPath, KSIZE cwcPath)
367 {
368     const wchar_t *pwcDot = wmemchr(pwszPath, '.', cwcPath);
369     while (pwcDot)
370     {
371         if (pwcDot[1] != '.')
372         {
373             pwcDot++;
374             pwcDot = wmemchr(pwcDot, '.', &pwszPath[cwcPath] - pwcDot);
375         }
376         else
377         {
378             wchar_t wch;
379             if (   (wch = pwcDot[2]) != '\0'
380                 && IS_SLASH(wch))
381             {
382                 if (pwcDot == pwszPath)
383                     return K_TRUE;
384                 wch = pwcDot[-1];
385                 if (   IS_SLASH(wch)
386                     || wch == ':')
387                     return K_TRUE;
388             }
389             pwcDot = wmemchr(pwcDot + 2, '.', &pwszPath[cwcPath] - pwcDot - 2);
390         }
391     }
392 
393     return K_FALSE;
394 }
395 
396 
397 /**
398  * Creates an ANSI hash table entry for the given path.
399  *
400  * @returns The hash table entry or NULL if out of memory.
401  * @param   pCache              The hash
402  * @param   pFsObj              The resulting object.
403  * @param   pszPath             The path.
404  * @param   cchPath             The length of the path.
405  * @param   uHashPath           The hash of the path.
406  * @param   fAbsolute           Whether it can be refreshed using an absolute
407  *                              lookup or requires the slow treatment.
408  * @parma   idxMissingGen       The missing generation index.
409  * @param   idxHashTab          The hash table index of the path.
410  * @param   enmError            The lookup error.
411  */
kFsCacheCreatePathHashTabEntryA(PKFSCACHE pCache,PKFSOBJ pFsObj,const char * pszPath,KU32 cchPath,KU32 uHashPath,KU32 idxHashTab,BOOL fAbsolute,KU32 idxMissingGen,KFSLOOKUPERROR enmError)412 static PKFSHASHA kFsCacheCreatePathHashTabEntryA(PKFSCACHE pCache, PKFSOBJ pFsObj, const char *pszPath, KU32 cchPath,
413                                                  KU32 uHashPath, KU32 idxHashTab, BOOL fAbsolute, KU32 idxMissingGen,
414                                                  KFSLOOKUPERROR enmError)
415 {
416     PKFSHASHA pHashEntry = (PKFSHASHA)kHlpAlloc(sizeof(*pHashEntry) + cchPath + 1);
417     if (pHashEntry)
418     {
419         pHashEntry->uHashPath       = uHashPath;
420         pHashEntry->cchPath         = (KU16)cchPath;
421         pHashEntry->fAbsolute       = fAbsolute;
422         pHashEntry->idxMissingGen   = (KU8)idxMissingGen;
423         pHashEntry->enmError        = enmError;
424         pHashEntry->pszPath         = (const char *)kHlpMemCopy(pHashEntry + 1, pszPath, cchPath + 1);
425         if (pFsObj)
426         {
427             pHashEntry->pFsObj      = kFsCacheObjRetainInternal(pFsObj);
428             pHashEntry->uCacheGen   = pFsObj->bObjType != KFSOBJ_TYPE_MISSING
429                                     ? pCache->auGenerations[       pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
430                                     : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
431             pFsObj->abUnused[0] += 1; // for debugging
432         }
433         else
434         {
435             pHashEntry->pFsObj      = NULL;
436             if (enmError != KFSLOOKUPERROR_UNSUPPORTED)
437                 pHashEntry->uCacheGen = pCache->auGenerationsMissing[idxMissingGen];
438             else
439                 pHashEntry->uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
440         }
441 
442         pHashEntry->pNext = pCache->apAnsiPaths[idxHashTab];
443         pCache->apAnsiPaths[idxHashTab] = pHashEntry;
444 
445         pCache->cbAnsiPaths += sizeof(*pHashEntry) + cchPath + 1;
446         pCache->cAnsiPaths++;
447         if (pHashEntry->pNext)
448             pCache->cAnsiPathCollisions++;
449     }
450     return pHashEntry;
451 }
452 
453 
454 /**
455  * Creates an UTF-16 hash table entry for the given path.
456  *
457  * @returns The hash table entry or NULL if out of memory.
458  * @param   pCache              The hash
459  * @param   pFsObj              The resulting object.
460  * @param   pwszPath            The path.
461  * @param   cwcPath             The length of the path (in wchar_t's).
462  * @param   uHashPath           The hash of the path.
463  * @param   fAbsolute           Whether it can be refreshed using an absolute
464  *                              lookup or requires the slow treatment.
465  * @parma   idxMissingGen       The missing generation index.
466  * @param   idxHashTab          The hash table index of the path.
467  * @param   enmError            The lookup error.
468  */
kFsCacheCreatePathHashTabEntryW(PKFSCACHE pCache,PKFSOBJ pFsObj,const wchar_t * pwszPath,KU32 cwcPath,KU32 uHashPath,KU32 idxHashTab,BOOL fAbsolute,KU32 idxMissingGen,KFSLOOKUPERROR enmError)469 static PKFSHASHW kFsCacheCreatePathHashTabEntryW(PKFSCACHE pCache, PKFSOBJ pFsObj, const wchar_t *pwszPath, KU32 cwcPath,
470                                                  KU32 uHashPath, KU32 idxHashTab, BOOL fAbsolute, KU32 idxMissingGen,
471                                                  KFSLOOKUPERROR enmError)
472 {
473     PKFSHASHW pHashEntry = (PKFSHASHW)kHlpAlloc(sizeof(*pHashEntry) + (cwcPath + 1) * sizeof(wchar_t));
474     if (pHashEntry)
475     {
476         pHashEntry->uHashPath       = uHashPath;
477         pHashEntry->cwcPath         = cwcPath;
478         pHashEntry->fAbsolute       = fAbsolute;
479         pHashEntry->idxMissingGen   = (KU8)idxMissingGen;
480         pHashEntry->enmError        = enmError;
481         pHashEntry->pwszPath        = (const wchar_t *)kHlpMemCopy(pHashEntry + 1, pwszPath, (cwcPath + 1) * sizeof(wchar_t));
482         if (pFsObj)
483         {
484             pHashEntry->pFsObj      = kFsCacheObjRetainInternal(pFsObj);
485             pHashEntry->uCacheGen   = pFsObj->bObjType != KFSOBJ_TYPE_MISSING
486                                     ? pCache->auGenerations[       pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
487                                     : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
488             pFsObj->abUnused[0] += 1; // for debugging
489         }
490         else
491         {
492             pHashEntry->pFsObj      = NULL;
493             if (enmError != KFSLOOKUPERROR_UNSUPPORTED)
494                 pHashEntry->uCacheGen = pCache->auGenerationsMissing[idxMissingGen];
495             else
496                 pHashEntry->uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
497         }
498 
499         pHashEntry->pNext = pCache->apUtf16Paths[idxHashTab];
500         pCache->apUtf16Paths[idxHashTab] = pHashEntry;
501 
502         pCache->cbUtf16Paths += sizeof(*pHashEntry) + (cwcPath + 1) * sizeof(wchar_t);
503         pCache->cUtf16Paths++;
504         if (pHashEntry->pNext)
505             pCache->cAnsiPathCollisions++;
506     }
507     return pHashEntry;
508 }
509 
510 
511 /**
512  * Links the child in under the parent.
513  *
514  * @returns K_TRUE on success, K_FALSE if out of memory.
515  * @param   pParent             The parent node.
516  * @param   pChild              The child node.
517  */
kFsCacheDirAddChild(PKFSCACHE pCache,PKFSDIR pParent,PKFSOBJ pChild,KFSLOOKUPERROR * penmError)518 static KBOOL kFsCacheDirAddChild(PKFSCACHE pCache, PKFSDIR pParent, PKFSOBJ pChild, KFSLOOKUPERROR *penmError)
519 {
520     if (pParent->cChildren >= pParent->cChildrenAllocated)
521     {
522         void *pvNew = kHlpRealloc(pParent->papChildren, (pParent->cChildrenAllocated + 16) * sizeof(pParent->papChildren[0]));
523         if (!pvNew)
524             return K_FALSE;
525         pParent->papChildren = (PKFSOBJ *)pvNew;
526         pParent->cChildrenAllocated += 16;
527         pCache->cbObjects += 16 * sizeof(pParent->papChildren[0]);
528     }
529     pParent->papChildren[pParent->cChildren++] = kFsCacheObjRetainInternal(pChild);
530     return K_TRUE;
531 }
532 
533 
534 /**
535  * Creates a new cache object.
536  *
537  * @returns Pointer (with 1 reference) to the new object.  The object will not
538  *          be linked to the parent directory yet.
539  *
540  *          NULL if we're out of memory.
541  *
542  * @param   pCache          The cache.
543  * @param   pParent         The parent directory.
544  * @param   pszName         The ANSI name.
545  * @param   cchName         The length of the ANSI name.
546  * @param   pwszName        The UTF-16 name.
547  * @param   cwcName         The length of the UTF-16 name.
548  * @param   pszShortName    The ANSI short name, NULL if none.
549  * @param   cchShortName    The length of the ANSI short name, 0 if none.
550  * @param   pwszShortName   The UTF-16 short name, NULL if none.
551  * @param   cwcShortName    The length of the UTF-16 short name, 0 if none.
552  * @param   bObjType        The objct type.
553  * @param   penmError       Where to explain failures.
554  */
kFsCacheCreateObject(PKFSCACHE pCache,PKFSDIR pParent,char const * pszName,KU16 cchName,wchar_t const * pwszName,KU16 cwcName,char const * pszShortName,KU16 cchShortName,wchar_t const * pwszShortName,KU16 cwcShortName,KU8 bObjType,KFSLOOKUPERROR * penmError)555 PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,
556                              char const *pszName, KU16 cchName, wchar_t const *pwszName, KU16 cwcName,
557 #ifdef KFSCACHE_CFG_SHORT_NAMES
558                              char const *pszShortName, KU16 cchShortName, wchar_t const *pwszShortName, KU16 cwcShortName,
559 #endif
560                              KU8 bObjType, KFSLOOKUPERROR *penmError)
561 {
562     /*
563      * Allocate the object.
564      */
565     KBOOL const fDirish = bObjType != KFSOBJ_TYPE_FILE && bObjType != KFSOBJ_TYPE_OTHER;
566     KSIZE const cbObj   = fDirish ? sizeof(KFSDIR) : sizeof(KFSOBJ);
567     KSIZE const cbNames = (cwcName + 1) * sizeof(wchar_t)                           + cchName + 1
568 #ifdef KFSCACHE_CFG_SHORT_NAMES
569                         + (cwcShortName > 0 ? (cwcShortName + 1) * sizeof(wchar_t)  + cchShortName + 1 : 0)
570 #endif
571                           ;
572     PKFSOBJ pObj;
573     kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
574 
575     pObj = (PKFSOBJ)kHlpAlloc(cbObj + cbNames);
576     if (pObj)
577     {
578         KU8 *pbExtra = (KU8 *)pObj + cbObj;
579 
580         pCache->cbObjects += cbObj + cbNames;
581         pCache->cObjects++;
582 
583         /*
584          * Initialize the object.
585          */
586         pObj->u32Magic      = KFSOBJ_MAGIC;
587         pObj->cRefs         = 1;
588         pObj->uCacheGen     = bObjType != KFSOBJ_TYPE_MISSING
589                             ? pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
590                             : pCache->auGenerationsMissing[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
591         pObj->bObjType      = bObjType;
592         pObj->fHaveStats    = K_FALSE;
593         pObj->abUnused[0]   = K_FALSE;
594         pObj->abUnused[1]   = K_FALSE;
595         pObj->fFlags        = pParent->Obj.fFlags & KFSOBJ_F_INHERITED_MASK;
596         pObj->pParent       = pParent;
597         pObj->uNameHash     = 0;
598         pObj->pNextNameHash = NULL;
599         pObj->pNameAlloc    = NULL;
600         pObj->pUserDataHead = NULL;
601 
602 #ifdef KFSCACHE_CFG_UTF16
603         pObj->cwcParent = pParent->Obj.cwcParent + pParent->Obj.cwcName + !!pParent->Obj.cwcName;
604         pObj->pwszName  = (wchar_t *)kHlpMemCopy(pbExtra, pwszName, cwcName * sizeof(wchar_t));
605         pObj->cwcName   = cwcName;
606         pbExtra += cwcName * sizeof(wchar_t);
607         *pbExtra++ = '\0';
608         *pbExtra++ = '\0';
609 # ifdef KFSCACHE_CFG_SHORT_NAMES
610         pObj->cwcShortParent = pParent->Obj.cwcShortParent + pParent->Obj.cwcShortName + !!pParent->Obj.cwcShortName;
611         if (cwcShortName)
612         {
613             pObj->pwszShortName = (wchar_t *)kHlpMemCopy(pbExtra, pwszShortName, cwcShortName * sizeof(wchar_t));
614             pObj->cwcShortName  = cwcShortName;
615             pbExtra += cwcShortName * sizeof(wchar_t);
616             *pbExtra++ = '\0';
617             *pbExtra++ = '\0';
618         }
619         else
620         {
621             pObj->pwszShortName = pObj->pwszName;
622             pObj->cwcShortName  = cwcName;
623         }
624 # endif
625 #endif
626         pObj->cchParent = pParent->Obj.cchParent + pParent->Obj.cchName + !!pParent->Obj.cchName;
627         pObj->pszName   = (char *)kHlpMemCopy(pbExtra, pszName, cchName);
628         pObj->cchName   = cchName;
629         pbExtra += cchName;
630         *pbExtra++ = '\0';
631 # ifdef KFSCACHE_CFG_SHORT_NAMES
632         pObj->cchShortParent = pParent->Obj.cchShortParent + pParent->Obj.cchShortName + !!pParent->Obj.cchShortName;
633         if (cchShortName)
634         {
635             pObj->pszShortName = (char *)kHlpMemCopy(pbExtra, pszShortName, cchShortName);
636             pObj->cchShortName = cchShortName;
637             pbExtra += cchShortName;
638             *pbExtra++ = '\0';
639         }
640         else
641         {
642             pObj->pszShortName = pObj->pszName;
643             pObj->cchShortName = cchName;
644         }
645 #endif
646         kHlpAssert(pbExtra - (KU8 *)pObj == cbObj);
647 
648         /*
649          * Type specific initialization.
650          */
651         if (fDirish)
652         {
653             PKFSDIR pDirObj = (PKFSDIR)pObj;
654             pDirObj->cChildren          = 0;
655             pDirObj->cChildrenAllocated = 0;
656             pDirObj->papChildren        = NULL;
657             pDirObj->fHashTabMask       = 0;
658             pDirObj->papHashTab         = NULL;
659             pDirObj->hDir               = INVALID_HANDLE_VALUE;
660             pDirObj->uDevNo             = pParent->uDevNo;
661             pDirObj->iLastWrite         = 0;
662             pDirObj->fPopulated         = K_FALSE;
663         }
664     }
665     else
666         *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
667     return pObj;
668 }
669 
670 
671 /**
672  * Creates a new object given wide char names.
673  *
674  * This function just converts the paths and calls kFsCacheCreateObject.
675  *
676  *
677  * @returns Pointer (with 1 reference) to the new object.  The object will not
678  *          be linked to the parent directory yet.
679  *
680  *          NULL if we're out of memory.
681  *
682  * @param   pCache          The cache.
683  * @param   pParent         The parent directory.
684  * @param   pszName         The ANSI name.
685  * @param   cchName         The length of the ANSI name.
686  * @param   pwszName        The UTF-16 name.
687  * @param   cwcName         The length of the UTF-16 name.
688  * @param   pwszShortName   The UTF-16 short name, NULL if none.
689  * @param   cwcShortName    The length of the UTF-16 short name, 0 if none.
690  * @param   bObjType        The objct type.
691  * @param   penmError       Where to explain failures.
692  */
kFsCacheCreateObjectW(PKFSCACHE pCache,PKFSDIR pParent,wchar_t const * pwszName,KU32 cwcName,wchar_t const * pwszShortName,KU32 cwcShortName,KU8 bObjType,KFSLOOKUPERROR * penmError)693 PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName,
694 #ifdef KFSCACHE_CFG_SHORT_NAMES
695                               wchar_t const *pwszShortName, KU32 cwcShortName,
696 #endif
697                               KU8 bObjType, KFSLOOKUPERROR *penmError)
698 {
699     /* Convert names to ANSI first so we know their lengths. */
700     char szName[KFSCACHE_CFG_MAX_ANSI_NAME];
701     int  cchName = WideCharToMultiByte(CP_ACP, 0, pwszName, cwcName, szName, sizeof(szName) - 1, NULL, NULL);
702     if (cchName >= 0)
703     {
704 #ifdef KFSCACHE_CFG_SHORT_NAMES
705         char szShortName[12*3 + 1];
706         int  cchShortName = 0;
707         if (   cwcShortName == 0
708             || (cchShortName = WideCharToMultiByte(CP_ACP, 0, pwszShortName, cwcShortName,
709                                                    szShortName, sizeof(szShortName) - 1, NULL, NULL)) > 0)
710 #endif
711         {
712             return kFsCacheCreateObject(pCache, pParent,
713                                         szName, cchName, pwszName, cwcName,
714 #ifdef KFSCACHE_CFG_SHORT_NAMES
715                                         szShortName, cchShortName, pwszShortName, cwcShortName,
716 #endif
717                                         bObjType, penmError);
718         }
719     }
720     *penmError = KFSLOOKUPERROR_ANSI_CONVERSION_ERROR;
721     return NULL;
722 }
723 
724 
725 /**
726  * Creates a missing object.
727  *
728  * This is used for caching negative results.
729  *
730  * @returns Pointer to the newly created object on success (already linked into
731  *          pParent).  No reference.
732  *
733  *          NULL on failure.
734  *
735  * @param   pCache              The cache.
736  * @param   pParent             The parent directory.
737  * @param   pchName             The name.
738  * @param   cchName             The length of the name.
739  * @param   penmError           Where to return failure explanations.
740  */
kFsCacheCreateMissingA(PKFSCACHE pCache,PKFSDIR pParent,const char * pchName,KU32 cchName,KFSLOOKUPERROR * penmError)741 static PKFSOBJ kFsCacheCreateMissingA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName,
742                                       KFSLOOKUPERROR *penmError)
743 {
744     /*
745      * Just convert the name to UTF-16 and call kFsCacheCreateObject to do the job.
746      */
747     wchar_t wszName[KFSCACHE_CFG_MAX_PATH];
748     int cwcName = MultiByteToWideChar(CP_ACP, 0, pchName, cchName, wszName, KFSCACHE_CFG_MAX_UTF16_NAME - 1);
749     if (cwcName > 0)
750     {
751         /** @todo check that it actually doesn't exists before we add it.  We should not
752          *        trust the directory enumeration here, or maybe we should?? */
753 
754         PKFSOBJ pMissing = kFsCacheCreateObject(pCache, pParent, pchName, cchName, wszName, cwcName,
755 #ifdef KFSCACHE_CFG_SHORT_NAMES
756                                                 NULL, 0, NULL, 0,
757 #endif
758                                                 KFSOBJ_TYPE_MISSING, penmError);
759         if (pMissing)
760         {
761             KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError);
762             kFsCacheObjRelease(pCache, pMissing);
763             return fRc ? pMissing : NULL;
764         }
765         return NULL;
766     }
767     *penmError = KFSLOOKUPERROR_UTF16_CONVERSION_ERROR;
768     return NULL;
769 }
770 
771 
772 /**
773  * Creates a missing object, UTF-16 version.
774  *
775  * This is used for caching negative results.
776  *
777  * @returns Pointer to the newly created object on success (already linked into
778  *          pParent).  No reference.
779  *
780  *          NULL on failure.
781  *
782  * @param   pCache              The cache.
783  * @param   pParent             The parent directory.
784  * @param   pwcName             The name.
785  * @param   cwcName             The length of the name.
786  * @param   penmError           Where to return failure explanations.
787  */
kFsCacheCreateMissingW(PKFSCACHE pCache,PKFSDIR pParent,const wchar_t * pwcName,KU32 cwcName,KFSLOOKUPERROR * penmError)788 static PKFSOBJ kFsCacheCreateMissingW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName,
789                                       KFSLOOKUPERROR *penmError)
790 {
791     /** @todo check that it actually doesn't exists before we add it.  We should not
792      *        trust the directory enumeration here, or maybe we should?? */
793     PKFSOBJ pMissing = kFsCacheCreateObjectW(pCache, pParent, pwcName, cwcName,
794 #ifdef KFSCACHE_CFG_SHORT_NAMES
795                                              NULL, 0,
796 #endif
797                                              KFSOBJ_TYPE_MISSING, penmError);
798     if (pMissing)
799     {
800         KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError);
801         kFsCacheObjRelease(pCache, pMissing);
802         return fRc ? pMissing : NULL;
803     }
804     return NULL;
805 }
806 
807 
808 /**
809  * Does the growing of names.
810  *
811  * @returns pCur
812  * @param   pCache          The cache.
813  * @param   pCur            The object.
814  * @param   pchName         The name (not necessarily terminated).
815  * @param   cchName         Name length.
816  * @param   pwcName         The UTF-16 name (not necessarily terminated).
817  * @param   cwcName         The length of the UTF-16 name in wchar_t's.
818  * @param   pchShortName    The short name.
819  * @param   cchShortName    The length of the short name.  This is 0 if no short
820  *                          name.
821  * @param   pwcShortName    The short UTF-16 name.
822  * @param   cwcShortName    The length of the short UTF-16 name.  This is 0 if
823  *                          no short name.
824  */
kFsCacheRefreshGrowNames(PKFSCACHE pCache,PKFSOBJ pCur,const char * pchName,KU32 cchName,wchar_t const * pwcName,KU32 cwcName,const char * pchShortName,KU32 cchShortName,wchar_t const * pwcShortName,KU32 cwcShortName)825 static PKFSOBJ kFsCacheRefreshGrowNames(PKFSCACHE pCache, PKFSOBJ pCur,
826                                         const char *pchName, KU32 cchName,
827                                         wchar_t const *pwcName, KU32 cwcName
828 #ifdef KFSCACHE_CFG_SHORT_NAMES
829                                         , const char *pchShortName, KU32 cchShortName,
830                                         wchar_t const *pwcShortName, KU32 cwcShortName
831 #endif
832                                         )
833 {
834     PKFSOBJNAMEALLOC    pNameAlloc;
835     char               *pch;
836     KU32                cbNeeded;
837 
838     pCache->cNameGrowths++;
839 
840     /*
841      * Figure out our requirements.
842      */
843     cbNeeded = sizeof(KU32) + cchName + 1;
844 #ifdef KFSCACHE_CFG_UTF16
845     cbNeeded += (cwcName + 1) * sizeof(wchar_t);
846 #endif
847 #ifdef KFSCACHE_CFG_SHORT_NAMES
848     cbNeeded += cchShortName + !!cchShortName;
849 # ifdef KFSCACHE_CFG_UTF16
850     cbNeeded += (cwcShortName + !!cwcShortName) * sizeof(wchar_t);
851 # endif
852 #endif
853     cbNeeded = K_ALIGN_Z(cbNeeded, 8); /* Memory will likely be 8 or 16 byte aligned, so we might just claim it. */
854 
855     /*
856      * Allocate memory.
857      */
858     pNameAlloc = pCur->pNameAlloc;
859     if (!pNameAlloc)
860     {
861         pNameAlloc = (PKFSOBJNAMEALLOC)kHlpAlloc(cbNeeded);
862         if (!pNameAlloc)
863             return pCur;
864         pCache->cbObjects += cbNeeded;
865         pCur->pNameAlloc = pNameAlloc;
866         pNameAlloc->cb = cbNeeded;
867     }
868     else if (pNameAlloc->cb < cbNeeded)
869     {
870         pNameAlloc = (PKFSOBJNAMEALLOC)kHlpRealloc(pNameAlloc, cbNeeded);
871         if (!pNameAlloc)
872             return pCur;
873         pCache->cbObjects += cbNeeded - pNameAlloc->cb;
874         pCur->pNameAlloc = pNameAlloc;
875         pNameAlloc->cb = cbNeeded;
876     }
877 
878     /*
879      * Copy out the new names, starting with the wide char ones to avoid misaligning them.
880      */
881     pch = &pNameAlloc->abSpace[0];
882 
883 #ifdef KFSCACHE_CFG_UTF16
884     pCur->pwszName = (wchar_t *)pch;
885     pCur->cwcName  = cwcName;
886     pch = kHlpMemPCopy(pch, pwcName, cwcName * sizeof(wchar_t));
887     *pch++ = '\0';
888     *pch++ = '\0';
889 
890 # ifdef KFSCACHE_CFG_SHORT_NAMES
891     if (cwcShortName == 0)
892     {
893         pCur->pwszShortName = pCur->pwszName;
894         pCur->cwcShortName  = pCur->cwcName;
895     }
896     else
897     {
898         pCur->pwszShortName = (wchar_t *)pch;
899         pCur->cwcShortName  = cwcShortName;
900         pch = kHlpMemPCopy(pch, pwcShortName, cwcShortName * sizeof(wchar_t));
901         *pch++ = '\0';
902         *pch++ = '\0';
903     }
904 # endif
905 #endif
906 
907     pCur->pszName = pch;
908     pCur->cchName = cchName;
909     pch = kHlpMemPCopy(pch, pchName, cchShortName);
910     *pch++ = '\0';
911 
912 #ifdef KFSCACHE_CFG_SHORT_NAMES
913     if (cchShortName == 0)
914     {
915         pCur->pszShortName = pCur->pszName;
916         pCur->cchShortName = pCur->cchName;
917     }
918     else
919     {
920         pCur->pszShortName = pch;
921         pCur->cchShortName = cchShortName;
922         pch = kHlpMemPCopy(pch, pchShortName, cchShortName);
923         *pch++ = '\0';
924     }
925 #endif
926 
927     return pCur;
928 }
929 
930 
931 /**
932  * Worker for kFsCacheDirFindOldChild that refreshes the file ID value on an
933  * object found by name.
934  *
935  * @returns pCur.
936  * @param   pDirRePop       Repopulation data.
937  * @param   pCur            The object to check the names of.
938  * @param   idFile          The file ID.
939  */
kFsCacheDirRefreshOldChildFileId(PKFSDIRREPOP pDirRePop,PKFSOBJ pCur,KI64 idFile)940 static PKFSOBJ kFsCacheDirRefreshOldChildFileId(PKFSDIRREPOP pDirRePop, PKFSOBJ pCur, KI64 idFile)
941 {
942     KFSCACHE_LOG(("Refreshing %s/%s/ - %s changed file ID from %#llx -> %#llx...\n",
943                   pCur->pParent->Obj.pParent->Obj.pszName, pCur->pParent->Obj.pszName, pCur->pszName,
944                   pCur->Stats.st_ino, idFile));
945     pCur->Stats.st_ino = idFile;
946     /** @todo inform user data items...  */
947     return pCur;
948 }
949 
950 
951 /**
952  * Worker for kFsCacheDirFindOldChild that checks the names after an old object
953  * has been found the file ID.
954  *
955  * @returns pCur.
956  * @param   pDirRePop       Repopulation data.
957  * @param   pCur            The object to check the names of.
958  * @param   pwcName         The file name.
959  * @param   cwcName         The length of the filename (in wchar_t's).
960  * @param   pwcShortName    The short name, if present.
961  * @param   cwcShortName    The length of the short name (in wchar_t's).
962  */
kFsCacheDirRefreshOldChildName(PKFSDIRREPOP pDirRePop,PKFSOBJ pCur,wchar_t const * pwcName,KU32 cwcName,wchar_t const * pwcShortName,KU32 cwcShortName)963 static PKFSOBJ kFsCacheDirRefreshOldChildName(PKFSDIRREPOP pDirRePop, PKFSOBJ pCur, wchar_t const *pwcName, KU32 cwcName
964 #ifdef KFSCACHE_CFG_SHORT_NAMES
965                                               , wchar_t const *pwcShortName, KU32 cwcShortName
966 #endif
967                                               )
968 {
969     char szName[KFSCACHE_CFG_MAX_ANSI_NAME];
970     int  cchName;
971 
972     pDirRePop->pCache->cNameChanges++;
973 
974     /*
975      * Convert the names to ANSI first, that way we know all the lengths.
976      */
977     cchName = WideCharToMultiByte(CP_ACP, 0, pwcName, cwcName, szName, sizeof(szName) - 1, NULL, NULL);
978     if (cchName >= 0)
979     {
980 #ifdef KFSCACHE_CFG_SHORT_NAMES
981         char szShortName[12*3 + 1];
982         int  cchShortName = 0;
983         if (   cwcShortName == 0
984             || (cchShortName = WideCharToMultiByte(CP_ACP, 0, pwcShortName, cwcShortName,
985                                                    szShortName, sizeof(szShortName) - 1, NULL, NULL)) > 0)
986 #endif
987         {
988             /*
989              * Shortening is easy for non-directory objects, for
990              * directory object we're only good when the length doesn't change
991              * on any of the components (cchParent et al).
992              *
993              * This deals with your typical xxxx.ext.tmp -> xxxx.ext renames.
994              */
995             if (   cchName <= pCur->cchName
996 #ifdef KFSCACHE_CFG_UTF16
997                 && cwcName <= pCur->cwcName
998 #endif
999 #ifdef KFSCACHE_CFG_SHORT_NAMES
1000                 && (   cchShortName == 0
1001                     || (   cchShortName <= pCur->cchShortName
1002                         && pCur->pszShortName != pCur->pszName
1003 # ifdef KFSCACHE_CFG_UTF16
1004                         && cwcShortName <= pCur->cwcShortName
1005                         && pCur->pwszShortName != pCur->pwszName
1006 # endif
1007                        )
1008                    )
1009 #endif
1010                )
1011             {
1012                 if (   pCur->bObjType != KFSOBJ_TYPE_DIR
1013                     || (   cchName == pCur->cchName
1014 #ifdef KFSCACHE_CFG_UTF16
1015                         && cwcName == pCur->cwcName
1016 #endif
1017 #ifdef KFSCACHE_CFG_SHORT_NAMES
1018                         && (   cchShortName == 0
1019                             || (   cchShortName == pCur->cchShortName
1020 # ifdef KFSCACHE_CFG_UTF16
1021                                 && cwcShortName == pCur->cwcShortName
1022                                 )
1023 # endif
1024                            )
1025 #endif
1026                        )
1027                    )
1028                 {
1029                     KFSCACHE_LOG(("Refreshing %ls - name changed to '%*.*ls'\n", pCur->pwszName, cwcName, cwcName, pwcName));
1030                     *(char *)kHlpMemPCopy((void *)pCur->pszName, szName, cchName) = '\0';
1031                     pCur->cchName = cchName;
1032 #ifdef KFSCACHE_CFG_UTF16
1033                     *(wchar_t *)kHlpMemPCopy((void *)pCur->pwszName, pwcName, cwcName * sizeof(wchar_t)) = '\0';
1034                     pCur->cwcName = cwcName;
1035 #endif
1036 #ifdef KFSCACHE_CFG_SHORT_NAMES
1037                     *(char *)kHlpMemPCopy((void *)pCur->pszShortName, szShortName, cchShortName) = '\0';
1038                     pCur->cchShortName = cchShortName;
1039 # ifdef KFSCACHE_CFG_UTF16
1040                     *(wchar_t *)kHlpMemPCopy((void *)pCur->pwszShortName, pwcShortName, cwcShortName * sizeof(wchar_t)) = '\0';
1041                     pCur->cwcShortName = cwcShortName;
1042 # endif
1043 #endif
1044                     return pCur;
1045                 }
1046             }
1047 
1048             return kFsCacheRefreshGrowNames(pDirRePop->pCache, pCur, szName, cchName, pwcName, cwcName,
1049 #ifdef KFSCACHE_CFG_SHORT_NAMES
1050                                             szShortName, cchShortName, pwcShortName, cwcShortName
1051 #endif
1052                                             );
1053         }
1054     }
1055 
1056     fprintf(stderr, "kFsCacheDirRefreshOldChildName: WideCharToMultiByte error\n");
1057     return pCur;
1058 }
1059 
1060 
1061 /**
1062  * Worker for kFsCacheDirFindOldChild that checks the names after an old object
1063  * has been found by the file ID.
1064  *
1065  * @returns pCur.
1066  * @param   pDirRePop       Repopulation data.
1067  * @param   pCur            The object to check the names of.
1068  * @param   pwcName         The file name.
1069  * @param   cwcName         The length of the filename (in wchar_t's).
1070  * @param   pwcShortName    The short name, if present.
1071  * @param   cwcShortName    The length of the short name (in wchar_t's).
1072  */
kFsCacheDirCheckOldChildName(PKFSDIRREPOP pDirRePop,PKFSOBJ pCur,wchar_t const * pwcName,KU32 cwcName,wchar_t const * pwcShortName,KU32 cwcShortName)1073 K_INLINE PKFSOBJ kFsCacheDirCheckOldChildName(PKFSDIRREPOP pDirRePop, PKFSOBJ pCur, wchar_t const *pwcName, KU32 cwcName
1074 #ifdef KFSCACHE_CFG_SHORT_NAMES
1075                                               , wchar_t const *pwcShortName, KU32 cwcShortName
1076 #endif
1077                                               )
1078 {
1079     if (   pCur->cwcName == cwcName
1080         && kHlpMemComp(pCur->pwszName, pwcName, cwcName * sizeof(wchar_t)) == 0)
1081     {
1082 #ifdef KFSCACHE_CFG_SHORT_NAMES
1083         if (cwcShortName == 0
1084             ?    pCur->pwszShortName == pCur->pwszName
1085               || (   pCur->cwcShortName == cwcName
1086                   && kHlpMemComp(pCur->pwszShortName, pCur->pwszName, cwcName * sizeof(wchar_t)) == 0)
1087             :    pCur->cwcShortName == cwcShortName
1088               && kHlpMemComp(pCur->pwszShortName, pwcShortName, cwcShortName * sizeof(wchar_t)) == 0 )
1089 #endif
1090         {
1091             return pCur;
1092         }
1093     }
1094 #ifdef KFSCACHE_CFG_SHORT_NAMES
1095     return kFsCacheDirRefreshOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1096 #else
1097     return kFsCacheDirRefreshOldChildName(pDirRePop, pCur, pwcName, cwcName);
1098 #endif
1099 }
1100 
1101 
1102 /**
1103  * Worker for kFsCachePopuplateOrRefreshDir that locates an old child object
1104  * while re-populating a directory.
1105  *
1106  * @returns Pointer to the existing object if found, NULL if not.
1107  * @param   pDirRePop       Repopulation data.
1108  * @param   idFile          The file ID, 0 if none.
1109  * @param   pwcName         The file name.
1110  * @param   cwcName         The length of the filename (in wchar_t's).
1111  * @param   pwcShortName    The short name, if present.
1112  * @param   cwcShortName    The length of the short name (in wchar_t's).
1113  */
kFsCacheDirFindOldChildSlow(PKFSDIRREPOP pDirRePop,KI64 idFile,wchar_t const * pwcName,KU32 cwcName,wchar_t const * pwcShortName,KU32 cwcShortName)1114 static PKFSOBJ kFsCacheDirFindOldChildSlow(PKFSDIRREPOP pDirRePop, KI64 idFile, wchar_t const *pwcName, KU32 cwcName
1115 #ifdef KFSCACHE_CFG_SHORT_NAMES
1116                                            , wchar_t const *pwcShortName, KU32 cwcShortName
1117 #endif
1118                                            )
1119 {
1120     KU32        cOldChildren  = pDirRePop->cOldChildren;
1121     KU32 const  iNextOldChild = K_MIN(pDirRePop->iNextOldChild, cOldChildren - 1);
1122     KU32        iCur;
1123     KI32        cInc;
1124     KI32        cDirLefts;
1125 
1126     kHlpAssertReturn(cOldChildren > 0, NULL);
1127 
1128     /*
1129      * Search by file ID first, if we've got one.
1130      * ASSUMES that KU32 wraps around when -1 is added to 0.
1131      */
1132     if (   idFile != 0
1133         && idFile != KI64_MAX
1134         && idFile != KI64_MIN)
1135     {
1136         cInc = pDirRePop->cNextOldChildInc;
1137         kHlpAssert(cInc == -1 || cInc == 1);
1138         for (cDirLefts = 2; cDirLefts > 0; cDirLefts--)
1139         {
1140             for (iCur = iNextOldChild; iCur < cOldChildren; iCur += cInc)
1141             {
1142                 PKFSOBJ pCur = pDirRePop->papOldChildren[iCur];
1143                 if (pCur->Stats.st_ino == idFile)
1144                 {
1145                     /* Remove it and check the name. */
1146                     pDirRePop->cOldChildren = --cOldChildren;
1147                     if (iCur < cOldChildren)
1148                         pDirRePop->papOldChildren[iCur] = pDirRePop->papOldChildren[cOldChildren];
1149                     else
1150                         cInc = -1;
1151                     pDirRePop->cNextOldChildInc = cInc;
1152                     pDirRePop->iNextOldChild    = iCur + cInc;
1153 
1154 #ifdef KFSCACHE_CFG_SHORT_NAMES
1155                     return kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1156 #else
1157                     return kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1158 #endif
1159                 }
1160             }
1161             cInc = -cInc;
1162         }
1163     }
1164 
1165     /*
1166      * Search by name.
1167      * ASSUMES that KU32 wraps around when -1 is added to 0.
1168      */
1169     cInc = pDirRePop->cNextOldChildInc;
1170     kHlpAssert(cInc == -1 || cInc == 1);
1171     for (cDirLefts = 2; cDirLefts > 0; cDirLefts--)
1172     {
1173         for (iCur = iNextOldChild; iCur < cOldChildren; iCur += cInc)
1174         {
1175             PKFSOBJ pCur = pDirRePop->papOldChildren[iCur];
1176             if (   (   pCur->cwcName == cwcName
1177                     && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
1178 #ifdef KFSCACHE_CFG_SHORT_NAMES
1179                 || (   pCur->cwcShortName == cwcName
1180                     && pCur->pwszShortName != pCur->pwszName
1181                     && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
1182 #endif
1183                )
1184             {
1185                 /* Do this first so the compiler can share the rest with the above file ID return. */
1186                 if (pCur->Stats.st_ino == idFile)
1187                 { /* likely */ }
1188                 else
1189                     pCur = kFsCacheDirRefreshOldChildFileId(pDirRePop, pCur, idFile);
1190 
1191                 /* Remove it and check the name. */
1192                 pDirRePop->cOldChildren = --cOldChildren;
1193                 if (iCur < cOldChildren)
1194                     pDirRePop->papOldChildren[iCur] = pDirRePop->papOldChildren[cOldChildren];
1195                 else
1196                     cInc = -1;
1197                 pDirRePop->cNextOldChildInc = cInc;
1198                 pDirRePop->iNextOldChild    = iCur + cInc;
1199 
1200 #ifdef KFSCACHE_CFG_SHORT_NAMES
1201                 return kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1202 #else
1203                 return kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1204 #endif
1205             }
1206         }
1207         cInc = -cInc;
1208     }
1209 
1210     return NULL;
1211 }
1212 
1213 
1214 
1215 /**
1216  * Worker for kFsCachePopuplateOrRefreshDir that locates an old child object
1217  * while re-populating a directory.
1218  *
1219  * @returns Pointer to the existing object if found, NULL if not.
1220  * @param   pDirRePop       Repopulation data.
1221  * @param   idFile          The file ID, 0 if none.
1222  * @param   pwcName         The file name.
1223  * @param   cwcName         The length of the filename (in wchar_t's).
1224  * @param   pwcShortName    The short name, if present.
1225  * @param   cwcShortName    The length of the short name (in wchar_t's).
1226  */
kFsCacheDirFindOldChild(PKFSDIRREPOP pDirRePop,KI64 idFile,wchar_t const * pwcName,KU32 cwcName,wchar_t const * pwcShortName,KU32 cwcShortName)1227 K_INLINE PKFSOBJ kFsCacheDirFindOldChild(PKFSDIRREPOP pDirRePop, KI64 idFile, wchar_t const *pwcName, KU32 cwcName
1228 #ifdef KFSCACHE_CFG_SHORT_NAMES
1229                                          , wchar_t const *pwcShortName, KU32 cwcShortName
1230 #endif
1231                                          )
1232 {
1233     /*
1234      * We only check the iNextOldChild element here, hoping that the compiler
1235      * will actually inline this code, letting the slow version of the function
1236      * do the rest.
1237      */
1238     KU32 cOldChildren = pDirRePop->cOldChildren;
1239     if (cOldChildren > 0)
1240     {
1241         KU32 const  iNextOldChild = K_MIN(pDirRePop->iNextOldChild, cOldChildren - 1);
1242         PKFSOBJ     pCur          = pDirRePop->papOldChildren[iNextOldChild];
1243 
1244         if (   pCur->Stats.st_ino == idFile
1245             && idFile != 0
1246             && idFile != KI64_MAX
1247             && idFile != KI64_MIN)
1248             pCur = kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1249         else if (   pCur->cwcName == cwcName
1250                  && kHlpMemComp(pCur->pwszName,  pwcName, cwcName * sizeof(wchar_t)) == 0)
1251         {
1252             if (pCur->Stats.st_ino == idFile)
1253             { /* likely */ }
1254             else
1255                 pCur = kFsCacheDirRefreshOldChildFileId(pDirRePop, pCur, idFile);
1256 
1257 #ifdef KFSCACHE_CFG_SHORT_NAMES
1258             if (cwcShortName == 0
1259                 ?    pCur->pwszShortName == pCur->pwszName
1260                   || (   pCur->cwcShortName == cwcName
1261                       && kHlpMemComp(pCur->pwszShortName, pCur->pwszName, cwcName * sizeof(wchar_t)) == 0)
1262                 :    pCur->cwcShortName == cwcShortName
1263                   && kHlpMemComp(pCur->pwszShortName, pwcShortName, cwcShortName * sizeof(wchar_t)) == 0 )
1264              { /* likely */ }
1265              else
1266                  pCur = kFsCacheDirRefreshOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1267 #endif
1268         }
1269         else
1270             pCur = NULL;
1271         if (pCur)
1272         {
1273             /*
1274              * Got a match.  Remove the child from the array, replacing it with
1275              * the last element.  (This means we're reversing the second half of
1276              * the elements, which is why we need cNextOldChildInc.)
1277              */
1278             pDirRePop->cOldChildren = --cOldChildren;
1279             if (iNextOldChild < cOldChildren)
1280                 pDirRePop->papOldChildren[iNextOldChild] = pDirRePop->papOldChildren[cOldChildren];
1281             pDirRePop->iNextOldChild = iNextOldChild + pDirRePop->cNextOldChildInc;
1282             return pCur;
1283         }
1284 
1285 #ifdef KFSCACHE_CFG_SHORT_NAMES
1286         return kFsCacheDirFindOldChildSlow(pDirRePop, idFile, pwcName, cwcName, pwcShortName, cwcShortName);
1287 #else
1288         return kFsCacheDirFindOldChildSlow(pDirRePop, idFile, pwcName, cwcName);
1289 #endif
1290     }
1291 
1292     return NULL;
1293 }
1294 
1295 
1296 
1297 /**
1298  * Does the initial directory populating or refreshes it if it has been
1299  * invalidated.
1300  *
1301  * This assumes the parent directory is opened.
1302  *
1303  * @returns K_TRUE on success, K_FALSE on error.
1304  * @param   pCache              The cache.
1305  * @param   pDir                The directory.
1306  * @param   penmError           Where to store K_FALSE explanation.
1307  */
kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache,PKFSDIR pDir,KFSLOOKUPERROR * penmError)1308 static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
1309 {
1310     KBOOL                       fRefreshing = K_FALSE;
1311     KFSDIRREPOP                 DirRePop    = { NULL, 0, 0, 0, NULL };
1312     MY_UNICODE_STRING           UniStrStar  = { 1 * sizeof(wchar_t), 2 * sizeof(wchar_t), L"*" };
1313 
1314     /** @todo May have to make this more flexible wrt information classes since
1315      *        older windows versions (XP, w2K) might not correctly support the
1316      *        ones with file ID on all file systems. */
1317 #ifdef KFSCACHE_CFG_SHORT_NAMES
1318     MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdBothDirectoryInformation;
1319     MY_FILE_INFORMATION_CLASS       enmInfoClass = MyFileIdBothDirectoryInformation;
1320 #else
1321     MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdFullDirectoryInformation;
1322     MY_FILE_INFORMATION_CLASS       enmInfoClass = MyFileIdFullDirectoryInformation;
1323 #endif
1324     MY_NTSTATUS                 rcNt;
1325     MY_IO_STATUS_BLOCK          Ios;
1326     union
1327     {
1328         /* Include the structures for better alignment. */
1329         MY_FILE_ID_BOTH_DIR_INFORMATION     WithId;
1330         MY_FILE_ID_FULL_DIR_INFORMATION     NoId;
1331         /** Buffer padding. We're using a 56KB buffer here to avoid size troubles
1332          * with CIFS and such that starts at 64KB. */
1333         KU8                                 abBuf[56*1024];
1334     } uBuf;
1335 
1336 
1337     /*
1338      * Open the directory.
1339      */
1340     if (pDir->hDir == INVALID_HANDLE_VALUE)
1341     {
1342         MY_OBJECT_ATTRIBUTES    ObjAttr;
1343         MY_UNICODE_STRING       UniStr;
1344 
1345         kHlpAssert(!pDir->fPopulated);
1346 
1347         Ios.Information = -1;
1348         Ios.u.Status    = -1;
1349 
1350         UniStr.Buffer        = (wchar_t *)pDir->Obj.pwszName;
1351         UniStr.Length        = (USHORT)(pDir->Obj.cwcName * sizeof(wchar_t));
1352         UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1353 
1354         kHlpAssertStmtReturn(pDir->Obj.pParent, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
1355         kHlpAssertStmtReturn(pDir->Obj.pParent->hDir != INVALID_HANDLE_VALUE, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
1356         MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pDir->Obj.pParent->hDir, NULL /*pSecAttr*/);
1357 
1358         /** @todo FILE_OPEN_REPARSE_POINT? */
1359         rcNt = g_pfnNtCreateFile(&pDir->hDir,
1360                                  FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1361                                  &ObjAttr,
1362                                  &Ios,
1363                                  NULL, /*cbFileInitialAlloc */
1364                                  FILE_ATTRIBUTE_NORMAL,
1365                                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1366                                  FILE_OPEN,
1367                                  FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1368                                  NULL, /*pEaBuffer*/
1369                                  0);   /*cbEaBuffer*/
1370         if (MY_NT_SUCCESS(rcNt))
1371         {  /* likely */ }
1372         else
1373         {
1374             pDir->hDir = INVALID_HANDLE_VALUE;
1375             *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR;
1376             return K_FALSE;
1377         }
1378     }
1379     /*
1380      * When re-populating, we replace papChildren in the directory and pick
1381      * from the old one as we go along.
1382      */
1383     else if (pDir->fPopulated)
1384     {
1385         KU32  cAllocated;
1386         void *pvNew;
1387 
1388         /* Make sure we really need to do this first. */
1389         if (!pDir->fNeedRePopulating)
1390         {
1391             if (   pDir->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1392                 || pDir->Obj.uCacheGen == pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
1393                 return K_TRUE;
1394             if (   kFsCacheRefreshObj(pCache, &pDir->Obj, penmError)
1395                 && !pDir->fNeedRePopulating)
1396                 return K_TRUE;
1397         }
1398 
1399         /* Yes we do need to. */
1400         cAllocated = K_ALIGN_Z(pDir->cChildren, 16);
1401         pvNew      = kHlpAlloc(sizeof(pDir->papChildren[0]) * cAllocated);
1402         if (pvNew)
1403         {
1404             DirRePop.papOldChildren     = pDir->papChildren;
1405             DirRePop.cOldChildren       = pDir->cChildren;
1406             DirRePop.iNextOldChild      = 0;
1407             DirRePop.cNextOldChildInc   = 1;
1408             DirRePop.pCache             = pCache;
1409 
1410             pDir->cChildren             = 0;
1411             pDir->cChildrenAllocated    = cAllocated;
1412             pDir->papChildren           = (PKFSOBJ *)pvNew;
1413         }
1414         else
1415         {
1416             *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
1417             return K_FALSE;
1418         }
1419 
1420         fRefreshing = K_TRUE;
1421     }
1422     if (!fRefreshing)
1423         KFSCACHE_LOG(("Populating %s...\n", pDir->Obj.pszName));
1424     else
1425         KFSCACHE_LOG(("Refreshing %s...\n", pDir->Obj.pszName));
1426 
1427     /*
1428      * Enumerate the directory content.
1429      *
1430      * Note! The "*" filter is necessary because kFsCacheRefreshObj may have
1431      *       previously quried a single file name and just passing NULL would
1432      *       restart that single file name query.
1433      */
1434     Ios.Information = -1;
1435     Ios.u.Status    = -1;
1436     rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1437                                      NULL,      /* hEvent */
1438                                      NULL,      /* pfnApcComplete */
1439                                      NULL,      /* pvApcCompleteCtx */
1440                                      &Ios,
1441                                      &uBuf,
1442                                      sizeof(uBuf),
1443                                      enmInfoClass,
1444                                      FALSE,     /* fReturnSingleEntry */
1445                                      &UniStrStar, /* Filter / restart pos. */
1446                                      TRUE);     /* fRestartScan */
1447     while (MY_NT_SUCCESS(rcNt))
1448     {
1449         /*
1450          * Process the entries in the buffer.
1451          */
1452         KSIZE offBuf = 0;
1453         for (;;)
1454         {
1455             union
1456             {
1457                 KU8                             *pb;
1458 #ifdef KFSCACHE_CFG_SHORT_NAMES
1459                 MY_FILE_ID_BOTH_DIR_INFORMATION *pWithId;
1460                 MY_FILE_BOTH_DIR_INFORMATION    *pNoId;
1461 #else
1462                 MY_FILE_ID_FULL_DIR_INFORMATION *pWithId;
1463                 MY_FILE_FULL_DIR_INFORMATION    *pNoId;
1464 #endif
1465             }           uPtr;
1466             PKFSOBJ     pCur;
1467             KU32        offNext;
1468             KU32        cbMinCur;
1469             wchar_t    *pwchFilename;
1470 
1471             /* ASSUME only the FileName member differs between the two structures. */
1472             uPtr.pb = &uBuf.abBuf[offBuf];
1473             if (enmInfoClass == enmInfoClassWithId)
1474             {
1475                 pwchFilename = &uPtr.pWithId->FileName[0];
1476                 cbMinCur  = (KU32)((uintptr_t)&uPtr.pWithId->FileName[0] - (uintptr_t)uPtr.pWithId);
1477                 cbMinCur += uPtr.pNoId->FileNameLength;
1478             }
1479             else
1480             {
1481                 pwchFilename = &uPtr.pNoId->FileName[0];
1482                 cbMinCur  = (KU32)((uintptr_t)&uPtr.pNoId->FileName[0] - (uintptr_t)uPtr.pNoId);
1483                 cbMinCur += uPtr.pNoId->FileNameLength;
1484             }
1485 
1486             /* We need to skip the '.' and '..' entries. */
1487             if (   *pwchFilename != '.'
1488                 ||  uPtr.pNoId->FileNameLength > 4
1489                 ||  !(   uPtr.pNoId->FileNameLength == 2
1490                       ||  (   uPtr.pNoId->FileNameLength == 4
1491                            && pwchFilename[1] == '.') )
1492                )
1493             {
1494                 KBOOL       fRc;
1495                 KU8 const   bObjType = uPtr.pNoId->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR
1496                                      : uPtr.pNoId->FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)
1497                                      ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE;
1498 
1499                 /*
1500                  * If refreshing, we must first see if this directory entry already
1501                  * exists.
1502                  */
1503                 if (!fRefreshing)
1504                     pCur = NULL;
1505                 else
1506                 {
1507                     pCur = kFsCacheDirFindOldChild(&DirRePop,
1508                                                    enmInfoClass == enmInfoClassWithId ? uPtr.pWithId->FileId.QuadPart : 0,
1509                                                    pwchFilename, uPtr.pWithId->FileNameLength / sizeof(wchar_t)
1510 #ifdef KFSCACHE_CFG_SHORT_NAMES
1511                                                    , uPtr.pWithId->ShortName, uPtr.pWithId->ShortNameLength / sizeof(wchar_t)
1512 #endif
1513                                                    );
1514                     if (pCur)
1515                     {
1516                         if (pCur->bObjType == bObjType)
1517                         {
1518                             if (pCur->bObjType == KFSOBJ_TYPE_DIR)
1519                             {
1520                                 PKFSDIR pCurDir = (PKFSDIR)pCur;
1521                                 if (   !pCurDir->fPopulated
1522                                     ||  (   pCurDir->iLastWrite == uPtr.pWithId->LastWriteTime.QuadPart
1523                                          && (pCur->fFlags & KFSOBJ_F_WORKING_DIR_MTIME) ) )
1524                                 { /* kind of likely */ }
1525                                 else
1526                                 {
1527                                     KFSCACHE_LOG(("Refreshing %s/%s/ - %s/ needs re-populating...\n",
1528                                                   pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pCur->pszName));
1529                                     pCurDir->fNeedRePopulating = K_TRUE;
1530                                 }
1531                             }
1532                         }
1533                         else if (pCur->bObjType == KFSOBJ_TYPE_MISSING)
1534                         {
1535                             KFSCACHE_LOG(("Refreshing %s/%s/ - %s appeared as %u, was missing.\n",
1536                                           pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pCur->pszName, bObjType));
1537                             pCur->bObjType = bObjType;
1538                         }
1539                         else
1540                         {
1541                             KFSCACHE_LOG(("Refreshing %s/%s/ - %s changed type from %u to %u! Dropping old object.\n",
1542                                           pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pCur->pszName,
1543                                           pCur->bObjType, bObjType));
1544                             kFsCacheObjRelease(pCache, pCur);
1545                             pCur = NULL;
1546                         }
1547                     }
1548                     else
1549                         KFSCACHE_LOG(("Refreshing %s/%s/ - %*.*ls added.\n", pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName,
1550                                       uPtr.pNoId->FileNameLength / sizeof(wchar_t), uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1551                                       pwchFilename));
1552                 }
1553 
1554                 if (!pCur)
1555                 {
1556                     /*
1557                      * Create the entry (not linked yet).
1558                      */
1559                     pCur = kFsCacheCreateObjectW(pCache, pDir, pwchFilename, uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1560 #ifdef KFSCACHE_CFG_SHORT_NAMES
1561                                                  uPtr.pNoId->ShortName, uPtr.pNoId->ShortNameLength / sizeof(wchar_t),
1562 #endif
1563                                                  bObjType, penmError);
1564                     if (!pCur)
1565                         return K_FALSE;
1566                     kHlpAssert(pCur->cRefs == 1);
1567                 }
1568 
1569 #ifdef KFSCACHE_CFG_SHORT_NAMES
1570                 if (enmInfoClass == enmInfoClassWithId)
1571                     birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId);
1572                 else
1573                     birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId);
1574 #else
1575                 if (enmInfoClass == enmInfoClassWithId)
1576                     birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId);
1577                 else
1578                     birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId);
1579 #endif
1580                 pCur->Stats.st_dev = pDir->uDevNo;
1581                 pCur->fHaveStats   = K_TRUE;
1582 
1583                 /*
1584                  * Add the entry to the directory.
1585                  */
1586                 fRc = kFsCacheDirAddChild(pCache, pDir, pCur, penmError);
1587                 kFsCacheObjRelease(pCache, pCur);
1588                 if (fRc)
1589                 { /* likely */ }
1590                 else
1591                 {
1592                     rcNt = STATUS_NO_MEMORY;
1593                     break;
1594                 }
1595             }
1596             /*
1597              * When seeing '.' we update the directory info.
1598              */
1599             else if (uPtr.pNoId->FileNameLength == 2)
1600             {
1601                 pDir->iLastWrite = uPtr.pNoId->LastWriteTime.QuadPart;
1602 #ifdef KFSCACHE_CFG_SHORT_NAMES
1603                 if (enmInfoClass == enmInfoClassWithId)
1604                     birdStatFillFromFileIdBothDirInfo(&pDir->Obj.Stats, uPtr.pWithId);
1605                 else
1606                     birdStatFillFromFileBothDirInfo(&pDir->Obj.Stats, uPtr.pNoId);
1607 #else
1608                 if (enmInfoClass == enmInfoClassWithId)
1609                     birdStatFillFromFileIdFullDirInfo(&pDir->Obj.Stats, uPtr.pWithId);
1610                 else
1611                     birdStatFillFromFileFullDirInfo(&pDir->Obj.Stats, uPtr.pNoId);
1612 #endif
1613             }
1614 
1615             /*
1616              * Advance.
1617              */
1618             offNext = uPtr.pNoId->NextEntryOffset;
1619             if (   offNext >= cbMinCur
1620                 && offNext < sizeof(uBuf))
1621                 offBuf += offNext;
1622             else
1623                 break;
1624         }
1625 
1626         /*
1627          * Read the next chunk.
1628          */
1629         rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1630                                          NULL,      /* hEvent */
1631                                          NULL,      /* pfnApcComplete */
1632                                          NULL,      /* pvApcCompleteCtx */
1633                                          &Ios,
1634                                          &uBuf,
1635                                          sizeof(uBuf),
1636                                          enmInfoClass,
1637                                          FALSE,     /* fReturnSingleEntry */
1638                                          &UniStrStar, /* Filter / restart pos. */
1639                                          FALSE);    /* fRestartScan */
1640     }
1641 
1642     if (rcNt == MY_STATUS_NO_MORE_FILES)
1643     {
1644         /*
1645          * If refreshing, add missing children objects and ditch the rest.
1646          * We ignore errors while adding missing children (lazy bird).
1647          */
1648         if (!fRefreshing)
1649         { /* more likely */ }
1650         else
1651         {
1652             while (DirRePop.cOldChildren > 0)
1653             {
1654                 KFSLOOKUPERROR enmErrorIgn;
1655                 PKFSOBJ pOldChild = DirRePop.papOldChildren[--DirRePop.cOldChildren];
1656                 if (pOldChild->bObjType == KFSOBJ_TYPE_MISSING)
1657                     kFsCacheDirAddChild(pCache, pDir, pOldChild, &enmErrorIgn);
1658                 else
1659                 {
1660                     KFSCACHE_LOG(("Refreshing %s/%s/ - %s was removed.\n",
1661                                   pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pOldChild->pszName));
1662                     kHlpAssert(pOldChild->bObjType != KFSOBJ_TYPE_DIR);
1663                     /* Remove from hash table. */
1664                     if (pOldChild->uNameHash != 0)
1665                     {
1666                         KU32    idx = pOldChild->uNameHash & pDir->fHashTabMask;
1667                         PKFSOBJ pPrev = pDir->papHashTab[idx];
1668                         if (pPrev == pOldChild)
1669                             pDir->papHashTab[idx] = pOldChild->pNextNameHash;
1670                         else
1671                         {
1672                             while (pPrev && pPrev->pNextNameHash != pOldChild)
1673                                 pPrev = pPrev->pNextNameHash;
1674                             kHlpAssert(pPrev);
1675                             if (pPrev)
1676                                 pPrev->pNextNameHash = pOldChild->pNextNameHash;
1677                         }
1678                         pOldChild->uNameHash = 0;
1679                     }
1680                 }
1681                 kFsCacheObjRelease(pCache, pOldChild);
1682             }
1683             kHlpFree(DirRePop.papOldChildren);
1684         }
1685 
1686         /*
1687          * Mark the directory as fully populated and up to date.
1688          */
1689         pDir->fPopulated        = K_TRUE;
1690         pDir->fNeedRePopulating = K_FALSE;
1691         if (pDir->Obj.uCacheGen != KFSOBJ_CACHE_GEN_IGNORE)
1692             pDir->Obj.uCacheGen = pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1693         return K_TRUE;
1694     }
1695 
1696     /*
1697      * If we failed during refresh, add back remaining old children.
1698      */
1699     if (!fRefreshing)
1700     {
1701         while (DirRePop.cOldChildren > 0)
1702         {
1703             KFSLOOKUPERROR enmErrorIgn;
1704             PKFSOBJ pOldChild = DirRePop.papOldChildren[--DirRePop.cOldChildren];
1705             kFsCacheDirAddChild(pCache, pDir, pOldChild, &enmErrorIgn);
1706             kFsCacheObjRelease(pCache, pOldChild);
1707         }
1708         kHlpFree(DirRePop.papOldChildren);
1709     }
1710 
1711     kHlpAssertMsgFailed(("%#x\n", rcNt));
1712     *penmError = KFSLOOKUPERROR_DIR_READ_ERROR;
1713     return K_TRUE;
1714 }
1715 
1716 
1717 /**
1718  * Does the initial directory populating or refreshes it if it has been
1719  * invalidated.
1720  *
1721  * This assumes the parent directory is opened.
1722  *
1723  * @returns K_TRUE on success, K_FALSE on error.
1724  * @param   pCache              The cache.
1725  * @param   pDir                The directory.
1726  * @param   penmError           Where to store K_FALSE explanation.  Optional.
1727  */
kFsCacheDirEnsurePopuplated(PKFSCACHE pCache,PKFSDIR pDir,KFSLOOKUPERROR * penmError)1728 KBOOL kFsCacheDirEnsurePopuplated(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
1729 {
1730     KFSLOOKUPERROR enmIgnored;
1731     if (   pDir->fPopulated
1732         && !pDir->fNeedRePopulating
1733         && (   pDir->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1734             || pDir->Obj.uCacheGen == pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
1735         return K_TRUE;
1736     return kFsCachePopuplateOrRefreshDir(pCache, pDir, penmError ? penmError : &enmIgnored);
1737 }
1738 
1739 
1740 /**
1741  * Checks whether the modified timestamp differs on this directory.
1742  *
1743  * @returns K_TRUE if possibly modified, K_FALSE if definitely not modified.
1744  * @param   pDir                The directory..
1745  */
kFsCacheDirIsModified(PKFSDIR pDir)1746 static KBOOL kFsCacheDirIsModified(PKFSDIR pDir)
1747 {
1748     if (   pDir->hDir != INVALID_HANDLE_VALUE
1749         && (pDir->Obj.fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
1750     {
1751         if (!pDir->fNeedRePopulating)
1752         {
1753             MY_IO_STATUS_BLOCK          Ios;
1754             MY_FILE_BASIC_INFORMATION   BasicInfo;
1755             MY_NTSTATUS                 rcNt;
1756 
1757             Ios.Information = -1;
1758             Ios.u.Status    = -1;
1759 
1760             rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
1761             if (MY_NT_SUCCESS(rcNt))
1762             {
1763                 if (BasicInfo.LastWriteTime.QuadPart != pDir->iLastWrite)
1764                 {
1765                     pDir->fNeedRePopulating = K_TRUE;
1766                     return K_TRUE;
1767                 }
1768                 return K_FALSE;
1769             }
1770         }
1771     }
1772     /* The cache root never changes. */
1773     else if (!pDir->Obj.pParent)
1774         return K_FALSE;
1775 
1776     return K_TRUE;
1777 }
1778 
1779 
kFsCacheRefreshMissing(PKFSCACHE pCache,PKFSOBJ pMissing,KFSLOOKUPERROR * penmError)1780 static KBOOL kFsCacheRefreshMissing(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1781 {
1782     /*
1783      * If we can, we start by checking whether the parent directory
1784      * has been modified.   If it has, we need to check if this entry
1785      * was added or not, most likely it wasn't added.
1786      */
1787     if (!kFsCacheDirIsModified(pMissing->pParent))
1788     {
1789         KFSCACHE_LOG(("Parent of missing not written to %s/%s\n", pMissing->pParent->Obj.pszName, pMissing->pszName));
1790         pMissing->uCacheGen = pCache->auGenerationsMissing[pMissing->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1791     }
1792     else
1793     {
1794         MY_UNICODE_STRING           UniStr;
1795         MY_OBJECT_ATTRIBUTES        ObjAttr;
1796         MY_FILE_BASIC_INFORMATION   BasicInfo;
1797         MY_NTSTATUS                 rcNt;
1798 
1799         UniStr.Buffer        = (wchar_t *)pMissing->pwszName;
1800         UniStr.Length        = (USHORT)(pMissing->cwcName * sizeof(wchar_t));
1801         UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1802 
1803         kHlpAssert(pMissing->pParent->hDir != INVALID_HANDLE_VALUE);
1804         MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pMissing->pParent->hDir, NULL /*pSecAttr*/);
1805 
1806         rcNt = g_pfnNtQueryAttributesFile(&ObjAttr, &BasicInfo);
1807         if (!MY_NT_SUCCESS(rcNt))
1808         {
1809             /*
1810              * Probably more likely that a missing node stays missing.
1811              */
1812             pMissing->uCacheGen = pCache->auGenerationsMissing[pMissing->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1813             KFSCACHE_LOG(("Still missing %s/%s\n", pMissing->pParent->Obj.pszName, pMissing->pszName));
1814         }
1815         else
1816         {
1817             /*
1818              * We must metamorphose this node.  This is tedious business
1819              * because we need to check the file name casing.  We might
1820              * just as well update the parent directory...
1821              */
1822             KU8 const   bObjType = BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR
1823                                  : BasicInfo.FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)
1824                                  ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE;
1825 
1826             KFSCACHE_LOG(("Birth of %s/%s as %d with attribs %#x...\n",
1827                           pMissing->pParent->Obj.pszName, pMissing->pszName, bObjType, BasicInfo.FileAttributes));
1828             pMissing->bObjType  = bObjType;
1829             pMissing->uCacheGen = pCache->auGenerations[pMissing->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1830 /**
1831  * @todo refresh missing object names when it appears.
1832  */
1833         }
1834     }
1835 
1836     return K_TRUE;
1837 }
1838 
1839 
kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache,PKFSOBJ pMissing,KFSLOOKUPERROR * penmError)1840 static KBOOL kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1841 {
1842     if (kFsCacheRefreshMissing(pCache, pMissing, penmError))
1843     {
1844         if (   pMissing->bObjType == KFSOBJ_TYPE_DIR
1845             || pMissing->bObjType == KFSOBJ_TYPE_MISSING)
1846             return K_TRUE;
1847         *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1848     }
1849 
1850     return K_FALSE;
1851 }
1852 
1853 
1854 /**
1855  * Generic object refresh.
1856  *
1857  * This does not refresh the content of directories.
1858  *
1859  * @returns K_TRUE on success.  K_FALSE and *penmError on failure.
1860  * @param   pCache              The cache.
1861  * @param   pObj                The object.
1862  * @param   penmError           Where to return error info.
1863  */
kFsCacheRefreshObj(PKFSCACHE pCache,PKFSOBJ pObj,KFSLOOKUPERROR * penmError)1864 static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError)
1865 {
1866     KBOOL fRc;
1867 
1868     /*
1869      * Since we generally assume nothing goes away in this cache, we only really
1870      * have a hard time with negative entries.  So, missing stuff goes to
1871      * complicated land.
1872      */
1873     if (pObj->bObjType == KFSOBJ_TYPE_MISSING)
1874         fRc = kFsCacheRefreshMissing(pCache, pObj, penmError);
1875     else
1876     {
1877         /*
1878          * This object is supposed to exist, so all we need to do is query essential
1879          * stats again.  Since we've already got handles on directories, there are
1880          * two ways to go about this.
1881          */
1882         union
1883         {
1884             MY_FILE_NETWORK_OPEN_INFORMATION    FullInfo;
1885             MY_FILE_STANDARD_INFORMATION        StdInfo;
1886 #ifdef KFSCACHE_CFG_SHORT_NAMES
1887             MY_FILE_ID_BOTH_DIR_INFORMATION     WithId;
1888             //MY_FILE_BOTH_DIR_INFORMATION        NoId;
1889 #else
1890             MY_FILE_ID_FULL_DIR_INFORMATION     WithId;
1891             //MY_FILE_FULL_DIR_INFORMATION        NoId;
1892 #endif
1893             KU8                                 abPadding[  sizeof(wchar_t) * KFSCACHE_CFG_MAX_UTF16_NAME
1894                                                           + sizeof(MY_FILE_ID_BOTH_DIR_INFORMATION)];
1895         } uBuf;
1896         MY_IO_STATUS_BLOCK                      Ios;
1897         MY_NTSTATUS                             rcNt;
1898         if (   pObj->bObjType != KFSOBJ_TYPE_DIR
1899             || ((PKFSDIR)pObj)->hDir == INVALID_HANDLE_VALUE)
1900         {
1901 #if 1
1902             /* This always works and doesn't mess up NtQueryDirectoryFile. */
1903             MY_UNICODE_STRING    UniStr;
1904             MY_OBJECT_ATTRIBUTES ObjAttr;
1905 
1906             UniStr.Buffer        = (wchar_t *)pObj->pwszName;
1907             UniStr.Length        = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1908             UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1909 
1910             kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1911             MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pObj->pParent->hDir, NULL /*pSecAttr*/);
1912 
1913             rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.FullInfo);
1914             if (MY_NT_SUCCESS(rcNt))
1915             {
1916                 pObj->Stats.st_size          = uBuf.FullInfo.EndOfFile.QuadPart;
1917                 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart,   &pObj->Stats.st_birthtim);
1918                 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart,     &pObj->Stats.st_ctim);
1919                 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart,  &pObj->Stats.st_mtim);
1920                 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1921                 pObj->Stats.st_attribs       = uBuf.FullInfo.FileAttributes;
1922                 pObj->Stats.st_blksize       = 65536;
1923                 pObj->Stats.st_blocks        = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1924                                              / BIRD_STAT_BLOCK_SIZE;
1925             }
1926 #else
1927             /* This alternative lets us keep the inode number up to date and
1928                detect name case changes.
1929                Update: This doesn't work on windows 7, it ignores the UniStr
1930                        and continue with the "*" search. So, we're using the
1931                        above query instead for the time being. */
1932             MY_UNICODE_STRING    UniStr;
1933 # ifdef KFSCACHE_CFG_SHORT_NAMES
1934             MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
1935 # else
1936             MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
1937 # endif
1938 
1939             UniStr.Buffer        = (wchar_t *)pObj->pwszName;
1940             UniStr.Length        = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1941             UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1942 
1943             kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1944 
1945             Ios.Information = -1;
1946             Ios.u.Status    = -1;
1947             rcNt = g_pfnNtQueryDirectoryFile(pObj->pParent->hDir,
1948                                              NULL,      /* hEvent */
1949                                              NULL,      /* pfnApcComplete */
1950                                              NULL,      /* pvApcCompleteCtx */
1951                                              &Ios,
1952                                              &uBuf,
1953                                              sizeof(uBuf),
1954                                              enmInfoClass,
1955                                              TRUE,      /* fReturnSingleEntry */
1956                                              &UniStr,   /* Filter / restart pos. */
1957                                              TRUE);     /* fRestartScan */
1958 
1959             if (MY_NT_SUCCESS(rcNt))
1960             {
1961                 if (pObj->Stats.st_ino == uBuf.WithId.FileId.QuadPart)
1962                     KFSCACHE_LOG(("Refreshing %s/%s, no ID change...\n", pObj->pParent->Obj.pszName, pObj->pszName));
1963                 else if (   pObj->cwcName == uBuf.WithId.FileNameLength / sizeof(wchar_t)
1964 # ifdef KFSCACHE_CFG_SHORT_NAMES
1965                          && (  uBuf.WithId.ShortNameLength == 0
1966                              ?    pObj->pwszName == pObj->pwszShortName
1967                                || (   pObj->cwcName == pObj->cwcShortName
1968                                    && memcmp(pObj->pwszName, pObj->pwszShortName, pObj->cwcName * sizeof(wchar_t)) == 0)
1969                              : pObj->cwcShortName == uBuf.WithId.ShortNameLength / sizeof(wchar_t)
1970                                && memcmp(pObj->pwszShortName, uBuf.WithId.ShortName, uBuf.WithId.ShortNameLength) == 0
1971                             )
1972 # endif
1973                          && memcmp(pObj->pwszName, uBuf.WithId.FileName, uBuf.WithId.FileNameLength) == 0
1974                          )
1975                 {
1976                     KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx...\n",
1977                                   pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1978                     pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1979                 }
1980                 else
1981                 {
1982                     KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx and names too...\n",
1983                                   pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1984                     fprintf(stderr, "kFsCacheRefreshObj - ID + name change not implemented!!\n");
1985                     fflush(stderr);
1986                     __debugbreak();
1987                     pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1988                     /** @todo implement as needed.   */
1989                 }
1990 
1991                 pObj->Stats.st_size          = uBuf.WithId.EndOfFile.QuadPart;
1992                 birdNtTimeToTimeSpec(uBuf.WithId.CreationTime.QuadPart,   &pObj->Stats.st_birthtim);
1993                 birdNtTimeToTimeSpec(uBuf.WithId.ChangeTime.QuadPart,     &pObj->Stats.st_ctim);
1994                 birdNtTimeToTimeSpec(uBuf.WithId.LastWriteTime.QuadPart,  &pObj->Stats.st_mtim);
1995                 birdNtTimeToTimeSpec(uBuf.WithId.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1996                 pObj->Stats.st_attribs       = uBuf.WithId.FileAttributes;
1997                 pObj->Stats.st_blksize       = 65536;
1998                 pObj->Stats.st_blocks        = (uBuf.WithId.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1999                                              / BIRD_STAT_BLOCK_SIZE;
2000             }
2001 #endif
2002             if (MY_NT_SUCCESS(rcNt))
2003             {
2004                 pObj->uCacheGen = pCache->auGenerations[pObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
2005                 fRc = K_TRUE;
2006             }
2007             else
2008             {
2009                 /* ouch! */
2010                 kHlpAssertMsgFailed(("%#x\n", rcNt));
2011                 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on non-dir - not implemented!\n", rcNt);
2012                 __debugbreak();
2013                 fRc = K_FALSE;
2014             }
2015         }
2016         else
2017         {
2018             /*
2019              * An open directory.  Query information via the handle, the
2020              * file ID shouldn't have been able to change, so we can use
2021              * NtQueryInformationFile.  Right...
2022              */
2023             PKFSDIR pDir = (PKFSDIR)pObj;
2024             Ios.Information = -1;
2025             Ios.u.Status    = -1;
2026             rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &uBuf.FullInfo, sizeof(uBuf.FullInfo),
2027                                                MyFileNetworkOpenInformation);
2028             if (MY_NT_SUCCESS(rcNt))
2029                 rcNt = Ios.u.Status;
2030             if (MY_NT_SUCCESS(rcNt))
2031             {
2032                 pObj->Stats.st_size          = uBuf.FullInfo.EndOfFile.QuadPart;
2033                 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart,   &pObj->Stats.st_birthtim);
2034                 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart,     &pObj->Stats.st_ctim);
2035                 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart,  &pObj->Stats.st_mtim);
2036                 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
2037                 pObj->Stats.st_attribs       = uBuf.FullInfo.FileAttributes;
2038                 pObj->Stats.st_blksize       = 65536;
2039                 pObj->Stats.st_blocks        = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
2040                                              / BIRD_STAT_BLOCK_SIZE;
2041 
2042                 if (   pDir->iLastWrite == uBuf.FullInfo.LastWriteTime.QuadPart
2043                     && (pObj->fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
2044                     KFSCACHE_LOG(("Refreshing %s/%s/ - no re-populating necessary.\n",
2045                                   pObj->pParent->Obj.pszName, pObj->pszName));
2046                 else
2047                 {
2048                     KFSCACHE_LOG(("Refreshing %s/%s/ - needs re-populating...\n",
2049                                   pObj->pParent->Obj.pszName, pObj->pszName));
2050                     pDir->fNeedRePopulating = K_TRUE;
2051 #if 0
2052                     /* Refresh the link count. */
2053                     rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
2054                     if (MY_NT_SUCCESS(rcNt))
2055                         rcNt = Ios.s.Status;
2056                     if (MY_NT_SUCCESS(rcNt))
2057                         pObj->Stats.st_nlink = StdInfo.NumberOfLinks;
2058 #endif
2059                 }
2060             }
2061             if (MY_NT_SUCCESS(rcNt))
2062             {
2063                 pObj->uCacheGen = pCache->auGenerations[pObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
2064                 fRc = K_TRUE;
2065             }
2066             else
2067             {
2068                 /* ouch! */
2069                 kHlpAssertMsgFailed(("%#x\n", rcNt));
2070                 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on dir - not implemented!\n", rcNt);
2071                 fflush(stderr);
2072                 __debugbreak();
2073                 fRc = K_FALSE;
2074             }
2075         }
2076     }
2077 
2078     return fRc;
2079 }
2080 
2081 
2082 
2083 /**
2084  * Looks up a drive letter.
2085  *
2086  * Will enter the drive if necessary.
2087  *
2088  * @returns Pointer to the root directory of the drive or an update-to-date
2089  *          missing node.
2090  * @param   pCache              The cache.
2091  * @param   chLetter            The uppercased drive letter.
2092  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2093  * @param   penmError           Where to return details as to why the lookup
2094  *                              failed.
2095  */
kFsCacheLookupDrive(PKFSCACHE pCache,char chLetter,KU32 fFlags,KFSLOOKUPERROR * penmError)2096 static PKFSOBJ kFsCacheLookupDrive(PKFSCACHE pCache, char chLetter, KU32 fFlags, KFSLOOKUPERROR *penmError)
2097 {
2098     KU32 const          uNameHash = chLetter - 'A';
2099     PKFSOBJ             pCur      = pCache->RootDir.papHashTab[uNameHash];
2100 
2101     KU32                cLeft;
2102     PKFSOBJ            *ppCur;
2103     MY_UNICODE_STRING   NtPath;
2104     wchar_t             wszTmp[8];
2105     char                szTmp[4];
2106 
2107     /*
2108      * Custom drive letter hashing.
2109      */
2110     kHlpAssert((uNameHash & pCache->RootDir.fHashTabMask) == uNameHash);
2111     while (pCur)
2112     {
2113         if (   pCur->uNameHash == uNameHash
2114             && pCur->cchName == 2
2115             && pCur->pszName[0] == chLetter
2116             && pCur->pszName[1] == ':')
2117         {
2118             if (pCur->bObjType == KFSOBJ_TYPE_DIR)
2119                 return pCur;
2120             if (   (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2121                 || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
2122                 return pCur;
2123             return NULL;
2124         }
2125         pCur = pCur->pNextNameHash;
2126     }
2127 
2128     /*
2129      * Make 100% sure it's not there.
2130      */
2131     cLeft = pCache->RootDir.cChildren;
2132     ppCur = pCache->RootDir.papChildren;
2133     while (cLeft-- > 0)
2134     {
2135         pCur = *ppCur++;
2136         if (   pCur->cchName == 2
2137             && pCur->pszName[0] == chLetter
2138             && pCur->pszName[1] == ':')
2139         {
2140             if (pCur->bObjType == KFSOBJ_TYPE_DIR)
2141                 return pCur;
2142             kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
2143             if (   (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2144                 || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
2145                 return pCur;
2146             return NULL;
2147         }
2148     }
2149 
2150     if (fFlags & KFSCACHE_LOOKUP_F_NO_INSERT)
2151     {
2152         *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; /* close enough */
2153         return NULL;
2154     }
2155 
2156     /*
2157      * Need to add it.  We always keep the drive letters open for the benefit
2158      * of kFsCachePopuplateOrRefreshDir and others.
2159      */
2160     wszTmp[0] = szTmp[0] = chLetter;
2161     wszTmp[1] = szTmp[1] = ':';
2162     wszTmp[2] = szTmp[2] = '\\';
2163     wszTmp[3] = '.';
2164     wszTmp[4] = '\0';
2165     szTmp[2] = '\0';
2166 
2167     NtPath.Buffer        = NULL;
2168     NtPath.Length        = 0;
2169     NtPath.MaximumLength = 0;
2170     if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
2171     {
2172         HANDLE      hDir;
2173         MY_NTSTATUS rcNt;
2174         rcNt = birdOpenFileUniStr(NULL /*hRoot*/,
2175                                   &NtPath,
2176                                   FILE_READ_DATA  | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
2177                                   FILE_ATTRIBUTE_NORMAL,
2178                                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2179                                   FILE_OPEN,
2180                                   FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
2181                                   OBJ_CASE_INSENSITIVE,
2182                                   &hDir);
2183         birdFreeNtPath(&NtPath);
2184         if (MY_NT_SUCCESS(rcNt))
2185         {
2186             PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
2187 #ifdef KFSCACHE_CFG_SHORT_NAMES
2188                                                          NULL, 0, NULL, 0,
2189 #endif
2190                                                          KFSOBJ_TYPE_DIR, penmError);
2191             if (pDir)
2192             {
2193                 /*
2194                  * We need a little bit of extra info for a drive root.  These things are typically
2195                  * inherited by subdirectories down the tree, so, we do it all here for till that changes.
2196                  */
2197                 union
2198                 {
2199                     MY_FILE_FS_VOLUME_INFORMATION       VolInfo;
2200                     MY_FILE_FS_ATTRIBUTE_INFORMATION    FsAttrInfo;
2201                     char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
2202                 } uBuf;
2203                 MY_IO_STATUS_BLOCK Ios;
2204                 KBOOL fRc;
2205 
2206                 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
2207                 pDir->hDir = hDir;
2208 
2209                 if (birdStatHandle(hDir, &pDir->Obj.Stats, pDir->Obj.pszName) == 0)
2210                 {
2211                     pDir->Obj.fHaveStats = K_TRUE;
2212                     pDir->uDevNo = pDir->Obj.Stats.st_dev;
2213                 }
2214                 else
2215                 {
2216                     /* Just in case. */
2217                     pDir->Obj.fHaveStats = K_FALSE;
2218                     rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
2219                     kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
2220                 }
2221 
2222                 /* Get the file system. */
2223                 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
2224                 Ios.Information = -1;
2225                 Ios.u.Status    = -1;
2226                 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
2227                                                          MyFileFsAttributeInformation);
2228                 if (MY_NT_SUCCESS(rcNt))
2229                     rcNt = Ios.u.Status;
2230                 if (MY_NT_SUCCESS(rcNt))
2231                 {
2232                     if (   uBuf.FsAttrInfo.FileSystemName[0] == 'N'
2233                         && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
2234                         && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
2235                         && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
2236                         && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
2237                     {
2238                         DWORD dwDriveType = GetDriveTypeW(wszTmp);
2239                         if (   dwDriveType == DRIVE_FIXED
2240                             || dwDriveType == DRIVE_RAMDISK)
2241                             pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
2242                     }
2243                 }
2244 
2245                 /*
2246                  * Link the new drive letter into the root dir.
2247                  */
2248                 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
2249                 kFsCacheObjRelease(pCache, &pDir->Obj);
2250                 if (fRc)
2251                 {
2252                     pDir->Obj.pNextNameHash = pCache->RootDir.papHashTab[uNameHash];
2253                     pCache->RootDir.papHashTab[uNameHash] = &pDir->Obj;
2254                     return &pDir->Obj;
2255                 }
2256                 return NULL;
2257             }
2258 
2259             g_pfnNtClose(hDir);
2260             return NULL;
2261         }
2262 
2263         /* Assume it doesn't exist if this happens... This may be a little to
2264            restrictive wrt status code checks. */
2265         kHlpAssertMsgStmtReturn(   rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
2266                                 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
2267                                 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
2268                                 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
2269                                 ("%#x\n", rcNt),
2270                                 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
2271                                 NULL);
2272     }
2273     else
2274     {
2275         kHlpAssertFailed();
2276         *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
2277         return NULL;
2278     }
2279 
2280     /*
2281      * Maybe create a missing entry.
2282      */
2283     if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2284     {
2285         PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
2286 #ifdef KFSCACHE_CFG_SHORT_NAMES
2287                                         NULL, 0, NULL, 0,
2288 #endif
2289                                         KFSOBJ_TYPE_MISSING, penmError);
2290         if (pMissing)
2291         {
2292             KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
2293             kFsCacheObjRelease(pCache, pMissing);
2294             return fRc ? pMissing : NULL;
2295         }
2296     }
2297     else
2298     {
2299         /** @todo this isn't necessary correct for a root spec.   */
2300         *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2301     }
2302     return NULL;
2303 }
2304 
2305 
2306 /**
2307  * Slow path that allocates the child hash table and enters the given one.
2308  *
2309  * Allocation fialures are ignored.
2310  *
2311  * @param   pCache              The cache (for stats).
2312  * @param   pDir                The directory.
2313  * @param   uNameHash           The name hash  to enter @a pChild under.
2314  * @param   pChild              The child to enter into the hash table.
2315  */
kFsCacheDirAllocHashTabAndEnterChild(PKFSCACHE pCache,PKFSDIR pDir,KU32 uNameHash,PKFSOBJ pChild)2316 static void kFsCacheDirAllocHashTabAndEnterChild(PKFSCACHE pCache, PKFSDIR pDir, KU32 uNameHash, PKFSOBJ pChild)
2317 {
2318     if (uNameHash != 0) /* paranoia ^ 4! */
2319     {
2320         /*
2321          * Double the current number of children and round up to a multiple of
2322          * two so we can avoid division.
2323          */
2324         KU32 cbHashTab;
2325         KU32 cEntries;
2326         kHlpAssert(pDir->cChildren > 0);
2327         if (pDir->cChildren <= KU32_MAX / 4)
2328         {
2329 #if defined(_MSC_VER) && 1
2330             KU32 cEntriesRaw = pDir->cChildren * 2;
2331             KU32 cEntriesShift;
2332             kHlpAssert(sizeof(cEntries) == (unsigned long));
2333             if (_BitScanReverse(&cEntriesShift, cEntriesRaw))
2334             {
2335                 if (   K_BIT32(cEntriesShift) < cEntriesRaw
2336                     && cEntriesShift < 31U)
2337                     cEntriesShift++;
2338                 cEntries = K_BIT32(cEntriesShift);
2339             }
2340             else
2341             {
2342                 kHlpAssertFailed();
2343                 cEntries = KU32_MAX / 2 + 1;
2344             }
2345 #else
2346             cEntries = pDir->cChildren * 2 - 1;
2347             cEntries |= cEntries >> 1;
2348             cEntries |= cEntries >> 2;
2349             cEntries |= cEntries >> 4;
2350             cEntries |= cEntries >> 8;
2351             cEntries |= cEntries >> 16;
2352             cEntries++;
2353 #endif
2354         }
2355         else
2356             cEntries = KU32_MAX / 2 + 1;
2357         kHlpAssert((cEntries & (cEntries -  1)) == 0);
2358 
2359         cbHashTab = cEntries * sizeof(pDir->papHashTab[0]);
2360         pDir->papHashTab = (PKFSOBJ *)kHlpAllocZ(cbHashTab);
2361         if (pDir->papHashTab)
2362         {
2363             KU32 idx;
2364             pDir->fHashTabMask = cEntries - 1;
2365             pCache->cbObjects += cbHashTab;
2366             pCache->cChildHashTabs++;
2367             pCache->cChildHashEntriesTotal += cEntries;
2368 
2369             /*
2370              * Insert it.
2371              */
2372             pChild->uNameHash     = uNameHash;
2373             idx = uNameHash & (pDir->fHashTabMask);
2374             pChild->pNextNameHash = pDir->papHashTab[idx];
2375             pDir->papHashTab[idx] = pChild;
2376             pCache->cChildHashed++;
2377         }
2378     }
2379 }
2380 
2381 
2382 /**
2383  * Look up a child node, ANSI version.
2384  *
2385  * @returns Pointer to the child if found, NULL if not.
2386  * @param   pCache              The cache.
2387  * @param   pParent             The parent directory to search.
2388  * @param   pchName             The child name to search for (not terminated).
2389  * @param   cchName             The length of the child name.
2390  */
kFsCacheFindChildA(PKFSCACHE pCache,PKFSDIR pParent,const char * pchName,KU32 cchName)2391 static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
2392 {
2393     /*
2394      * Check for '.' first ('..' won't appear).
2395      */
2396     if (cchName != 1 || *pchName != '.')
2397     {
2398         PKFSOBJ    *ppCur;
2399         KU32        cLeft;
2400         KU32        uNameHash;
2401 
2402         /*
2403          * Do hash table lookup.
2404          *
2405          * This caches previous lookups, which should be useful when looking up
2406          * intermediate directories at least.
2407          */
2408         if (pParent->papHashTab != NULL)
2409         {
2410             PKFSOBJ pCur;
2411             uNameHash = kFsCacheStrHashN(pchName, cchName);
2412             pCur = pParent->papHashTab[uNameHash & pParent->fHashTabMask];
2413             while (pCur)
2414             {
2415                 if (   pCur->uNameHash == uNameHash
2416                     && (   (   pCur->cchName == cchName
2417                             && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2418 #ifdef KFSCACHE_CFG_SHORT_NAMES
2419                         || (   pCur->cchShortName == cchName
2420                             && pCur->pszShortName != pCur->pszName
2421                             && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2422 #endif
2423                         )
2424                    )
2425                 {
2426                     pCache->cChildHashHits++;
2427                     pCache->cChildSearches++;
2428                     return pCur;
2429                 }
2430                 pCur = pCur->pNextNameHash;
2431             }
2432         }
2433         else
2434             uNameHash = 0;
2435 
2436         /*
2437          * Do linear search.
2438          */
2439         cLeft = pParent->cChildren;
2440         ppCur = pParent->papChildren;
2441         while (cLeft-- > 0)
2442         {
2443             PKFSOBJ pCur = *ppCur++;
2444             if (   (   pCur->cchName == cchName
2445                     && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2446 #ifdef KFSCACHE_CFG_SHORT_NAMES
2447                 || (   pCur->cchShortName == cchName
2448                     && pCur->pszShortName != pCur->pszName
2449                     && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2450 #endif
2451                )
2452             {
2453                 /*
2454                  * Consider entering it into the parent hash table.
2455                  * Note! We hash the input, not the name we found.
2456                  */
2457                 if (   pCur->uNameHash == 0
2458                     && pParent->cChildren >= 2)
2459                 {
2460                     if (pParent->papHashTab)
2461                     {
2462                         if (uNameHash != 0)
2463                         {
2464                             KU32 idxNameHash = uNameHash & pParent->fHashTabMask;
2465                             pCur->uNameHash     = uNameHash;
2466                             pCur->pNextNameHash = pParent->papHashTab[idxNameHash];
2467                             pParent->papHashTab[idxNameHash] = pCur;
2468                             if (pCur->pNextNameHash)
2469                                 pCache->cChildHashCollisions++;
2470                             pCache->cChildHashed++;
2471                         }
2472                     }
2473                     else
2474                         kFsCacheDirAllocHashTabAndEnterChild(pCache, pParent, kFsCacheStrHashN(pchName, cchName), pCur);
2475                 }
2476 
2477                 pCache->cChildSearches++;
2478                 return pCur;
2479             }
2480         }
2481 
2482         pCache->cChildSearches++;
2483         return NULL;
2484     }
2485     return &pParent->Obj;
2486 }
2487 
2488 
2489 /**
2490  * Look up a child node, UTF-16 version.
2491  *
2492  * @returns Pointer to the child if found, NULL if not.
2493  * @param   pCache              The cache.
2494  * @param   pParent             The parent directory to search.
2495  * @param   pwcName             The child name to search for (not terminated).
2496  * @param   cwcName             The length of the child name (in wchar_t's).
2497  */
kFsCacheFindChildW(PKFSCACHE pCache,PKFSDIR pParent,const wchar_t * pwcName,KU32 cwcName)2498 static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
2499 {
2500     /*
2501      * Check for '.' first ('..' won't appear).
2502      */
2503     if (cwcName != 1 || *pwcName != '.')
2504     {
2505         PKFSOBJ    *ppCur;
2506         KU32        cLeft;
2507         KU32        uNameHash;
2508 
2509         /*
2510          * Do hash table lookup.
2511          *
2512          * This caches previous lookups, which should be useful when looking up
2513          * intermediate directories at least.
2514          */
2515         if (pParent->papHashTab != NULL)
2516         {
2517             PKFSOBJ pCur;
2518             uNameHash = kFsCacheUtf16HashN(pwcName, cwcName);
2519             pCur = pParent->papHashTab[uNameHash & pParent->fHashTabMask];
2520             while (pCur)
2521             {
2522                 if (   pCur->uNameHash == uNameHash
2523                     && (   (   pCur->cwcName == cwcName
2524                             && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2525 #ifdef KFSCACHE_CFG_SHORT_NAMES
2526                          || (   pCur->cwcShortName == cwcName
2527                              && pCur->pwszShortName != pCur->pwszName
2528                              && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2529 #endif
2530                        )
2531                    )
2532                 {
2533                     pCache->cChildHashHits++;
2534                     pCache->cChildSearches++;
2535                     return pCur;
2536                 }
2537                 pCur = pCur->pNextNameHash;
2538             }
2539         }
2540         else
2541             uNameHash = 0;
2542 
2543         /*
2544          * Do linear search.
2545          */
2546         cLeft = pParent->cChildren;
2547         ppCur = pParent->papChildren;
2548         while (cLeft-- > 0)
2549         {
2550             PKFSOBJ pCur = *ppCur++;
2551             if (   (   pCur->cwcName == cwcName
2552                     && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2553 #ifdef KFSCACHE_CFG_SHORT_NAMES
2554                 || (   pCur->cwcShortName == cwcName
2555                     && pCur->pwszShortName != pCur->pwszName
2556                     && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2557 #endif
2558                )
2559             {
2560                 /*
2561                  * Consider entering it into the parent hash table.
2562                  * Note! We hash the input, not the name we found.
2563                  */
2564                 if (   pCur->uNameHash == 0
2565                     && pParent->cChildren >= 4)
2566                 {
2567                     if (pParent->papHashTab)
2568                     {
2569                         if (uNameHash != 0)
2570                         {
2571                             KU32 idxNameHash = uNameHash & pParent->fHashTabMask;
2572                             pCur->uNameHash     = uNameHash;
2573                             pCur->pNextNameHash = pParent->papHashTab[idxNameHash];
2574                             pParent->papHashTab[idxNameHash] = pCur;
2575                             if (pCur->pNextNameHash)
2576                                 pCache->cChildHashCollisions++;
2577                             pCache->cChildHashed++;
2578                         }
2579                     }
2580                     else
2581                         kFsCacheDirAllocHashTabAndEnterChild(pCache, pParent, kFsCacheUtf16HashN(pwcName, cwcName), pCur);
2582                 }
2583 
2584                 pCache->cChildSearches++;
2585                 return pCur;
2586             }
2587         }
2588         pCache->cChildSearches++;
2589         return NULL;
2590     }
2591     return &pParent->Obj;
2592 }
2593 
2594 
2595 /**
2596  * Looks up a UNC share, ANSI version.
2597  *
2598  * We keep both the server and share in the root directory entry.  This means we
2599  * have to clean up the entry name before we can insert it.
2600  *
2601  * @returns Pointer to the share root directory or an update-to-date missing
2602  *          node.
2603  * @param   pCache              The cache.
2604  * @param   pszPath             The path.
2605  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2606  * @param   poff                Where to return the root dire.
2607  * @param   penmError           Where to return details as to why the lookup
2608  *                              failed.
2609  */
kFsCacheLookupUncShareA(PKFSCACHE pCache,const char * pszPath,KU32 fFlags,KU32 * poff,KFSLOOKUPERROR * penmError)2610 static PKFSOBJ kFsCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 fFlags,
2611                                        KU32 *poff, KFSLOOKUPERROR *penmError)
2612 {
2613     /*
2614      * Special case: Long path prefix w/ drive letter following it.
2615      * Note! Must've been converted from wide char to ANSI.
2616      */
2617     if (   IS_SLASH(pszPath[0])
2618         && IS_SLASH(pszPath[1])
2619         && pszPath[2] == '?'
2620         && IS_SLASH(pszPath[3])
2621         && IS_ALPHA(pszPath[4])
2622         && pszPath[5] == ':'
2623         && IS_SLASH(pszPath[6]) )
2624     {
2625         *poff = 4 + 2;
2626         return kFsCacheLookupDrive(pCache, pszPath[4], fFlags, penmError);
2627     }
2628 
2629 #if 0 /* later */
2630     KU32 offStartServer;
2631     KU32 offEndServer;
2632     KU32 offStartShare;
2633 
2634     KU32 offEnd = 2;
2635     while (IS_SLASH(pszPath[offEnd]))
2636         offEnd++;
2637 
2638     offStartServer = offEnd;
2639     while (   (ch = pszPath[offEnd]) != '\0'
2640            && !IS_SLASH(ch))
2641         offEnd++;
2642     offEndServer = offEnd;
2643 
2644     if (ch != '\0')
2645     { /* likely */ }
2646     else
2647     {
2648         *penmError = KFSLOOKUPERROR_NOT_FOUND;
2649         return NULL;
2650     }
2651 
2652     while (IS_SLASH(pszPath[offEnd]))
2653         offEnd++;
2654     offStartServer = offEnd;
2655     while (   (ch = pszPath[offEnd]) != '\0'
2656            && !IS_SLASH(ch))
2657         offEnd++;
2658 #endif
2659     *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2660     return NULL;
2661 }
2662 
2663 
2664 /**
2665  * Looks up a UNC share, UTF-16 version.
2666  *
2667  * We keep both the server and share in the root directory entry.  This means we
2668  * have to clean up the entry name before we can insert it.
2669  *
2670  * @returns Pointer to the share root directory or an update-to-date missing
2671  *          node.
2672  * @param   pCache              The cache.
2673  * @param   pwszPath            The path.
2674  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2675  * @param   poff                Where to return the root dir.
2676  * @param   penmError           Where to return details as to why the lookup
2677  *                              failed.
2678  */
kFsCacheLookupUncShareW(PKFSCACHE pCache,const wchar_t * pwszPath,KU32 fFlags,KU32 * poff,KFSLOOKUPERROR * penmError)2679 static PKFSOBJ kFsCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 fFlags,
2680                                        KU32 *poff, KFSLOOKUPERROR *penmError)
2681 {
2682     /*
2683      * Special case: Long path prefix w/ drive letter following it.
2684      */
2685     if (   IS_SLASH(pwszPath[0])
2686         && IS_SLASH(pwszPath[1])
2687         && pwszPath[2] == '?'
2688         && IS_SLASH(pwszPath[3])
2689         && IS_ALPHA(pwszPath[4])
2690         && pwszPath[5] == ':'
2691         && IS_SLASH(pwszPath[6]) )
2692     {
2693         *poff = 4 + 2;
2694         return kFsCacheLookupDrive(pCache, (char)pwszPath[4], fFlags, penmError);
2695     }
2696 
2697 
2698 #if 0 /* later */
2699     KU32 offStartServer;
2700     KU32 offEndServer;
2701     KU32 offStartShare;
2702 
2703     KU32 offEnd = 2;
2704     while (IS_SLASH(pwszPath[offEnd]))
2705         offEnd++;
2706 
2707     offStartServer = offEnd;
2708     while (   (ch = pwszPath[offEnd]) != '\0'
2709            && !IS_SLASH(ch))
2710         offEnd++;
2711     offEndServer = offEnd;
2712 
2713     if (ch != '\0')
2714     { /* likely */ }
2715     else
2716     {
2717         *penmError = KFSLOOKUPERROR_NOT_FOUND;
2718         return NULL;
2719     }
2720 
2721     while (IS_SLASH(pwszPath[offEnd]))
2722         offEnd++;
2723     offStartServer = offEnd;
2724     while (   (ch = pwszPath[offEnd]) != '\0'
2725            && !IS_SLASH(ch))
2726         offEnd++;
2727 #endif
2728     *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2729     return NULL;
2730 }
2731 
2732 
2733 /**
2734  * Walks an full path relative to the given directory, ANSI version.
2735  *
2736  * This will create any missing nodes while walking.
2737  *
2738  * The caller will have to do the path hash table insertion of the result.
2739  *
2740  * @returns Pointer to the tree node corresponding to @a pszPath.
2741  *          NULL on lookup failure, see @a penmError for details.
2742  * @param   pCache              The cache.
2743  * @param   pParent             The directory to start the lookup in.
2744  * @param   pszPath             The path to walk.
2745  * @param   cchPath             The length of the path.
2746  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2747  * @param   penmError           Where to return details as to why the lookup
2748  *                              failed.
2749  * @param   ppLastAncestor      Where to return the last parent element found
2750  *                              (referenced) in case of error like an path/file
2751  *                              not found problem.  Optional.
2752  */
kFsCacheLookupRelativeToDirA(PKFSCACHE pCache,PKFSDIR pParent,const char * pszPath,KU32 cchPath,KU32 fFlags,KFSLOOKUPERROR * penmError,PKFSOBJ * ppLastAncestor)2753 PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags,
2754                                      KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2755 {
2756     /*
2757      * Walk loop.
2758      */
2759     KU32 off = 0;
2760     if (ppLastAncestor)
2761         *ppLastAncestor = NULL;
2762     for (;;)
2763     {
2764         PKFSOBJ pChild;
2765 
2766         /*
2767          * Find the end of the component, counting trailing slashes.
2768          */
2769         char    ch;
2770         KU32    cchSlashes = 0;
2771         KU32    offEnd     = off + 1;
2772         while ((ch = pszPath[offEnd]) != '\0')
2773         {
2774             if (!IS_SLASH(ch))
2775                 offEnd++;
2776             else
2777             {
2778                 do
2779                     cchSlashes++;
2780                 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2781                 break;
2782             }
2783         }
2784 
2785         /*
2786          * Do we need to populate or refresh this directory first?
2787          */
2788         if (   !pParent->fNeedRePopulating
2789             && pParent->fPopulated
2790             && (   pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2791                 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
2792         { /* likely */ }
2793         else if (   (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
2794                  || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2795         { /* likely */ }
2796         else
2797         {
2798             if (ppLastAncestor)
2799                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2800             return NULL;
2801         }
2802 
2803         /*
2804          * Search the current node for the name.
2805          *
2806          * If we don't find it, we may insert a missing node depending on
2807          * the cache configuration.
2808          */
2809         pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
2810         if (pChild != NULL)
2811         { /* probably likely */ }
2812         else
2813         {
2814             if (    (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2815                 && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
2816                 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
2817             if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
2818             {
2819                 if (pChild)
2820                     return kFsCacheObjRetainInternal(pChild);
2821                 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2822             }
2823             else
2824                 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2825             if (ppLastAncestor)
2826                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2827             return NULL;
2828         }
2829 
2830         /* Advance off and check if we're done already. */
2831         off = offEnd + cchSlashes;
2832         if (   cchSlashes == 0
2833             || off >= cchPath)
2834         {
2835             if (   pChild->bObjType != KFSOBJ_TYPE_MISSING
2836                 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2837                 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2838                 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2839                 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2840             { /* likely */ }
2841             else
2842             {
2843                 if (ppLastAncestor)
2844                     *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2845                 return NULL;
2846             }
2847             return kFsCacheObjRetainInternal(pChild);
2848         }
2849 
2850         /*
2851          * Check that it's a directory.  If a missing entry, we may have to
2852          * refresh it and re-examin it.
2853          */
2854         if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2855             pParent = (PKFSDIR)pChild;
2856         else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2857         {
2858             *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2859             if (ppLastAncestor)
2860                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2861             return NULL;
2862         }
2863         else if (   pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2864                  || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2865                  || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
2866         {
2867             *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2868             if (ppLastAncestor)
2869                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2870             return NULL;
2871         }
2872         else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2873             pParent = (PKFSDIR)pChild;
2874         else
2875         {
2876             if (ppLastAncestor)
2877                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2878             return NULL;
2879         }
2880     }
2881 
2882     /* not reached */
2883     return NULL;
2884 }
2885 
2886 
2887 /**
2888  * Walks an full path relative to the given directory, UTF-16 version.
2889  *
2890  * This will create any missing nodes while walking.
2891  *
2892  * The caller will have to do the path hash table insertion of the result.
2893  *
2894  * @returns Pointer to the tree node corresponding to @a pszPath.
2895  *          NULL on lookup failure, see @a penmError for details.
2896  * @param   pCache              The cache.
2897  * @param   pParent             The directory to start the lookup in.
2898  * @param   pszPath             The path to walk.  No dot-dot bits allowed!
2899  * @param   cchPath             The length of the path.
2900  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2901  * @param   penmError           Where to return details as to why the lookup
2902  *                              failed.
2903  * @param   ppLastAncestor      Where to return the last parent element found
2904  *                              (referenced) in case of error like an path/file
2905  *                              not found problem.  Optional.
2906  */
kFsCacheLookupRelativeToDirW(PKFSCACHE pCache,PKFSDIR pParent,const wchar_t * pwszPath,KU32 cwcPath,KU32 fFlags,KFSLOOKUPERROR * penmError,PKFSOBJ * ppLastAncestor)2907 PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
2908                                      KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2909 {
2910     /*
2911      * Walk loop.
2912      */
2913     KU32 off = 0;
2914     if (ppLastAncestor)
2915         *ppLastAncestor = NULL;
2916     for (;;)
2917     {
2918         PKFSOBJ pChild;
2919 
2920         /*
2921          * Find the end of the component, counting trailing slashes.
2922          */
2923         wchar_t wc;
2924         KU32    cwcSlashes = 0;
2925         KU32    offEnd     = off + 1;
2926         while ((wc = pwszPath[offEnd]) != '\0')
2927         {
2928             if (!IS_SLASH(wc))
2929                 offEnd++;
2930             else
2931             {
2932                 do
2933                     cwcSlashes++;
2934                 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2935                 break;
2936             }
2937         }
2938 
2939         /*
2940          * Do we need to populate or refresh this directory first?
2941          */
2942         if (   !pParent->fNeedRePopulating
2943             && pParent->fPopulated
2944             && (   pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2945                 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
2946         { /* likely */ }
2947         else if (   (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
2948                  || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2949         { /* likely */ }
2950         else
2951         {
2952             if (ppLastAncestor)
2953                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2954             return NULL;
2955         }
2956 
2957         /*
2958          * Search the current node for the name.
2959          *
2960          * If we don't find it, we may insert a missing node depending on
2961          * the cache configuration.
2962          */
2963         pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off);
2964         if (pChild != NULL)
2965         { /* probably likely */ }
2966         else
2967         {
2968             if (    (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2969                 && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
2970                 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
2971             if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
2972             {
2973                 if (pChild)
2974                     return kFsCacheObjRetainInternal(pChild);
2975                 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2976             }
2977             else
2978                 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2979             if (ppLastAncestor)
2980                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2981             return NULL;
2982         }
2983 
2984         /* Advance off and check if we're done already. */
2985         off = offEnd + cwcSlashes;
2986         if (   cwcSlashes == 0
2987             || off >= cwcPath)
2988         {
2989             if (   pChild->bObjType != KFSOBJ_TYPE_MISSING
2990                 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2991                 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2992                 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2993                 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2994             { /* likely */ }
2995             else
2996             {
2997                 if (ppLastAncestor)
2998                     *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2999                 return NULL;
3000             }
3001             return kFsCacheObjRetainInternal(pChild);
3002         }
3003 
3004         /*
3005          * Check that it's a directory.  If a missing entry, we may have to
3006          * refresh it and re-examin it.
3007          */
3008         if (pChild->bObjType == KFSOBJ_TYPE_DIR)
3009             pParent = (PKFSDIR)pChild;
3010         else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
3011         {
3012             *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
3013             if (ppLastAncestor)
3014                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3015             return NULL;
3016         }
3017         else if (   pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3018                  || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3019                  || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) )
3020 
3021         {
3022             *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
3023             if (ppLastAncestor)
3024                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3025             return NULL;
3026         }
3027         else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
3028             pParent = (PKFSDIR)pChild;
3029         else
3030         {
3031             if (ppLastAncestor)
3032                 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3033             return NULL;
3034         }
3035     }
3036 
3037     return NULL;
3038 }
3039 
3040 /**
3041  * Walk the file system tree for the given absolute path, entering it into the
3042  * hash table.
3043  *
3044  * This will create any missing nodes while walking.
3045  *
3046  * The caller will have to do the path hash table insertion of the result.
3047  *
3048  * @returns Pointer to the tree node corresponding to @a pszPath.
3049  *          NULL on lookup failure, see @a penmError for details.
3050  * @param   pCache              The cache.
3051  * @param   pszPath             The path to walk. No dot-dot bits allowed!
3052  * @param   cchPath             The length of the path.
3053  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3054  * @param   penmError           Where to return details as to why the lookup
3055  *                              failed.
3056  * @param   ppLastAncestor      Where to return the last parent element found
3057  *                              (referenced) in case of error an path/file not
3058  *                              found problem.  Optional.
3059  */
kFsCacheLookupAbsoluteA(PKFSCACHE pCache,const char * pszPath,KU32 cchPath,KU32 fFlags,KFSLOOKUPERROR * penmError,PKFSOBJ * ppLastAncestor)3060 static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
3061                                        KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3062 {
3063     PKFSOBJ     pRoot;
3064     KU32        cchSlashes;
3065     KU32        offEnd;
3066 
3067     KFSCACHE_LOG2(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
3068 
3069     /*
3070      * The root "directory" needs special handling, so we keep it outside the
3071      * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
3072      */
3073     cchSlashes = 0;
3074     if (   pszPath[1] == ':'
3075         && IS_ALPHA(pszPath[0]))
3076     {
3077         /* Drive letter. */
3078         offEnd = 2;
3079         kHlpAssert(IS_SLASH(pszPath[2]));
3080         pRoot = kFsCacheLookupDrive(pCache, toupper(pszPath[0]), fFlags, penmError);
3081     }
3082     else if (   IS_SLASH(pszPath[0])
3083              && IS_SLASH(pszPath[1]) )
3084         pRoot = kFsCacheLookupUncShareA(pCache, pszPath, fFlags, &offEnd, penmError);
3085     else
3086     {
3087         *penmError = KFSLOOKUPERROR_UNSUPPORTED;
3088         return NULL;
3089     }
3090     if (pRoot)
3091     { /* likely */ }
3092     else
3093         return NULL;
3094 
3095     /* Count slashes trailing the root spec. */
3096     if (offEnd < cchPath)
3097     {
3098         kHlpAssert(IS_SLASH(pszPath[offEnd]));
3099         do
3100             cchSlashes++;
3101         while (IS_SLASH(pszPath[offEnd + cchSlashes]));
3102     }
3103 
3104     /* Done already? */
3105     if (offEnd >= cchPath)
3106     {
3107         if (   pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3108             || pRoot->uCacheGen == (  pRoot->bObjType != KFSOBJ_TYPE_MISSING
3109                                     ? pCache->auGenerations[       pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3110                                     : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
3111             || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
3112             || kFsCacheRefreshObj(pCache, pRoot, penmError))
3113             return kFsCacheObjRetainInternal(pRoot);
3114         if (ppLastAncestor)
3115             *ppLastAncestor = kFsCacheObjRetainInternal(pRoot);
3116         return NULL;
3117     }
3118 
3119     /* Check that we've got a valid result and not a cached negative one. */
3120     if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
3121     { /* likely */ }
3122     else
3123     {
3124         kHlpAssert(pRoot->bObjType == KFSOBJ_TYPE_MISSING);
3125         kHlpAssert(   pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3126                    || pRoot->uCacheGen == pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]);
3127         return pRoot;
3128     }
3129 
3130     /*
3131      * Now that we've found a valid root directory, lookup the
3132      * remainder of the path starting with it.
3133      */
3134     return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pRoot, &pszPath[offEnd + cchSlashes],
3135                                         cchPath - offEnd - cchSlashes, fFlags, penmError, ppLastAncestor);
3136 }
3137 
3138 
3139 /**
3140  * Walk the file system tree for the given absolute path, UTF-16 version.
3141  *
3142  * This will create any missing nodes while walking.
3143  *
3144  * The caller will have to do the path hash table insertion of the result.
3145  *
3146  * @returns Pointer to the tree node corresponding to @a pszPath.
3147  *          NULL on lookup failure, see @a penmError for details.
3148  * @param   pCache              The cache.
3149  * @param   pwszPath            The path to walk.
3150  * @param   cwcPath             The length of the path (in wchar_t's).
3151  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3152  * @param   penmError           Where to return details as to why the lookup
3153  *                              failed.
3154  * @param   ppLastAncestor      Where to return the last parent element found
3155  *                              (referenced) in case of error an path/file not
3156  *                              found problem.  Optional.
3157  */
kFsCacheLookupAbsoluteW(PKFSCACHE pCache,const wchar_t * pwszPath,KU32 cwcPath,KU32 fFlags,KFSLOOKUPERROR * penmError,PKFSOBJ * ppLastAncestor)3158 static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
3159                                        KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3160 {
3161     PKFSDIR     pParent = &pCache->RootDir;
3162     PKFSOBJ     pRoot;
3163     KU32        off;
3164     KU32        cwcSlashes;
3165     KU32        offEnd;
3166 
3167     KFSCACHE_LOG2(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath));
3168 
3169     /*
3170      * The root "directory" needs special handling, so we keep it outside the
3171      * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
3172      */
3173     cwcSlashes = 0;
3174     off        = 0;
3175     if (   pwszPath[1] == ':'
3176         && IS_ALPHA(pwszPath[0]))
3177     {
3178         /* Drive letter. */
3179         offEnd = 2;
3180         kHlpAssert(IS_SLASH(pwszPath[2]));
3181         pRoot = kFsCacheLookupDrive(pCache, toupper(pwszPath[0]), fFlags, penmError);
3182     }
3183     else if (   IS_SLASH(pwszPath[0])
3184              && IS_SLASH(pwszPath[1]) )
3185         pRoot = kFsCacheLookupUncShareW(pCache, pwszPath, fFlags, &offEnd, penmError);
3186     else
3187     {
3188         *penmError = KFSLOOKUPERROR_UNSUPPORTED;
3189         return NULL;
3190     }
3191     if (pRoot)
3192     { /* likely */ }
3193     else
3194         return NULL;
3195 
3196     /* Count slashes trailing the root spec. */
3197     if (offEnd < cwcPath)
3198     {
3199         kHlpAssert(IS_SLASH(pwszPath[offEnd]));
3200         do
3201             cwcSlashes++;
3202         while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
3203     }
3204 
3205     /* Done already? */
3206     if (offEnd >= cwcPath)
3207     {
3208         if (   pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3209             || pRoot->uCacheGen == (pRoot->bObjType != KFSOBJ_TYPE_MISSING
3210                                     ? pCache->auGenerations[       pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3211                                     : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
3212             || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
3213             || kFsCacheRefreshObj(pCache, pRoot, penmError))
3214             return kFsCacheObjRetainInternal(pRoot);
3215         if (ppLastAncestor)
3216             *ppLastAncestor = kFsCacheObjRetainInternal(pRoot);
3217         return NULL;
3218     }
3219 
3220     /* Check that we've got a valid result and not a cached negative one. */
3221     if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
3222     { /* likely */ }
3223     else
3224     {
3225         kHlpAssert(pRoot->bObjType == KFSOBJ_TYPE_MISSING);
3226         kHlpAssert(   pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3227                    || pRoot->uCacheGen == pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]);
3228         return pRoot;
3229     }
3230 
3231     /*
3232      * Now that we've found a valid root directory, lookup the
3233      * remainder of the path starting with it.
3234      */
3235     return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pRoot, &pwszPath[offEnd + cwcSlashes],
3236                                         cwcPath - offEnd - cwcSlashes, fFlags, penmError, ppLastAncestor);
3237 }
3238 
3239 
3240 /**
3241  * This deals with paths that are relative and paths that contains '..'
3242  * elements, ANSI version.
3243  *
3244  * @returns Pointer to object corresponding to @a pszPath on success.
3245  *          NULL if this isn't a path we care to cache.
3246  *
3247  * @param   pCache              The cache.
3248  * @param   pszPath             The path.
3249  * @param   cchPath             The length of the path.
3250  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3251  * @param   penmError           Where to return details as to why the lookup
3252  *                              failed.
3253  * @param   ppLastAncestor      Where to return the last parent element found
3254  *                              (referenced) in case of error an path/file not
3255  *                              found problem.  Optional.
3256  */
kFsCacheLookupSlowA(PKFSCACHE pCache,const char * pszPath,KU32 cchPath,KU32 fFlags,KFSLOOKUPERROR * penmError,PKFSOBJ * ppLastAncestor)3257 static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
3258                                    KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3259 {
3260     /*
3261      * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
3262      * ends up calling it anyway.
3263      */
3264     char szFull[KFSCACHE_CFG_MAX_PATH];
3265     UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
3266     if (   cchFull >= 3
3267         && cchFull < sizeof(szFull))
3268     {
3269         KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath));
3270         return kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, fFlags, penmError, ppLastAncestor);
3271     }
3272 
3273     /* The path is too long! */
3274     kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
3275     *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3276     return NULL;
3277 }
3278 
3279 
3280 /**
3281  * This deals with paths that are relative and paths that contains '..'
3282  * elements, UTF-16 version.
3283  *
3284  * @returns Pointer to object corresponding to @a pszPath on success.
3285  *          NULL if this isn't a path we care to cache.
3286  *
3287  * @param   pCache              The cache.
3288  * @param   pwszPath            The path.
3289  * @param   cwcPath             The length of the path (in wchar_t's).
3290  * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3291  * @param   penmError           Where to return details as to why the lookup
3292  *                              failed.
3293  * @param   ppLastAncestor      Where to return the last parent element found
3294  *                              (referenced) in case of error an path/file not
3295  *                              found problem.  Optional.
3296  */
kFsCacheLookupSlowW(PKFSCACHE pCache,const wchar_t * pwszPath,KU32 wcwPath,KU32 fFlags,KFSLOOKUPERROR * penmError,PKFSOBJ * ppLastAncestor)3297 static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KU32 fFlags,
3298                                    KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3299 {
3300     /*
3301      * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
3302      * ends up calling it anyway.
3303      */
3304     wchar_t wszFull[KFSCACHE_CFG_MAX_PATH];
3305     UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL);
3306     if (   cwcFull >= 3
3307         && cwcFull < KFSCACHE_CFG_MAX_PATH)
3308     {
3309         KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
3310         return kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, fFlags, penmError, ppLastAncestor);
3311     }
3312 
3313     /* The path is too long! */
3314     kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull));
3315     *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3316     return NULL;
3317 }
3318 
3319 
3320 /**
3321  * Refreshes a path hash that has expired, ANSI version.
3322  *
3323  * @returns pHash on success, NULL if removed.
3324  * @param   pCache              The cache.
3325  * @param   pHashEntry          The path hash.
3326  * @param   idxHashTab          The hash table entry.
3327  */
kFsCacheRefreshPathA(PKFSCACHE pCache,PKFSHASHA pHashEntry,KU32 idxHashTab)3328 static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
3329 {
3330     PKFSOBJ pLastAncestor = NULL;
3331     if (!pHashEntry->pFsObj)
3332     {
3333         if (pHashEntry->fAbsolute)
3334             pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3335                                                          &pHashEntry->enmError, &pLastAncestor);
3336         else
3337             pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3338                                                      &pHashEntry->enmError, &pLastAncestor);
3339     }
3340     else
3341     {
3342         KU8             bOldType = pHashEntry->pFsObj->bObjType;
3343         KFSLOOKUPERROR  enmError;
3344         if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
3345         {
3346             if (pHashEntry->pFsObj->bObjType == bOldType)
3347             { }
3348             else
3349             {
3350                 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
3351                 if (pHashEntry->fAbsolute)
3352                     pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3353                                                                  &pHashEntry->enmError, &pLastAncestor);
3354                 else
3355                     pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3356                                                              &pHashEntry->enmError, &pLastAncestor);
3357             }
3358         }
3359         else
3360         {
3361             fprintf(stderr, "kFsCacheRefreshPathA - refresh failure handling not implemented!\n");
3362             __debugbreak();
3363             /** @todo just remove this entry.   */
3364             return NULL;
3365         }
3366     }
3367 
3368     if (pLastAncestor && !pHashEntry->pFsObj)
3369         pHashEntry->idxMissingGen = pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN;
3370     pHashEntry->uCacheGen = !pHashEntry->pFsObj
3371                           ? pCache->auGenerationsMissing[pHashEntry->idxMissingGen]
3372                           : pHashEntry->pFsObj->bObjType == KFSOBJ_TYPE_MISSING
3373                           ? pCache->auGenerationsMissing[pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3374                           : pCache->auGenerations[       pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
3375     if (pLastAncestor)
3376         kFsCacheObjRelease(pCache, pLastAncestor);
3377     return pHashEntry;
3378 }
3379 
3380 
3381 /**
3382  * Refreshes a path hash that has expired, UTF-16 version.
3383  *
3384  * @returns pHash on success, NULL if removed.
3385  * @param   pCache              The cache.
3386  * @param   pHashEntry          The path hash.
3387  * @param   idxHashTab          The hash table entry.
3388  */
kFsCacheRefreshPathW(PKFSCACHE pCache,PKFSHASHW pHashEntry,KU32 idxHashTab)3389 static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab)
3390 {
3391     PKFSOBJ pLastAncestor = NULL;
3392     if (!pHashEntry->pFsObj)
3393     {
3394         if (pHashEntry->fAbsolute)
3395             pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3396                                                          &pHashEntry->enmError, &pLastAncestor);
3397         else
3398             pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3399                                                      &pHashEntry->enmError, &pLastAncestor);
3400     }
3401     else
3402     {
3403         KU8             bOldType = pHashEntry->pFsObj->bObjType;
3404         KFSLOOKUPERROR  enmError;
3405         if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
3406         {
3407             if (pHashEntry->pFsObj->bObjType == bOldType)
3408             { }
3409             else
3410             {
3411                 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
3412                 if (pHashEntry->fAbsolute)
3413                     pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3414                                                                  &pHashEntry->enmError, &pLastAncestor);
3415                 else
3416                     pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3417                                                              &pHashEntry->enmError, &pLastAncestor);
3418             }
3419         }
3420         else
3421         {
3422             fprintf(stderr, "kFsCacheRefreshPathW - refresh failure handling not implemented!\n");
3423             fflush(stderr);
3424             __debugbreak();
3425             /** @todo just remove this entry.   */
3426             return NULL;
3427         }
3428     }
3429     if (pLastAncestor && !pHashEntry->pFsObj)
3430         pHashEntry->idxMissingGen = pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN;
3431     pHashEntry->uCacheGen = !pHashEntry->pFsObj
3432                           ? pCache->auGenerationsMissing[pHashEntry->idxMissingGen]
3433                           : pHashEntry->pFsObj->bObjType == KFSOBJ_TYPE_MISSING
3434                           ? pCache->auGenerationsMissing[pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3435                           : pCache->auGenerations[       pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
3436     if (pLastAncestor)
3437         kFsCacheObjRelease(pCache, pLastAncestor);
3438     return pHashEntry;
3439 }
3440 
3441 
3442 /**
3443  * Internal lookup worker that looks up a KFSOBJ for the given ANSI path with
3444  * length and hash.
3445  *
3446  * This will first try the hash table.  If not in the hash table, the file
3447  * system cache tree is walked, missing bits filled in and finally a hash table
3448  * entry is created.
3449  *
3450  * Only drive letter paths are cachable.  We don't do any UNC paths at this
3451  * point.
3452  *
3453  * @returns Reference to object corresponding to @a pszPath on success, this
3454  *          must be released by kFsCacheObjRelease.
3455  *          NULL if not a path we care to cache.
3456  * @param   pCache              The cache.
3457  * @param   pchPath             The path to lookup.
3458  * @param   cchPath             The path length.
3459  * @param   uHashPath           The hash of the path.
3460  * @param   penmError           Where to return details as to why the lookup
3461  *                              failed.
3462  */
kFsCacheLookupHashedA(PKFSCACHE pCache,const char * pchPath,KU32 cchPath,KU32 uHashPath,KFSLOOKUPERROR * penmError)3463 static PKFSOBJ kFsCacheLookupHashedA(PKFSCACHE pCache, const char *pchPath, KU32 cchPath, KU32 uHashPath,
3464                                      KFSLOOKUPERROR *penmError)
3465 {
3466     /*
3467      * Do hash table lookup of the path.
3468      */
3469     KU32        idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3470     PKFSHASHA   pHashEntry = pCache->apAnsiPaths[idxHashTab];
3471     kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3472     if (pHashEntry)
3473     {
3474         do
3475         {
3476             if (   pHashEntry->uHashPath == uHashPath
3477                 && pHashEntry->cchPath   == cchPath
3478                 && kHlpMemComp(pHashEntry->pszPath, pchPath, cchPath) == 0)
3479             {
3480                 PKFSOBJ pFsObj;
3481                 if (   pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3482                     || pHashEntry->uCacheGen == (  (pFsObj = pHashEntry->pFsObj) != NULL
3483                                                  ? pFsObj->bObjType != KFSOBJ_TYPE_MISSING
3484                                                    ? pCache->auGenerations[       pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3485                                                    : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3486                                                  : pCache->auGenerationsMissing[pHashEntry->idxMissingGen])
3487                     || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
3488                 {
3489                     pCache->cLookups++;
3490                     pCache->cPathHashHits++;
3491                     KFSCACHE_LOG2(("kFsCacheLookupA(%*.*s) - hit %p\n", cchPath, cchPath, pchPath, pHashEntry->pFsObj));
3492                     *penmError = pHashEntry->enmError;
3493                     if (pHashEntry->pFsObj)
3494                         return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3495                     return NULL;
3496                 }
3497                 break;
3498             }
3499             pHashEntry = pHashEntry->pNext;
3500         } while (pHashEntry);
3501     }
3502 
3503     /*
3504      * Create an entry for it by walking the file system cache and filling in the blanks.
3505      */
3506     if (   cchPath > 0
3507         && cchPath < KFSCACHE_CFG_MAX_PATH)
3508     {
3509         PKFSOBJ pFsObj;
3510         KBOOL   fAbsolute;
3511         PKFSOBJ pLastAncestor = NULL;
3512 
3513         /* Is absolute without any '..' bits? */
3514         if (   cchPath >= 3
3515             && (   (   pchPath[1] == ':'    /* Drive letter */
3516                     && IS_SLASH(pchPath[2])
3517                     && IS_ALPHA(pchPath[0]) )
3518                 || (   IS_SLASH(pchPath[0]) /* UNC */
3519                     && IS_SLASH(pchPath[1]) ) )
3520             && !kFsCacheHasDotDotA(pchPath, cchPath) )
3521         {
3522             pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3523             fAbsolute = K_TRUE;
3524         }
3525         else
3526         {
3527             pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3528             fAbsolute = K_FALSE;
3529         }
3530         if (   pFsObj
3531             || (   (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3532                 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3533             || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3534             kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pchPath, cchPath, uHashPath, idxHashTab, fAbsolute,
3535                                             pLastAncestor ? pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN : 0, *penmError);
3536         if (pLastAncestor)
3537             kFsCacheObjRelease(pCache, pLastAncestor);
3538 
3539         pCache->cLookups++;
3540         if (pFsObj)
3541             pCache->cWalkHits++;
3542         return pFsObj;
3543     }
3544 
3545     *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3546     return NULL;
3547 }
3548 
3549 
3550 /**
3551  * Internal lookup worker that looks up a KFSOBJ for the given UTF-16 path with
3552  * length and hash.
3553  *
3554  * This will first try the hash table.  If not in the hash table, the file
3555  * system cache tree is walked, missing bits filled in and finally a hash table
3556  * entry is created.
3557  *
3558  * Only drive letter paths are cachable.  We don't do any UNC paths at this
3559  * point.
3560  *
3561  * @returns Reference to object corresponding to @a pwcPath on success, this
3562  *          must be released by kFsCacheObjRelease.
3563  *          NULL if not a path we care to cache.
3564  * @param   pCache              The cache.
3565  * @param   pwcPath             The path to lookup.
3566  * @param   cwcPath             The length of the path (in wchar_t's).
3567  * @param   uHashPath           The hash of the path.
3568  * @param   penmError           Where to return details as to why the lookup
3569  *                              failed.
3570  */
kFsCacheLookupHashedW(PKFSCACHE pCache,const wchar_t * pwcPath,KU32 cwcPath,KU32 uHashPath,KFSLOOKUPERROR * penmError)3571 static PKFSOBJ kFsCacheLookupHashedW(PKFSCACHE pCache, const wchar_t *pwcPath, KU32 cwcPath, KU32 uHashPath,
3572                                      KFSLOOKUPERROR *penmError)
3573 {
3574     /*
3575      * Do hash table lookup of the path.
3576      */
3577     KU32        idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3578     PKFSHASHW   pHashEntry = pCache->apUtf16Paths[idxHashTab];
3579     kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3580     if (pHashEntry)
3581     {
3582         do
3583         {
3584             if (   pHashEntry->uHashPath == uHashPath
3585                 && pHashEntry->cwcPath   == cwcPath
3586                 && kHlpMemComp(pHashEntry->pwszPath, pwcPath, cwcPath) == 0)
3587             {
3588                 PKFSOBJ pFsObj;
3589                 if (   pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3590                     || pHashEntry->uCacheGen == ((pFsObj = pHashEntry->pFsObj) != NULL
3591                                                  ? pFsObj->bObjType != KFSOBJ_TYPE_MISSING
3592                                                    ? pCache->auGenerations[       pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3593                                                    : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3594                                                  : pCache->auGenerationsMissing[pHashEntry->idxMissingGen])
3595                     || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) )
3596                 {
3597                     pCache->cLookups++;
3598                     pCache->cPathHashHits++;
3599                     KFSCACHE_LOG2(("kFsCacheLookupW(%*.*ls) - hit %p\n", cwcPath, cwcPath, pwcPath, pHashEntry->pFsObj));
3600                     *penmError = pHashEntry->enmError;
3601                     if (pHashEntry->pFsObj)
3602                         return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3603                     return NULL;
3604                 }
3605                 break;
3606             }
3607             pHashEntry = pHashEntry->pNext;
3608         } while (pHashEntry);
3609     }
3610 
3611     /*
3612      * Create an entry for it by walking the file system cache and filling in the blanks.
3613      */
3614     if (   cwcPath > 0
3615         && cwcPath < KFSCACHE_CFG_MAX_PATH)
3616     {
3617         PKFSOBJ pFsObj;
3618         KBOOL   fAbsolute;
3619         PKFSOBJ pLastAncestor = NULL;
3620 
3621         /* Is absolute without any '..' bits? */
3622         if (   cwcPath >= 3
3623             && (   (   pwcPath[1] == ':'    /* Drive letter */
3624                     && IS_SLASH(pwcPath[2])
3625                     && IS_ALPHA(pwcPath[0]) )
3626                 || (   IS_SLASH(pwcPath[0]) /* UNC */
3627                     && IS_SLASH(pwcPath[1]) ) )
3628             && !kFsCacheHasDotDotW(pwcPath, cwcPath) )
3629         {
3630             pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3631             fAbsolute = K_TRUE;
3632         }
3633         else
3634         {
3635             pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3636             fAbsolute = K_FALSE;
3637         }
3638         if (   pFsObj
3639             || (   (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3640                 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3641             || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3642             kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwcPath, cwcPath, uHashPath, idxHashTab, fAbsolute,
3643                                             pLastAncestor ? pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN : 0, *penmError);
3644         if (pLastAncestor)
3645             kFsCacheObjRelease(pCache, pLastAncestor);
3646 
3647         pCache->cLookups++;
3648         if (pFsObj)
3649             pCache->cWalkHits++;
3650         return pFsObj;
3651     }
3652 
3653     *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3654     return NULL;
3655 }
3656 
3657 
3658 
3659 /**
3660  * Looks up a KFSOBJ for the given ANSI path.
3661  *
3662  * This will first try the hash table.  If not in the hash table, the file
3663  * system cache tree is walked, missing bits filled in and finally a hash table
3664  * entry is created.
3665  *
3666  * Only drive letter paths are cachable.  We don't do any UNC paths at this
3667  * point.
3668  *
3669  * @returns Reference to object corresponding to @a pszPath on success, this
3670  *          must be released by kFsCacheObjRelease.
3671  *          NULL if not a path we care to cache.
3672  * @param   pCache              The cache.
3673  * @param   pszPath             The path to lookup.
3674  * @param   penmError           Where to return details as to why the lookup
3675  *                              failed.
3676  */
kFsCacheLookupA(PKFSCACHE pCache,const char * pszPath,KFSLOOKUPERROR * penmError)3677 PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3678 {
3679     KU32 uHashPath;
3680     KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
3681     return kFsCacheLookupHashedA(pCache, pszPath, cchPath, uHashPath, penmError);
3682 }
3683 
3684 
3685 /**
3686  * Looks up a KFSOBJ for the given UTF-16 path.
3687  *
3688  * This will first try the hash table.  If not in the hash table, the file
3689  * system cache tree is walked, missing bits filled in and finally a hash table
3690  * entry is created.
3691  *
3692  * Only drive letter paths are cachable.  We don't do any UNC paths at this
3693  * point.
3694  *
3695  * @returns Reference to object corresponding to @a pwszPath on success, this
3696  *          must be released by kFsCacheObjRelease.
3697  *          NULL if not a path we care to cache.
3698  * @param   pCache              The cache.
3699  * @param   pwszPath            The path to lookup.
3700  * @param   penmError           Where to return details as to why the lookup
3701  *                              failed.
3702  */
kFsCacheLookupW(PKFSCACHE pCache,const wchar_t * pwszPath,KFSLOOKUPERROR * penmError)3703 PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3704 {
3705     KU32 uHashPath;
3706     KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath);
3707     return kFsCacheLookupHashedW(pCache, pwszPath, cwcPath, uHashPath, penmError);
3708 }
3709 
3710 
3711 /**
3712  * Looks up a KFSOBJ for the given ANSI path.
3713  *
3714  * This will first try the hash table.  If not in the hash table, the file
3715  * system cache tree is walked, missing bits filled in and finally a hash table
3716  * entry is created.
3717  *
3718  * Only drive letter paths are cachable.  We don't do any UNC paths at this
3719  * point.
3720  *
3721  * @returns Reference to object corresponding to @a pchPath on success, this
3722  *          must be released by kFsCacheObjRelease.
3723  *          NULL if not a path we care to cache.
3724  * @param   pCache              The cache.
3725  * @param   pchPath             The path to lookup (does not need to be nul
3726  *                              terminated).
3727  * @param   cchPath             The path length.
3728  * @param   penmError           Where to return details as to why the lookup
3729  *                              failed.
3730  */
kFsCacheLookupWithLengthA(PKFSCACHE pCache,const char * pchPath,KSIZE cchPath,KFSLOOKUPERROR * penmError)3731 PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError)
3732 {
3733     return kFsCacheLookupHashedA(pCache, pchPath, (KU32)cchPath, kFsCacheStrHashN(pchPath, cchPath), penmError);
3734 }
3735 
3736 
3737 /**
3738  * Looks up a KFSOBJ for the given UTF-16 path.
3739  *
3740  * This will first try the hash table.  If not in the hash table, the file
3741  * system cache tree is walked, missing bits filled in and finally a hash table
3742  * entry is created.
3743  *
3744  * Only drive letter paths are cachable.  We don't do any UNC paths at this
3745  * point.
3746  *
3747  * @returns Reference to object corresponding to @a pwchPath on success, this
3748  *          must be released by kFsCacheObjRelease.
3749  *          NULL if not a path we care to cache.
3750  * @param   pCache              The cache.
3751  * @param   pwcPath             The path to lookup (does not need to be nul
3752  *                              terminated).
3753  * @param   cwcPath             The path length (in wchar_t's).
3754  * @param   penmError           Where to return details as to why the lookup
3755  *                              failed.
3756  */
kFsCacheLookupWithLengthW(PKFSCACHE pCache,const wchar_t * pwcPath,KSIZE cwcPath,KFSLOOKUPERROR * penmError)3757 PKFSOBJ kFsCacheLookupWithLengthW(PKFSCACHE pCache, const wchar_t *pwcPath, KSIZE cwcPath, KFSLOOKUPERROR *penmError)
3758 {
3759     return kFsCacheLookupHashedW(pCache, pwcPath, (KU32)cwcPath, kFsCacheUtf16HashN(pwcPath, cwcPath), penmError);
3760 }
3761 
3762 
3763 /**
3764  * Wrapper around kFsCacheLookupA that drops KFSOBJ_TYPE_MISSING and returns
3765  * KFSLOOKUPERROR_NOT_FOUND instead.
3766  *
3767  * @returns Reference to object corresponding to @a pszPath on success, this
3768  *          must be released by kFsCacheObjRelease.
3769  *          NULL if not a path we care to cache.
3770  * @param   pCache              The cache.
3771  * @param   pszPath             The path to lookup.
3772  * @param   penmError           Where to return details as to why the lookup
3773  *                              failed.
3774  */
kFsCacheLookupNoMissingA(PKFSCACHE pCache,const char * pszPath,KFSLOOKUPERROR * penmError)3775 PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3776 {
3777     PKFSOBJ pObj = kFsCacheLookupA(pCache, pszPath, penmError);
3778     if (pObj)
3779     {
3780         if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3781             return pObj;
3782 
3783         kFsCacheObjRelease(pCache, pObj);
3784         *penmError = KFSLOOKUPERROR_NOT_FOUND;
3785     }
3786     return NULL;
3787 }
3788 
3789 
3790 /**
3791  * Wrapper around kFsCacheLookupW that drops KFSOBJ_TYPE_MISSING and returns
3792  * KFSLOOKUPERROR_NOT_FOUND instead.
3793  *
3794  * @returns Reference to object corresponding to @a pszPath on success, this
3795  *          must be released by kFsCacheObjRelease.
3796  *          NULL if not a path we care to cache.
3797  * @param   pCache              The cache.
3798  * @param   pwszPath            The path to lookup.
3799  * @param   penmError           Where to return details as to why the lookup
3800  *                              failed.
3801  */
kFsCacheLookupNoMissingW(PKFSCACHE pCache,const wchar_t * pwszPath,KFSLOOKUPERROR * penmError)3802 PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3803 {
3804     PKFSOBJ pObj = kFsCacheLookupW(pCache, pwszPath, penmError);
3805     if (pObj)
3806     {
3807         if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3808             return pObj;
3809 
3810         kFsCacheObjRelease(pCache, pObj);
3811         *penmError = KFSLOOKUPERROR_NOT_FOUND;
3812     }
3813     return NULL;
3814 }
3815 
3816 
3817 /**
3818  * Destroys a cache object which has a zero reference count.
3819  *
3820  * @returns 0
3821  * @param   pCache              The cache.
3822  * @param   pObj                The object.
3823  * @param   pszWhere            Where it was released from.
3824  */
kFsCacheObjDestroy(PKFSCACHE pCache,PKFSOBJ pObj,const char * pszWhere)3825 KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere)
3826 {
3827     kHlpAssert(pObj->cRefs == 0);
3828     kHlpAssert(pObj->pParent == NULL);
3829     kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3830 
3831     KFSCACHE_LOG(("Destroying %s/%s, type=%d, pObj=%p, pszWhere=%s\n",
3832                   pObj->pParent ? pObj->pParent->Obj.pszName : "", pObj->pszName, pObj->bObjType, pObj, pszWhere));
3833     if (pObj->abUnused[1] != 0)
3834     {
3835         fprintf(stderr, "Destroying %s/%s, type=%d, path hash entries: %d!\n", pObj->pParent ? pObj->pParent->Obj.pszName : "",
3836                 pObj->pszName, pObj->bObjType, pObj->abUnused[0]);
3837         fflush(stderr);
3838         __debugbreak();
3839     }
3840 
3841     /*
3842      * Invalidate the structure.
3843      */
3844     pObj->u32Magic = ~KFSOBJ_MAGIC;
3845 
3846     /*
3847      * Destroy any user data first.
3848      */
3849     while (pObj->pUserDataHead != NULL)
3850     {
3851         PKFSUSERDATA pUserData = pObj->pUserDataHead;
3852         pObj->pUserDataHead = pUserData->pNext;
3853         if (pUserData->pfnDestructor)
3854             pUserData->pfnDestructor(pCache, pObj, pUserData);
3855         kHlpFree(pUserData);
3856     }
3857 
3858     /*
3859      * Do type specific destruction
3860      */
3861     switch (pObj->bObjType)
3862     {
3863         case KFSOBJ_TYPE_MISSING:
3864             /* nothing else to do here */
3865             pCache->cbObjects -= sizeof(KFSDIR);
3866             break;
3867 
3868         case KFSOBJ_TYPE_DIR:
3869         {
3870             PKFSDIR pDir = (PKFSDIR)pObj;
3871             KU32    cChildren = pDir->cChildren;
3872             pCache->cbObjects -= sizeof(*pDir)
3873                                + K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
3874                                + (pDir->fHashTabMask + !!pDir->fHashTabMask) * sizeof(pDir->papHashTab[0]);
3875 
3876             pDir->cChildren   = 0;
3877             while (cChildren-- > 0)
3878                 kFsCacheObjRelease(pCache, pDir->papChildren[cChildren]);
3879             kHlpFree(pDir->papChildren);
3880             pDir->papChildren = NULL;
3881 
3882             kHlpFree(pDir->papHashTab);
3883             pDir->papHashTab = NULL;
3884             break;
3885         }
3886 
3887         case KFSOBJ_TYPE_FILE:
3888         case KFSOBJ_TYPE_OTHER:
3889             pCache->cbObjects -= sizeof(*pObj);
3890             break;
3891 
3892         default:
3893             return 0;
3894     }
3895 
3896     /*
3897      * Common bits.
3898      */
3899     pCache->cbObjects -= pObj->cchName + 1;
3900 #ifdef KFSCACHE_CFG_UTF16
3901     pCache->cbObjects -= (pObj->cwcName + 1) * sizeof(wchar_t);
3902 #endif
3903 #ifdef KFSCACHE_CFG_SHORT_NAMES
3904     if (pObj->pszName != pObj->pszShortName)
3905     {
3906         pCache->cbObjects -= pObj->cchShortName + 1;
3907 # ifdef KFSCACHE_CFG_UTF16
3908         pCache->cbObjects -= (pObj->cwcShortName + 1) * sizeof(wchar_t);
3909 # endif
3910     }
3911 #endif
3912     pCache->cObjects--;
3913 
3914     if (pObj->pNameAlloc)
3915     {
3916         pCache->cbObjects -= pObj->pNameAlloc->cb;
3917         kHlpFree(pObj->pNameAlloc);
3918     }
3919 
3920     kHlpFree(pObj);
3921     return 0;
3922 }
3923 
3924 
3925 /**
3926  * Releases a reference to a cache object.
3927  *
3928  * @returns New reference count.
3929  * @param   pCache              The cache.
3930  * @param   pObj                The object.
3931  */
3932 #undef kFsCacheObjRelease
kFsCacheObjRelease(PKFSCACHE pCache,PKFSOBJ pObj)3933 KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
3934 {
3935     if (pObj)
3936     {
3937         KU32 cRefs;
3938         kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3939         kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3940 
3941         cRefs = --pObj->cRefs;
3942         if (cRefs)
3943             return cRefs;
3944         return kFsCacheObjDestroy(pCache, pObj, "kFsCacheObjRelease");
3945     }
3946     return 0;
3947 }
3948 
3949 
3950 /**
3951  * Debug version of kFsCacheObjRelease
3952  *
3953  * @returns New reference count.
3954  * @param   pCache              The cache.
3955  * @param   pObj                The object.
3956  * @param   pszWhere            Where it's invoked from.
3957  */
kFsCacheObjReleaseTagged(PKFSCACHE pCache,PKFSOBJ pObj,const char * pszWhere)3958 KU32 kFsCacheObjReleaseTagged(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere)
3959 {
3960     if (pObj)
3961     {
3962         KU32 cRefs;
3963         kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3964         kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3965 
3966         cRefs = --pObj->cRefs;
3967         if (cRefs)
3968             return cRefs;
3969         return kFsCacheObjDestroy(pCache, pObj, pszWhere);
3970     }
3971     return 0;
3972 }
3973 
3974 
3975 /**
3976  * Retains a reference to a cahce object.
3977  *
3978  * @returns New reference count.
3979  * @param   pObj                The object.
3980  */
kFsCacheObjRetain(PKFSOBJ pObj)3981 KU32 kFsCacheObjRetain(PKFSOBJ pObj)
3982 {
3983     KU32 cRefs;
3984     kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3985     kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3986 
3987     cRefs = ++pObj->cRefs;
3988     kHlpAssert(cRefs < 16384);
3989     return cRefs;
3990 }
3991 
3992 
3993 /**
3994  * Associates an item of user data with the given object.
3995  *
3996  * If the data needs cleaning up before being free, set the
3997  * PKFSUSERDATA::pfnDestructor member of the returned structure.
3998  *
3999  * @returns Pointer to the user data on success.
4000  *          NULL if out of memory or key already in use.
4001  *
4002  * @param   pCache              The cache.
4003  * @param   pObj                The object.
4004  * @param   uKey                The user data key.
4005  * @param   cbUserData          The size of the user data.
4006  */
kFsCacheObjAddUserData(PKFSCACHE pCache,PKFSOBJ pObj,KUPTR uKey,KSIZE cbUserData)4007 PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData)
4008 {
4009     kHlpAssert(cbUserData >= sizeof(*pNew));
4010     if (kFsCacheObjGetUserData(pCache, pObj, uKey) == NULL)
4011     {
4012         PKFSUSERDATA pNew = (PKFSUSERDATA)kHlpAllocZ(cbUserData);
4013         if (pNew)
4014         {
4015             pNew->uKey          = uKey;
4016             pNew->pfnDestructor = NULL;
4017             pNew->pNext         = pObj->pUserDataHead;
4018             pObj->pUserDataHead = pNew;
4019             return pNew;
4020         }
4021     }
4022 
4023     return NULL;
4024 }
4025 
4026 
4027 /**
4028  * Retrieves an item of user data associated with the given object.
4029  *
4030  * @returns Pointer to the associated user data if found, otherwise NULL.
4031  * @param   pCache              The cache.
4032  * @param   pObj                The object.
4033  * @param   uKey                The user data key.
4034  */
kFsCacheObjGetUserData(PKFSCACHE pCache,PKFSOBJ pObj,KUPTR uKey)4035 PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey)
4036 {
4037     PKFSUSERDATA pCur;
4038 
4039     kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
4040     kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4041 
4042     for (pCur = pObj->pUserDataHead; pCur; pCur = pCur->pNext)
4043         if (pCur->uKey == uKey)
4044             return pCur;
4045     return NULL;
4046 }
4047 
4048 
4049 /**
4050  * Gets the full path to @a pObj, ANSI version.
4051  *
4052  * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4053  * @param   pObj                The object to get the full path to.
4054  * @param   pszPath             Where to return the path
4055  * @param   cbPath              The size of the output buffer.
4056  * @param   chSlash             The slash to use.
4057  */
kFsCacheObjGetFullPathA(PKFSOBJ pObj,char * pszPath,KSIZE cbPath,char chSlash)4058 KBOOL kFsCacheObjGetFullPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
4059 {
4060     KSIZE off = pObj->cchParent;
4061     kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4062     if (off > 0)
4063     {
4064         KSIZE offEnd = off + pObj->cchName;
4065         if (offEnd < cbPath)
4066         {
4067             PKFSDIR pAncestor;
4068 
4069             pszPath[off + pObj->cchName] = '\0';
4070             memcpy(&pszPath[off], pObj->pszName, pObj->cchName);
4071 
4072             for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4073             {
4074                 kHlpAssert(off > 1);
4075                 kHlpAssert(pAncestor != NULL);
4076                 kHlpAssert(pAncestor->Obj.cchName > 0);
4077                 pszPath[--off] = chSlash;
4078                 off -= pAncestor->Obj.cchName;
4079                 kHlpAssert(pAncestor->Obj.cchParent == off);
4080                 memcpy(&pszPath[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4081             }
4082             return K_TRUE;
4083         }
4084     }
4085     else
4086     {
4087         KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
4088         off = pObj->cchName;
4089         if (off + fDriveLetter < cbPath)
4090         {
4091             memcpy(pszPath, pObj->pszName, off);
4092             if (fDriveLetter)
4093                 pszPath[off++] = chSlash;
4094             pszPath[off] = '\0';
4095             return K_TRUE;
4096         }
4097     }
4098 
4099     return K_FALSE;
4100 }
4101 
4102 
4103 /**
4104  * Gets the full path to @a pObj, UTF-16 version.
4105  *
4106  * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4107  * @param   pObj                The object to get the full path to.
4108  * @param   pszPath             Where to return the path
4109  * @param   cbPath              The size of the output buffer.
4110  * @param   wcSlash             The slash to use.
4111  */
kFsCacheObjGetFullPathW(PKFSOBJ pObj,wchar_t * pwszPath,KSIZE cwcPath,wchar_t wcSlash)4112 KBOOL kFsCacheObjGetFullPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
4113 {
4114     KSIZE off = pObj->cwcParent;
4115     kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4116     if (off > 0)
4117     {
4118         KSIZE offEnd = off + pObj->cwcName;
4119         if (offEnd < cwcPath)
4120         {
4121             PKFSDIR pAncestor;
4122 
4123             pwszPath[off + pObj->cwcName] = '\0';
4124             memcpy(&pwszPath[off], pObj->pwszName, pObj->cwcName * sizeof(wchar_t));
4125 
4126             for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4127             {
4128                 kHlpAssert(off > 1);
4129                 kHlpAssert(pAncestor != NULL);
4130                 kHlpAssert(pAncestor->Obj.cwcName > 0);
4131                 pwszPath[--off] = wcSlash;
4132                 off -= pAncestor->Obj.cwcName;
4133                 kHlpAssert(pAncestor->Obj.cwcParent == off);
4134                 memcpy(&pwszPath[off], pAncestor->Obj.pwszName, pAncestor->Obj.cwcName * sizeof(wchar_t));
4135             }
4136             return K_TRUE;
4137         }
4138     }
4139     else
4140     {
4141         KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
4142         off = pObj->cwcName;
4143         if (off + fDriveLetter < cwcPath)
4144         {
4145             memcpy(pwszPath, pObj->pwszName, off * sizeof(wchar_t));
4146             if (fDriveLetter)
4147                 pwszPath[off++] = wcSlash;
4148             pwszPath[off] = '\0';
4149             return K_TRUE;
4150         }
4151     }
4152 
4153     return K_FALSE;
4154 }
4155 
4156 
4157 #ifdef KFSCACHE_CFG_SHORT_NAMES
4158 
4159 /**
4160  * Gets the full short path to @a pObj, ANSI version.
4161  *
4162  * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4163  * @param   pObj                The object to get the full path to.
4164  * @param   pszPath             Where to return the path
4165  * @param   cbPath              The size of the output buffer.
4166  * @param   chSlash             The slash to use.
4167  */
kFsCacheObjGetFullShortPathA(PKFSOBJ pObj,char * pszPath,KSIZE cbPath,char chSlash)4168 KBOOL kFsCacheObjGetFullShortPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
4169 {
4170     KSIZE off = pObj->cchShortParent;
4171     kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4172     if (off > 0)
4173     {
4174         KSIZE offEnd = off + pObj->cchShortName;
4175         if (offEnd < cbPath)
4176         {
4177             PKFSDIR pAncestor;
4178 
4179             pszPath[off + pObj->cchShortName] = '\0';
4180             memcpy(&pszPath[off], pObj->pszShortName, pObj->cchShortName);
4181 
4182             for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4183             {
4184                 kHlpAssert(off > 1);
4185                 kHlpAssert(pAncestor != NULL);
4186                 kHlpAssert(pAncestor->Obj.cchShortName > 0);
4187                 pszPath[--off] = chSlash;
4188                 off -= pAncestor->Obj.cchShortName;
4189                 kHlpAssert(pAncestor->Obj.cchShortParent == off);
4190                 memcpy(&pszPath[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
4191             }
4192             return K_TRUE;
4193         }
4194     }
4195     else
4196     {
4197         KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
4198         off = pObj->cchShortName;
4199         if (off + fDriveLetter < cbPath)
4200         {
4201             memcpy(pszPath, pObj->pszShortName, off);
4202             if (fDriveLetter)
4203                 pszPath[off++] = chSlash;
4204             pszPath[off] = '\0';
4205             return K_TRUE;
4206         }
4207     }
4208 
4209     return K_FALSE;
4210 }
4211 
4212 
4213 /**
4214  * Gets the full short path to @a pObj, UTF-16 version.
4215  *
4216  * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4217  * @param   pObj                The object to get the full path to.
4218  * @param   pszPath             Where to return the path
4219  * @param   cbPath              The size of the output buffer.
4220  * @param   wcSlash             The slash to use.
4221  */
kFsCacheObjGetFullShortPathW(PKFSOBJ pObj,wchar_t * pwszPath,KSIZE cwcPath,wchar_t wcSlash)4222 KBOOL kFsCacheObjGetFullShortPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
4223 {
4224     KSIZE off = pObj->cwcShortParent;
4225     kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4226     if (off > 0)
4227     {
4228         KSIZE offEnd = off + pObj->cwcShortName;
4229         if (offEnd < cwcPath)
4230         {
4231             PKFSDIR pAncestor;
4232 
4233             pwszPath[off + pObj->cwcShortName] = '\0';
4234             memcpy(&pwszPath[off], pObj->pwszShortName, pObj->cwcShortName * sizeof(wchar_t));
4235 
4236             for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4237             {
4238                 kHlpAssert(off > 1);
4239                 kHlpAssert(pAncestor != NULL);
4240                 kHlpAssert(pAncestor->Obj.cwcShortName > 0);
4241                 pwszPath[--off] = wcSlash;
4242                 off -= pAncestor->Obj.cwcShortName;
4243                 kHlpAssert(pAncestor->Obj.cwcShortParent == off);
4244                 memcpy(&pwszPath[off], pAncestor->Obj.pwszShortName, pAncestor->Obj.cwcShortName * sizeof(wchar_t));
4245             }
4246             return K_TRUE;
4247         }
4248     }
4249     else
4250     {
4251         KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
4252         off = pObj->cwcShortName;
4253         if (off + fDriveLetter < cwcPath)
4254         {
4255             memcpy(pwszPath, pObj->pwszShortName, off * sizeof(wchar_t));
4256             if (fDriveLetter)
4257                 pwszPath[off++] = wcSlash;
4258             pwszPath[off] = '\0';
4259             return K_TRUE;
4260         }
4261     }
4262 
4263     return K_FALSE;
4264 }
4265 
4266 #endif /* KFSCACHE_CFG_SHORT_NAMES */
4267 
4268 
4269 
4270 /**
4271  * Read the specified bits from the files into the given buffer, simple version.
4272  *
4273  * @returns K_TRUE on success (all requested bytes read),
4274  *          K_FALSE on any kind of failure.
4275  *
4276  * @param   pCache              The cache.
4277  * @param   pFileObj            The file object.
4278  * @param   offStart            Where to start reading.
4279  * @param   pvBuf               Where to store what we read.
4280  * @param   cbToRead            How much to read (exact).
4281  */
kFsCacheFileSimpleOpenReadClose(PKFSCACHE pCache,PKFSOBJ pFileObj,KU64 offStart,void * pvBuf,KSIZE cbToRead)4282 KBOOL kFsCacheFileSimpleOpenReadClose(PKFSCACHE pCache, PKFSOBJ pFileObj, KU64 offStart, void *pvBuf, KSIZE cbToRead)
4283 {
4284     /*
4285      * Open the file relative to the parent directory.
4286      */
4287     MY_NTSTATUS             rcNt;
4288     HANDLE                  hFile;
4289     MY_IO_STATUS_BLOCK      Ios;
4290     MY_OBJECT_ATTRIBUTES    ObjAttr;
4291     MY_UNICODE_STRING       UniStr;
4292 
4293     kHlpAssertReturn(pFileObj->bObjType == KFSOBJ_TYPE_FILE, K_FALSE);
4294     kHlpAssert(pFileObj->pParent);
4295     kHlpAssertReturn(pFileObj->pParent->hDir != INVALID_HANDLE_VALUE, K_FALSE);
4296     kHlpAssertReturn(offStart == 0, K_FALSE); /** @todo when needed */
4297 
4298     Ios.Information = -1;
4299     Ios.u.Status    = -1;
4300 
4301     UniStr.Buffer        = (wchar_t *)pFileObj->pwszName;
4302     UniStr.Length        = (USHORT)(pFileObj->cwcName * sizeof(wchar_t));
4303     UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
4304 
4305     MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFileObj->pParent->hDir, NULL /*pSecAttr*/);
4306 
4307     rcNt = g_pfnNtCreateFile(&hFile,
4308                              GENERIC_READ | SYNCHRONIZE,
4309                              &ObjAttr,
4310                              &Ios,
4311                              NULL, /*cbFileInitialAlloc */
4312                              FILE_ATTRIBUTE_NORMAL,
4313                              FILE_SHARE_READ,
4314                              FILE_OPEN,
4315                              FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
4316                              NULL, /*pEaBuffer*/
4317                              0);   /*cbEaBuffer*/
4318     if (MY_NT_SUCCESS(rcNt))
4319     {
4320         LARGE_INTEGER offFile;
4321         offFile.QuadPart = offStart;
4322 
4323         Ios.Information = -1;
4324         Ios.u.Status    = -1;
4325         rcNt = g_pfnNtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApcComplete*/, NULL /*pvApcCtx*/, &Ios,
4326                                pvBuf, (KU32)cbToRead, !offStart ? &offFile : NULL, NULL /*puKey*/);
4327         if (MY_NT_SUCCESS(rcNt))
4328             rcNt = Ios.u.Status;
4329         if (MY_NT_SUCCESS(rcNt))
4330         {
4331             if (Ios.Information == cbToRead)
4332             {
4333                 g_pfnNtClose(hFile);
4334                 return K_TRUE;
4335             }
4336             KFSCACHE_LOG(("Error reading %#x bytes from '%ls': Information=%p\n", pFileObj->pwszName, Ios.Information));
4337         }
4338         else
4339             KFSCACHE_LOG(("Error reading %#x bytes from '%ls': %#x\n", pFileObj->pwszName, rcNt));
4340         g_pfnNtClose(hFile);
4341     }
4342     else
4343         KFSCACHE_LOG(("Error opening '%ls' for caching: %#x\n", pFileObj->pwszName, rcNt));
4344     return K_FALSE;
4345 }
4346 
4347 
4348 /**
4349  * Invalidate all cache entries of missing files.
4350  *
4351  * @param   pCache      The cache.
4352  */
kFsCacheInvalidateMissing(PKFSCACHE pCache)4353 void kFsCacheInvalidateMissing(PKFSCACHE pCache)
4354 {
4355     kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4356     pCache->auGenerationsMissing[0]++;
4357     kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
4358     KFSCACHE_LOG(("Invalidate missing %#x\n", pCache->auGenerationsMissing[0]));
4359 }
4360 
4361 
4362 /**
4363  * Invalidate all cache entries (regular, custom & missing).
4364  *
4365  * @param   pCache      The cache.
4366  */
kFsCacheInvalidateAll(PKFSCACHE pCache)4367 void kFsCacheInvalidateAll(PKFSCACHE pCache)
4368 {
4369     kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4370 
4371     pCache->auGenerationsMissing[0]++;
4372     kHlpAssert(pCache->auGenerationsMissing[0] < KU32_MAX);
4373     pCache->auGenerationsMissing[1]++;
4374     kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4375 
4376     pCache->auGenerations[0]++;
4377     kHlpAssert(pCache->auGenerations[0] < KU32_MAX);
4378     pCache->auGenerations[1]++;
4379     kHlpAssert(pCache->auGenerations[1] < KU32_MAX);
4380 
4381     KFSCACHE_LOG(("Invalidate all - default: %#x/%#x,  custom: %#x/%#x\n",
4382                   pCache->auGenerationsMissing[0], pCache->auGenerations[0],
4383                   pCache->auGenerationsMissing[1], pCache->auGenerations[1]));
4384 }
4385 
4386 
4387 /**
4388  * Invalidate all cache entries with custom generation handling set.
4389  *
4390  * @see     kFsCacheSetupCustomRevisionForTree, KFSOBJ_F_USE_CUSTOM_GEN
4391  * @param   pCache      The cache.
4392  */
kFsCacheInvalidateCustomMissing(PKFSCACHE pCache)4393 void kFsCacheInvalidateCustomMissing(PKFSCACHE pCache)
4394 {
4395     kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4396     pCache->auGenerationsMissing[1]++;
4397     kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4398     KFSCACHE_LOG(("Invalidate missing custom %#x\n", pCache->auGenerationsMissing[1]));
4399 }
4400 
4401 
4402 /**
4403  * Invalidate all cache entries with custom generation handling set, both
4404  * missing and regular present entries.
4405  *
4406  * @see     kFsCacheSetupCustomRevisionForTree, KFSOBJ_F_USE_CUSTOM_GEN
4407  * @param   pCache      The cache.
4408  */
kFsCacheInvalidateCustomBoth(PKFSCACHE pCache)4409 void kFsCacheInvalidateCustomBoth(PKFSCACHE pCache)
4410 {
4411     kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4412     pCache->auGenerations[1]++;
4413     kHlpAssert(pCache->auGenerations[1] < KU32_MAX);
4414     pCache->auGenerationsMissing[1]++;
4415     kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4416     KFSCACHE_LOG(("Invalidate both custom %#x/%#x\n", pCache->auGenerationsMissing[1], pCache->auGenerations[1]));
4417 }
4418 
4419 
4420 
4421 /**
4422  * Applies the given flags to all the objects in a tree.
4423  *
4424  * @param   pRoot               Where to start applying the flag changes.
4425  * @param   fAndMask            The AND mask.
4426  * @param   fOrMask             The OR mask.
4427  */
kFsCacheApplyFlagsToTree(PKFSDIR pRoot,KU32 fAndMask,KU32 fOrMask)4428 static void kFsCacheApplyFlagsToTree(PKFSDIR pRoot, KU32 fAndMask, KU32 fOrMask)
4429 {
4430     PKFSOBJ    *ppCur = ((PKFSDIR)pRoot)->papChildren;
4431     KU32        cLeft = ((PKFSDIR)pRoot)->cChildren;
4432     while (cLeft-- > 0)
4433     {
4434         PKFSOBJ pCur = *ppCur++;
4435         if (pCur->bObjType != KFSOBJ_TYPE_DIR)
4436             pCur->fFlags = (fAndMask & pCur->fFlags) | fOrMask;
4437         else
4438             kFsCacheApplyFlagsToTree((PKFSDIR)pCur, fAndMask, fOrMask);
4439     }
4440 
4441     pRoot->Obj.fFlags = (fAndMask & pRoot->Obj.fFlags) | fOrMask;
4442 }
4443 
4444 
4445 /**
4446  * Sets up using custom revisioning for the specified directory tree or file.
4447  *
4448  * There are some restrictions of the current implementation:
4449  *      - If the root of the sub-tree is ever deleted from the cache (i.e.
4450  *        deleted in real life and reflected in the cache), the setting is lost.
4451  *      - It is not automatically applied to the lookup paths caches.
4452  *
4453  * @returns K_TRUE on success, K_FALSE on failure.
4454  * @param   pCache              The cache.
4455  * @param   pRoot               The root of the subtree.  A non-directory is
4456  *                              fine, like a missing node.
4457  */
kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache,PKFSOBJ pRoot)4458 KBOOL kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot)
4459 {
4460     if (pRoot)
4461     {
4462         if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
4463             kFsCacheApplyFlagsToTree((PKFSDIR)pRoot, KU32_MAX, KFSOBJ_F_USE_CUSTOM_GEN);
4464         else
4465             pRoot->fFlags |= KFSOBJ_F_USE_CUSTOM_GEN;
4466         return K_TRUE;
4467     }
4468     return K_FALSE;
4469 }
4470 
4471 
4472 /**
4473  * Invalidates a deleted directory, ANSI version.
4474  *
4475  * @returns K_TRUE if found and is a non-root directory. Otherwise K_FALSE.
4476  * @param   pCache              The cache.
4477  * @param   pszDir              The directory.
4478  */
kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache,const char * pszDir)4479 KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir)
4480 {
4481     KU32            cchDir = (KU32)kHlpStrLen(pszDir);
4482     KFSLOOKUPERROR  enmError;
4483     PKFSOBJ         pFsObj;
4484 
4485     /* Is absolute without any '..' bits? */
4486     if (   cchDir >= 3
4487         && (   (   pszDir[1] == ':'    /* Drive letter */
4488                 && IS_SLASH(pszDir[2])
4489                 && IS_ALPHA(pszDir[0]) )
4490             || (   IS_SLASH(pszDir[0]) /* UNC */
4491                 && IS_SLASH(pszDir[1]) ) )
4492         && !kFsCacheHasDotDotA(pszDir, cchDir) )
4493         pFsObj = kFsCacheLookupAbsoluteA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
4494                                          &enmError, NULL);
4495     else
4496         pFsObj = kFsCacheLookupSlowA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
4497                                      &enmError, NULL);
4498     if (pFsObj)
4499     {
4500         /* Is directory? */
4501         if (pFsObj->bObjType == KFSOBJ_TYPE_DIR)
4502         {
4503             if (pFsObj->pParent != &pCache->RootDir)
4504             {
4505                 PKFSDIR pDir = (PKFSDIR)pFsObj;
4506                 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: %s hDir=%p\n", pszDir, pDir->hDir));
4507                 if (pDir->hDir != INVALID_HANDLE_VALUE)
4508                 {
4509                     g_pfnNtClose(pDir->hDir);
4510                     pDir->hDir = INVALID_HANDLE_VALUE;
4511                 }
4512                 pDir->fNeedRePopulating = K_TRUE;
4513                 pDir->Obj.uCacheGen = pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN] - 1;
4514                 kFsCacheObjRetainInternal(&pDir->Obj);
4515                 return K_TRUE;
4516             }
4517             KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a root directory was deleted! %s\n", pszDir));
4518         }
4519         else
4520             KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a non-directory: bObjType=%d %s\n",
4521                           pFsObj->bObjType, pszDir));
4522         kFsCacheObjRetainInternal(pFsObj);
4523     }
4524     else
4525         KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: '%s' was not found\n", pszDir));
4526     return K_FALSE;
4527 }
4528 
4529 
kFsCacheCreate(KU32 fFlags)4530 PKFSCACHE kFsCacheCreate(KU32 fFlags)
4531 {
4532     PKFSCACHE pCache;
4533     birdResolveImports();
4534 
4535     pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
4536     if (pCache)
4537     {
4538         /* Dummy root dir entry. */
4539         pCache->RootDir.Obj.u32Magic        = KFSOBJ_MAGIC;
4540         pCache->RootDir.Obj.cRefs           = 1;
4541         pCache->RootDir.Obj.uCacheGen       = KFSOBJ_CACHE_GEN_IGNORE;
4542         pCache->RootDir.Obj.bObjType        = KFSOBJ_TYPE_DIR;
4543         pCache->RootDir.Obj.fHaveStats      = K_FALSE;
4544         pCache->RootDir.Obj.pParent         = NULL;
4545         pCache->RootDir.Obj.pszName         = "";
4546         pCache->RootDir.Obj.cchName         = 0;
4547         pCache->RootDir.Obj.cchParent       = 0;
4548 #ifdef KFSCACHE_CFG_UTF16
4549         pCache->RootDir.Obj.cwcName         = 0;
4550         pCache->RootDir.Obj.cwcParent       = 0;
4551         pCache->RootDir.Obj.pwszName        = L"";
4552 #endif
4553 
4554 #ifdef KFSCACHE_CFG_SHORT_NAMES
4555         pCache->RootDir.Obj.pszShortName    = NULL;
4556         pCache->RootDir.Obj.cchShortName    = 0;
4557         pCache->RootDir.Obj.cchShortParent  = 0;
4558 # ifdef KFSCACHE_CFG_UTF16
4559         pCache->RootDir.Obj.cwcShortName;
4560         pCache->RootDir.Obj.cwcShortParent;
4561         pCache->RootDir.Obj.pwszShortName;
4562 # endif
4563 #endif
4564         pCache->RootDir.cChildren           = 0;
4565         pCache->RootDir.cChildrenAllocated  = 0;
4566         pCache->RootDir.papChildren         = NULL;
4567         pCache->RootDir.hDir                = INVALID_HANDLE_VALUE;
4568         pCache->RootDir.fHashTabMask        = 255; /* 256: 26 drive letters and 102 UNCs before we're half ways. */
4569         pCache->RootDir.papHashTab          = (PKFSOBJ *)kHlpAllocZ(256 * sizeof(pCache->RootDir.papHashTab[0]));
4570         if (pCache->RootDir.papHashTab)
4571         {
4572             /* The cache itself. */
4573             pCache->u32Magic                = KFSCACHE_MAGIC;
4574             pCache->fFlags                  = fFlags;
4575             pCache->auGenerations[0]        = KU32_MAX / 4;
4576             pCache->auGenerations[1]        = KU32_MAX / 32;
4577             pCache->auGenerationsMissing[0] = KU32_MAX / 256;
4578             pCache->auGenerationsMissing[1] = 1;
4579             pCache->cObjects                = 1;
4580             pCache->cbObjects               = sizeof(pCache->RootDir)
4581                                             + (pCache->RootDir.fHashTabMask + 1) * sizeof(pCache->RootDir.papHashTab[0]);
4582             pCache->cPathHashHits           = 0;
4583             pCache->cWalkHits               = 0;
4584             pCache->cChildSearches          = 0;
4585             pCache->cChildHashHits          = 0;
4586             pCache->cChildHashed            = 0;
4587             pCache->cChildHashTabs          = 1;
4588             pCache->cChildHashEntriesTotal  = pCache->RootDir.fHashTabMask + 1;
4589             pCache->cChildHashCollisions    = 0;
4590             pCache->cNameChanges            = 0;
4591             pCache->cNameGrowths            = 0;
4592             pCache->cAnsiPaths              = 0;
4593             pCache->cAnsiPathCollisions     = 0;
4594             pCache->cbAnsiPaths             = 0;
4595 #ifdef KFSCACHE_CFG_UTF16
4596             pCache->cUtf16Paths             = 0;
4597             pCache->cUtf16PathCollisions    = 0;
4598             pCache->cbUtf16Paths            = 0;
4599 #endif
4600             return pCache;
4601         }
4602 
4603         kHlpFree(pCache);
4604     }
4605     return NULL;
4606 }
4607 
4608