VirtualBox

source: kBuild/trunk/src/lib/nt/kFsCache.c@ 3094

Last change on this file since 3094 was 3082, checked in by bird, 8 years ago

nt/kFsCache: Fixed buggy negative lookup caching.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 168.5 KB
Line 
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 <[email protected]>
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 */
74typedef 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. */
88typedef KFSDIRREPOP *PKFSDIRREPOP;
89
90
91
92/*********************************************************************************************************************************
93* Internal Functions *
94*********************************************************************************************************************************/
95static 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 */
104K_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 */
120void 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 */
139void 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 */
160static 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 */
187static 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 */
209static 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 */
228static 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 */
250static 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 */
270KBOOL 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 */
290K_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 */
328static 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 */
366static 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 */
412static 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 */
469static 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 */
518static 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 */
555PKFSOBJ 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 */
693PKFSOBJ 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 */
741static 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 */
788static 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 */
825static 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 */
940static 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 */
963static 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 */
1073K_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 */
1114static 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 */
1227K_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 */
1308static 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 */
1728KBOOL 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 */
1746static 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
1780static 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
1840static 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 */
1864static 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 */
2096static 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 */
2316static 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 */
2391static 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 */
2498static 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 */
2610static 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 */
2679static 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 */
2753PKFSOBJ 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 */
2907PKFSOBJ 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 */
3060static 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 */
3158static 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 */
3257static 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 */
3297static 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 */
3328static 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 */
3389static 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 */
3463static 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 */
3571static 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 */
3677PKFSOBJ 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 */
3703PKFSOBJ 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 */
3731PKFSOBJ 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 */
3757PKFSOBJ 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 */
3775PKFSOBJ 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 */
3802PKFSOBJ 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 */
3825KU32 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
3933KU32 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 */
3958KU32 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 */
3981KU32 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 */
4007PKFSUSERDATA 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 */
4035PKFSUSERDATA 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 */
4058KBOOL 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 */
4112KBOOL 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 */
4168KBOOL 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 */
4222KBOOL 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 */
4282KBOOL 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 */
4353void 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 */
4367void 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 */
4393void 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 */
4409void 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 */
4428static 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 */
4458KBOOL 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 */
4479KBOOL 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
4530PKFSCACHE 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
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette