VirtualBox

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

Last change on this file since 3359 was 3359, checked in by bird, 5 years ago

kmk,kFsCache: Added variant of kFsCacheInvalidateAll that also closes directory handles, we need to do this befor ere-executing kmk after having remake some include file we needed. It messes up fetching otherwise.

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