VirtualBox

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

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

kFsCache: Fixed bug in name updating of missing entires. Fixed copy&paste error in lookup wrt flag handling (harmless). Update uCacheGen on old entries during directory repopulation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 175.4 KB
Line 
1/* $Id: kFsCache.c 3379 2020-06-12 00:54:55Z 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->cPathHashRefs += 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->cPathHashRefs += 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->cPathHashRefs = 0;
596 pObj->idxUserDataLock = KU8_MAX;
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, cchName);
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 if (pCur->uCacheGen != KFSOBJ_CACHE_GEN_IGNORE)
1538 pCur->uCacheGen = pCache->auGenerations[pCur->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1539 }
1540 else if (pCur->bObjType == KFSOBJ_TYPE_MISSING)
1541 {
1542 KFSCACHE_LOG(("Refreshing %s/%s/ - %s appeared as %u, was missing.\n",
1543 pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pCur->pszName, bObjType));
1544 pCur->bObjType = bObjType;
1545 if (pCur->uCacheGen != KFSOBJ_CACHE_GEN_IGNORE)
1546 pCur->uCacheGen = pCache->auGenerations[pCur->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1547 }
1548 else
1549 {
1550 KFSCACHE_LOG(("Refreshing %s/%s/ - %s changed type from %u to %u! Dropping old object.\n",
1551 pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pCur->pszName,
1552 pCur->bObjType, bObjType));
1553 kFsCacheObjRelease(pCache, pCur);
1554 pCur = NULL;
1555 }
1556 }
1557 else
1558 KFSCACHE_LOG(("Refreshing %s/%s/ - %*.*ls added.\n", pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName,
1559 uPtr.pNoId->FileNameLength / sizeof(wchar_t), uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1560 pwchFilename));
1561 }
1562
1563 if (!pCur)
1564 {
1565 /*
1566 * Create the entry (not linked yet).
1567 */
1568 pCur = kFsCacheCreateObjectW(pCache, pDir, pwchFilename, uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1569#ifdef KFSCACHE_CFG_SHORT_NAMES
1570 uPtr.pNoId->ShortName, uPtr.pNoId->ShortNameLength / sizeof(wchar_t),
1571#endif
1572 bObjType, penmError);
1573 if (!pCur)
1574 return K_FALSE;
1575 kHlpAssert(pCur->cRefs == 1);
1576 }
1577
1578#ifdef KFSCACHE_CFG_SHORT_NAMES
1579 if (enmInfoClass == enmInfoClassWithId)
1580 birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId);
1581 else
1582 birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId);
1583#else
1584 if (enmInfoClass == enmInfoClassWithId)
1585 birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId);
1586 else
1587 birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId);
1588#endif
1589 pCur->Stats.st_dev = pDir->uDevNo;
1590 pCur->fHaveStats = K_TRUE;
1591
1592 /*
1593 * Add the entry to the directory.
1594 */
1595 fRc = kFsCacheDirAddChild(pCache, pDir, pCur, penmError);
1596 kFsCacheObjRelease(pCache, pCur);
1597 if (fRc)
1598 { /* likely */ }
1599 else
1600 {
1601 rcNt = STATUS_NO_MEMORY;
1602 break;
1603 }
1604 }
1605 /*
1606 * When seeing '.' we update the directory info.
1607 */
1608 else if (uPtr.pNoId->FileNameLength == 2)
1609 {
1610 pDir->iLastWrite = uPtr.pNoId->LastWriteTime.QuadPart;
1611#ifdef KFSCACHE_CFG_SHORT_NAMES
1612 if (enmInfoClass == enmInfoClassWithId)
1613 birdStatFillFromFileIdBothDirInfo(&pDir->Obj.Stats, uPtr.pWithId);
1614 else
1615 birdStatFillFromFileBothDirInfo(&pDir->Obj.Stats, uPtr.pNoId);
1616#else
1617 if (enmInfoClass == enmInfoClassWithId)
1618 birdStatFillFromFileIdFullDirInfo(&pDir->Obj.Stats, uPtr.pWithId);
1619 else
1620 birdStatFillFromFileFullDirInfo(&pDir->Obj.Stats, uPtr.pNoId);
1621#endif
1622 }
1623
1624 /*
1625 * Advance.
1626 */
1627 offNext = uPtr.pNoId->NextEntryOffset;
1628 if ( offNext >= cbMinCur
1629 && offNext < sizeof(uBuf))
1630 offBuf += offNext;
1631 else
1632 break;
1633 }
1634
1635 /*
1636 * Read the next chunk.
1637 */
1638 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1639 NULL, /* hEvent */
1640 NULL, /* pfnApcComplete */
1641 NULL, /* pvApcCompleteCtx */
1642 &Ios,
1643 &uBuf,
1644 sizeof(uBuf),
1645 enmInfoClass,
1646 FALSE, /* fReturnSingleEntry */
1647 &UniStrStar, /* Filter / restart pos. */
1648 FALSE); /* fRestartScan */
1649 }
1650
1651 if (rcNt == MY_STATUS_NO_MORE_FILES)
1652 {
1653 /*
1654 * If refreshing, add missing children objects and ditch the rest.
1655 * We ignore errors while adding missing children (lazy bird).
1656 */
1657 if (!fRefreshing)
1658 { /* more likely */ }
1659 else
1660 {
1661 while (DirRePop.cOldChildren > 0)
1662 {
1663 KFSLOOKUPERROR enmErrorIgn;
1664 PKFSOBJ pOldChild = DirRePop.papOldChildren[--DirRePop.cOldChildren];
1665 if (pOldChild->bObjType == KFSOBJ_TYPE_MISSING)
1666 kFsCacheDirAddChild(pCache, pDir, pOldChild, &enmErrorIgn);
1667 else
1668 {
1669 KFSCACHE_LOG(("Refreshing %s/%s/ - %s was removed.\n",
1670 pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pOldChild->pszName));
1671 kHlpAssert(pOldChild->bObjType != KFSOBJ_TYPE_DIR);
1672 /* Remove from hash table. */
1673 if (pOldChild->uNameHash != 0)
1674 {
1675 KU32 idx = pOldChild->uNameHash & pDir->fHashTabMask;
1676 PKFSOBJ pPrev = pDir->papHashTab[idx];
1677 if (pPrev == pOldChild)
1678 pDir->papHashTab[idx] = pOldChild->pNextNameHash;
1679 else
1680 {
1681 while (pPrev && pPrev->pNextNameHash != pOldChild)
1682 pPrev = pPrev->pNextNameHash;
1683 kHlpAssert(pPrev);
1684 if (pPrev)
1685 pPrev->pNextNameHash = pOldChild->pNextNameHash;
1686 }
1687 pOldChild->uNameHash = 0;
1688 }
1689 }
1690 kFsCacheObjRelease(pCache, pOldChild);
1691 }
1692 kHlpFree(DirRePop.papOldChildren);
1693 }
1694
1695 /*
1696 * Mark the directory as fully populated and up to date.
1697 */
1698 pDir->fPopulated = K_TRUE;
1699 pDir->fNeedRePopulating = K_FALSE;
1700 if (pDir->Obj.uCacheGen != KFSOBJ_CACHE_GEN_IGNORE)
1701 pDir->Obj.uCacheGen = pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1702 return K_TRUE;
1703 }
1704
1705 /*
1706 * If we failed during refresh, add back remaining old children.
1707 */
1708 if (!fRefreshing)
1709 {
1710 while (DirRePop.cOldChildren > 0)
1711 {
1712 KFSLOOKUPERROR enmErrorIgn;
1713 PKFSOBJ pOldChild = DirRePop.papOldChildren[--DirRePop.cOldChildren];
1714 kFsCacheDirAddChild(pCache, pDir, pOldChild, &enmErrorIgn);
1715 kFsCacheObjRelease(pCache, pOldChild);
1716 }
1717 kHlpFree(DirRePop.papOldChildren);
1718 }
1719
1720 kHlpAssertMsgFailed(("%#x\n", rcNt));
1721 *penmError = KFSLOOKUPERROR_DIR_READ_ERROR;
1722 return K_TRUE;
1723}
1724
1725
1726/**
1727 * Does the initial directory populating or refreshes it if it has been
1728 * invalidated.
1729 *
1730 * This assumes the parent directory is opened.
1731 *
1732 * @returns K_TRUE on success, K_FALSE on error.
1733 * @param pCache The cache.
1734 * @param pDir The directory.
1735 * @param penmError Where to store K_FALSE explanation. Optional.
1736 */
1737KBOOL kFsCacheDirEnsurePopuplated(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
1738{
1739 KFSLOOKUPERROR enmIgnored;
1740 KBOOL fRet;
1741 KFSCACHE_LOCK(pCache);
1742 if ( pDir->fPopulated
1743 && !pDir->fNeedRePopulating
1744 && ( pDir->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1745 || pDir->Obj.uCacheGen == pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
1746 fRet = K_TRUE;
1747 else
1748 fRet = kFsCachePopuplateOrRefreshDir(pCache, pDir, penmError ? penmError : &enmIgnored);
1749 KFSCACHE_UNLOCK(pCache);
1750 return fRet;
1751}
1752
1753
1754/**
1755 * Checks whether the modified timestamp differs on this directory.
1756 *
1757 * @returns K_TRUE if possibly modified, K_FALSE if definitely not modified.
1758 * @param pDir The directory..
1759 */
1760static KBOOL kFsCacheDirIsModified(PKFSDIR pDir)
1761{
1762 if ( pDir->hDir != INVALID_HANDLE_VALUE
1763 && (pDir->Obj.fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
1764 {
1765 if (!pDir->fNeedRePopulating)
1766 {
1767 MY_IO_STATUS_BLOCK Ios;
1768 MY_FILE_BASIC_INFORMATION BasicInfo;
1769 MY_NTSTATUS rcNt;
1770
1771 Ios.Information = -1;
1772 Ios.u.Status = -1;
1773
1774 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
1775 if (MY_NT_SUCCESS(rcNt))
1776 {
1777 if (BasicInfo.LastWriteTime.QuadPart != pDir->iLastWrite)
1778 {
1779 pDir->fNeedRePopulating = K_TRUE;
1780 return K_TRUE;
1781 }
1782 return K_FALSE;
1783 }
1784 }
1785 }
1786 /* The cache root never changes. */
1787 else if (!pDir->Obj.pParent)
1788 return K_FALSE;
1789
1790 return K_TRUE;
1791}
1792
1793
1794static KBOOL kFsCacheRefreshMissing(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1795{
1796 /*
1797 * If we can, we start by checking whether the parent directory
1798 * has been modified. If it has, we need to check if this entry
1799 * was added or not, most likely it wasn't added.
1800 */
1801 if (!kFsCacheDirIsModified(pMissing->pParent))
1802 {
1803 KFSCACHE_LOG(("Parent of missing not written to %s/%s\n", pMissing->pParent->Obj.pszName, pMissing->pszName));
1804 pMissing->uCacheGen = pCache->auGenerationsMissing[pMissing->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1805 }
1806 else
1807 {
1808 MY_UNICODE_STRING UniStr;
1809 MY_OBJECT_ATTRIBUTES ObjAttr;
1810 MY_FILE_BASIC_INFORMATION BasicInfo;
1811 MY_NTSTATUS rcNt;
1812
1813 UniStr.Buffer = (wchar_t *)pMissing->pwszName;
1814 UniStr.Length = (USHORT)(pMissing->cwcName * sizeof(wchar_t));
1815 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1816
1817 kHlpAssert(pMissing->pParent->hDir != INVALID_HANDLE_VALUE);
1818 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pMissing->pParent->hDir, NULL /*pSecAttr*/);
1819
1820 rcNt = g_pfnNtQueryAttributesFile(&ObjAttr, &BasicInfo);
1821 if (!MY_NT_SUCCESS(rcNt))
1822 {
1823 /*
1824 * Probably more likely that a missing node stays missing.
1825 */
1826 pMissing->uCacheGen = pCache->auGenerationsMissing[pMissing->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1827 KFSCACHE_LOG(("Still missing %s/%s\n", pMissing->pParent->Obj.pszName, pMissing->pszName));
1828 }
1829 else
1830 {
1831 /*
1832 * We must metamorphose this node. This is tedious business
1833 * because we need to check the file name casing. We might
1834 * just as well update the parent directory...
1835 */
1836 KU8 const bObjType = BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR
1837 : BasicInfo.FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)
1838 ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE;
1839
1840 KFSCACHE_LOG(("Birth of %s/%s as %d with attribs %#x...\n",
1841 pMissing->pParent->Obj.pszName, pMissing->pszName, bObjType, BasicInfo.FileAttributes));
1842 pMissing->bObjType = bObjType;
1843 /* (auGenerations[] - 1): make sure it's not considered up to date */
1844 pMissing->uCacheGen = pCache->auGenerations[pMissing->fFlags & KFSOBJ_F_USE_CUSTOM_GEN] - 1;
1845 /* Trigger parent directory repopulation. */
1846 if (pMissing->pParent->fPopulated)
1847 pMissing->pParent->fNeedRePopulating = K_TRUE;
1848/**
1849 * @todo refresh missing object names when it appears.
1850 */
1851 }
1852 }
1853
1854 return K_TRUE;
1855}
1856
1857
1858static KBOOL kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1859{
1860 if (kFsCacheRefreshMissing(pCache, pMissing, penmError))
1861 {
1862 if ( pMissing->bObjType == KFSOBJ_TYPE_DIR
1863 || pMissing->bObjType == KFSOBJ_TYPE_MISSING)
1864 return K_TRUE;
1865 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1866 }
1867
1868 return K_FALSE;
1869}
1870
1871
1872/**
1873 * Generic object refresh.
1874 *
1875 * This does not refresh the content of directories.
1876 *
1877 * @returns K_TRUE on success. K_FALSE and *penmError on failure.
1878 * @param pCache The cache.
1879 * @param pObj The object.
1880 * @param penmError Where to return error info.
1881 */
1882static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError)
1883{
1884 KBOOL fRc;
1885
1886 /*
1887 * Since we generally assume nothing goes away in this cache, we only really
1888 * have a hard time with negative entries. So, missing stuff goes to
1889 * complicated land.
1890 */
1891 if (pObj->bObjType == KFSOBJ_TYPE_MISSING)
1892 fRc = kFsCacheRefreshMissing(pCache, pObj, penmError);
1893 else
1894 {
1895 /*
1896 * This object is supposed to exist, so all we need to do is query essential
1897 * stats again. Since we've already got handles on directories, there are
1898 * two ways to go about this.
1899 */
1900 union
1901 {
1902 MY_FILE_NETWORK_OPEN_INFORMATION FullInfo;
1903 MY_FILE_STANDARD_INFORMATION StdInfo;
1904#ifdef KFSCACHE_CFG_SHORT_NAMES
1905 MY_FILE_ID_BOTH_DIR_INFORMATION WithId;
1906 //MY_FILE_BOTH_DIR_INFORMATION NoId;
1907#else
1908 MY_FILE_ID_FULL_DIR_INFORMATION WithId;
1909 //MY_FILE_FULL_DIR_INFORMATION NoId;
1910#endif
1911 KU8 abPadding[ sizeof(wchar_t) * KFSCACHE_CFG_MAX_UTF16_NAME
1912 + sizeof(MY_FILE_ID_BOTH_DIR_INFORMATION)];
1913 } uBuf;
1914 MY_IO_STATUS_BLOCK Ios;
1915 MY_NTSTATUS rcNt;
1916 if ( pObj->bObjType != KFSOBJ_TYPE_DIR
1917 || ((PKFSDIR)pObj)->hDir == INVALID_HANDLE_VALUE)
1918 {
1919#if 1
1920 /* This always works and doesn't mess up NtQueryDirectoryFile. */
1921 MY_UNICODE_STRING UniStr;
1922 MY_OBJECT_ATTRIBUTES ObjAttr;
1923
1924 UniStr.Buffer = (wchar_t *)pObj->pwszName;
1925 UniStr.Length = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1926 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1927
1928 kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1929 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pObj->pParent->hDir, NULL /*pSecAttr*/);
1930
1931 rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.FullInfo);
1932 if (MY_NT_SUCCESS(rcNt))
1933 {
1934 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
1935 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1936 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1937 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1938 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1939 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
1940 pObj->Stats.st_blksize = 65536;
1941 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1942 / BIRD_STAT_BLOCK_SIZE;
1943 }
1944#else
1945 /* This alternative lets us keep the inode number up to date and
1946 detect name case changes.
1947 Update: This doesn't work on windows 7, it ignores the UniStr
1948 and continue with the "*" search. So, we're using the
1949 above query instead for the time being. */
1950 MY_UNICODE_STRING UniStr;
1951# ifdef KFSCACHE_CFG_SHORT_NAMES
1952 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
1953# else
1954 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
1955# endif
1956
1957 UniStr.Buffer = (wchar_t *)pObj->pwszName;
1958 UniStr.Length = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1959 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1960
1961 kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1962
1963 Ios.Information = -1;
1964 Ios.u.Status = -1;
1965 rcNt = g_pfnNtQueryDirectoryFile(pObj->pParent->hDir,
1966 NULL, /* hEvent */
1967 NULL, /* pfnApcComplete */
1968 NULL, /* pvApcCompleteCtx */
1969 &Ios,
1970 &uBuf,
1971 sizeof(uBuf),
1972 enmInfoClass,
1973 TRUE, /* fReturnSingleEntry */
1974 &UniStr, /* Filter / restart pos. */
1975 TRUE); /* fRestartScan */
1976
1977 if (MY_NT_SUCCESS(rcNt))
1978 {
1979 if (pObj->Stats.st_ino == uBuf.WithId.FileId.QuadPart)
1980 KFSCACHE_LOG(("Refreshing %s/%s, no ID change...\n", pObj->pParent->Obj.pszName, pObj->pszName));
1981 else if ( pObj->cwcName == uBuf.WithId.FileNameLength / sizeof(wchar_t)
1982# ifdef KFSCACHE_CFG_SHORT_NAMES
1983 && ( uBuf.WithId.ShortNameLength == 0
1984 ? pObj->pwszName == pObj->pwszShortName
1985 || ( pObj->cwcName == pObj->cwcShortName
1986 && memcmp(pObj->pwszName, pObj->pwszShortName, pObj->cwcName * sizeof(wchar_t)) == 0)
1987 : pObj->cwcShortName == uBuf.WithId.ShortNameLength / sizeof(wchar_t)
1988 && memcmp(pObj->pwszShortName, uBuf.WithId.ShortName, uBuf.WithId.ShortNameLength) == 0
1989 )
1990# endif
1991 && memcmp(pObj->pwszName, uBuf.WithId.FileName, uBuf.WithId.FileNameLength) == 0
1992 )
1993 {
1994 KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx...\n",
1995 pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1996 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1997 }
1998 else
1999 {
2000 KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx and names too...\n",
2001 pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
2002 fprintf(stderr, "kFsCacheRefreshObj - ID + name change not implemented!!\n");
2003 fflush(stderr);
2004 __debugbreak();
2005 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
2006 /** @todo implement as needed. */
2007 }
2008
2009 pObj->Stats.st_size = uBuf.WithId.EndOfFile.QuadPart;
2010 birdNtTimeToTimeSpec(uBuf.WithId.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
2011 birdNtTimeToTimeSpec(uBuf.WithId.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
2012 birdNtTimeToTimeSpec(uBuf.WithId.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
2013 birdNtTimeToTimeSpec(uBuf.WithId.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
2014 pObj->Stats.st_attribs = uBuf.WithId.FileAttributes;
2015 pObj->Stats.st_blksize = 65536;
2016 pObj->Stats.st_blocks = (uBuf.WithId.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
2017 / BIRD_STAT_BLOCK_SIZE;
2018 }
2019#endif
2020 if (MY_NT_SUCCESS(rcNt))
2021 {
2022 pObj->uCacheGen = pCache->auGenerations[pObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
2023 fRc = K_TRUE;
2024 }
2025 else
2026 {
2027 /* ouch! */
2028 kHlpAssertMsgFailed(("%#x\n", rcNt));
2029 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on non-dir - not implemented!\n", rcNt);
2030 __debugbreak();
2031 fRc = K_FALSE;
2032 }
2033 }
2034 else
2035 {
2036 /*
2037 * An open directory. Query information via the handle, the
2038 * file ID shouldn't have been able to change, so we can use
2039 * NtQueryInformationFile. Right...
2040 */
2041 PKFSDIR pDir = (PKFSDIR)pObj;
2042 Ios.Information = -1;
2043 Ios.u.Status = -1;
2044 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &uBuf.FullInfo, sizeof(uBuf.FullInfo),
2045 MyFileNetworkOpenInformation);
2046 if (MY_NT_SUCCESS(rcNt))
2047 rcNt = Ios.u.Status;
2048 if (MY_NT_SUCCESS(rcNt))
2049 {
2050 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
2051 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
2052 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
2053 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
2054 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
2055 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
2056 pObj->Stats.st_blksize = 65536;
2057 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
2058 / BIRD_STAT_BLOCK_SIZE;
2059
2060 if ( pDir->iLastWrite == uBuf.FullInfo.LastWriteTime.QuadPart
2061 && (pObj->fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
2062 KFSCACHE_LOG(("Refreshing %s/%s/ - no re-populating necessary.\n",
2063 pObj->pParent->Obj.pszName, pObj->pszName));
2064 else
2065 {
2066 KFSCACHE_LOG(("Refreshing %s/%s/ - needs re-populating...\n",
2067 pObj->pParent->Obj.pszName, pObj->pszName));
2068 pDir->fNeedRePopulating = K_TRUE;
2069#if 0
2070 /* Refresh the link count. */
2071 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
2072 if (MY_NT_SUCCESS(rcNt))
2073 rcNt = Ios.s.Status;
2074 if (MY_NT_SUCCESS(rcNt))
2075 pObj->Stats.st_nlink = StdInfo.NumberOfLinks;
2076#endif
2077 }
2078 }
2079 if (MY_NT_SUCCESS(rcNt))
2080 {
2081 pObj->uCacheGen = pCache->auGenerations[pObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
2082 fRc = K_TRUE;
2083 }
2084 else
2085 {
2086 /* ouch! */
2087 kHlpAssertMsgFailed(("%#x\n", rcNt));
2088 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on dir - not implemented!\n", rcNt);
2089 fflush(stderr);
2090 __debugbreak();
2091 fRc = K_FALSE;
2092 }
2093 }
2094 }
2095
2096 return fRc;
2097}
2098
2099
2100
2101/**
2102 * Looks up a drive letter.
2103 *
2104 * Will enter the drive if necessary.
2105 *
2106 * @returns Pointer to the root directory of the drive or an update-to-date
2107 * missing node.
2108 * @param pCache The cache.
2109 * @param chLetter The uppercased drive letter.
2110 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2111 * @param penmError Where to return details as to why the lookup
2112 * failed.
2113 */
2114static PKFSOBJ kFsCacheLookupDrive(PKFSCACHE pCache, char chLetter, KU32 fFlags, KFSLOOKUPERROR *penmError)
2115{
2116 KU32 const uNameHash = chLetter - 'A';
2117 PKFSOBJ pCur = pCache->RootDir.papHashTab[uNameHash];
2118
2119 KU32 cLeft;
2120 PKFSOBJ *ppCur;
2121 MY_UNICODE_STRING NtPath;
2122 wchar_t wszTmp[8];
2123 char szTmp[4];
2124
2125 /*
2126 * Custom drive letter hashing.
2127 */
2128 kHlpAssert((uNameHash & pCache->RootDir.fHashTabMask) == uNameHash);
2129 while (pCur)
2130 {
2131 if ( pCur->uNameHash == uNameHash
2132 && pCur->cchName == 2
2133 && pCur->pszName[0] == chLetter
2134 && pCur->pszName[1] == ':')
2135 {
2136 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
2137 return pCur;
2138 if ( (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2139 || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
2140 return pCur;
2141 return NULL;
2142 }
2143 pCur = pCur->pNextNameHash;
2144 }
2145
2146 /*
2147 * Make 100% sure it's not there.
2148 */
2149 cLeft = pCache->RootDir.cChildren;
2150 ppCur = pCache->RootDir.papChildren;
2151 while (cLeft-- > 0)
2152 {
2153 pCur = *ppCur++;
2154 if ( pCur->cchName == 2
2155 && pCur->pszName[0] == chLetter
2156 && pCur->pszName[1] == ':')
2157 {
2158 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
2159 return pCur;
2160 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
2161 if ( (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2162 || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
2163 return pCur;
2164 return NULL;
2165 }
2166 }
2167
2168 if (fFlags & KFSCACHE_LOOKUP_F_NO_INSERT)
2169 {
2170 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; /* close enough */
2171 return NULL;
2172 }
2173
2174 /*
2175 * Need to add it. We always keep the drive letters open for the benefit
2176 * of kFsCachePopuplateOrRefreshDir and others.
2177 */
2178 wszTmp[0] = szTmp[0] = chLetter;
2179 wszTmp[1] = szTmp[1] = ':';
2180 wszTmp[2] = szTmp[2] = '\\';
2181 wszTmp[3] = '.';
2182 wszTmp[4] = '\0';
2183 szTmp[2] = '\0';
2184
2185 NtPath.Buffer = NULL;
2186 NtPath.Length = 0;
2187 NtPath.MaximumLength = 0;
2188 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
2189 {
2190 HANDLE hDir;
2191 MY_NTSTATUS rcNt;
2192 rcNt = birdOpenFileUniStr(NULL /*hRoot*/,
2193 &NtPath,
2194 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
2195 FILE_ATTRIBUTE_NORMAL,
2196 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2197 FILE_OPEN,
2198 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
2199 OBJ_CASE_INSENSITIVE,
2200 &hDir);
2201 birdFreeNtPath(&NtPath);
2202 if (MY_NT_SUCCESS(rcNt))
2203 {
2204 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
2205#ifdef KFSCACHE_CFG_SHORT_NAMES
2206 NULL, 0, NULL, 0,
2207#endif
2208 KFSOBJ_TYPE_DIR, penmError);
2209 if (pDir)
2210 {
2211 /*
2212 * We need a little bit of extra info for a drive root. These things are typically
2213 * inherited by subdirectories down the tree, so, we do it all here for till that changes.
2214 */
2215 union
2216 {
2217 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
2218 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
2219 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
2220 } uBuf;
2221 MY_IO_STATUS_BLOCK Ios;
2222 KBOOL fRc;
2223
2224 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
2225 pDir->hDir = hDir;
2226
2227 if (birdStatHandle(hDir, &pDir->Obj.Stats, pDir->Obj.pszName) == 0)
2228 {
2229 pDir->Obj.fHaveStats = K_TRUE;
2230 pDir->uDevNo = pDir->Obj.Stats.st_dev;
2231 }
2232 else
2233 {
2234 /* Just in case. */
2235 pDir->Obj.fHaveStats = K_FALSE;
2236 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
2237 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
2238 }
2239
2240 /* Get the file system. */
2241 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
2242 Ios.Information = -1;
2243 Ios.u.Status = -1;
2244 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
2245 MyFileFsAttributeInformation);
2246 if (MY_NT_SUCCESS(rcNt))
2247 rcNt = Ios.u.Status;
2248 if (MY_NT_SUCCESS(rcNt))
2249 {
2250 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N'
2251 && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
2252 && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
2253 && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
2254 && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
2255 {
2256 DWORD dwDriveType = GetDriveTypeW(wszTmp);
2257 if ( dwDriveType == DRIVE_FIXED
2258 || dwDriveType == DRIVE_RAMDISK)
2259 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
2260 }
2261 }
2262
2263 /*
2264 * Link the new drive letter into the root dir.
2265 */
2266 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
2267 kFsCacheObjRelease(pCache, &pDir->Obj);
2268 if (fRc)
2269 {
2270 pDir->Obj.pNextNameHash = pCache->RootDir.papHashTab[uNameHash];
2271 pCache->RootDir.papHashTab[uNameHash] = &pDir->Obj;
2272 return &pDir->Obj;
2273 }
2274 return NULL;
2275 }
2276
2277 g_pfnNtClose(hDir);
2278 return NULL;
2279 }
2280
2281 /* Assume it doesn't exist if this happens... This may be a little to
2282 restrictive wrt status code checks. */
2283 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
2284 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
2285 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
2286 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
2287 ("%#x\n", rcNt),
2288 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
2289 NULL);
2290 }
2291 else
2292 {
2293 kHlpAssertFailed();
2294 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
2295 return NULL;
2296 }
2297
2298 /*
2299 * Maybe create a missing entry.
2300 */
2301 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2302 {
2303 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
2304#ifdef KFSCACHE_CFG_SHORT_NAMES
2305 NULL, 0, NULL, 0,
2306#endif
2307 KFSOBJ_TYPE_MISSING, penmError);
2308 if (pMissing)
2309 {
2310 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
2311 kFsCacheObjRelease(pCache, pMissing);
2312 return fRc ? pMissing : NULL;
2313 }
2314 }
2315 else
2316 {
2317 /** @todo this isn't necessary correct for a root spec. */
2318 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2319 }
2320 return NULL;
2321}
2322
2323
2324/**
2325 * Slow path that allocates the child hash table and enters the given one.
2326 *
2327 * Allocation fialures are ignored.
2328 *
2329 * @param pCache The cache (for stats).
2330 * @param pDir The directory.
2331 * @param uNameHash The name hash to enter @a pChild under.
2332 * @param pChild The child to enter into the hash table.
2333 */
2334static void kFsCacheDirAllocHashTabAndEnterChild(PKFSCACHE pCache, PKFSDIR pDir, KU32 uNameHash, PKFSOBJ pChild)
2335{
2336 if (uNameHash != 0) /* paranoia ^ 4! */
2337 {
2338 /*
2339 * Double the current number of children and round up to a multiple of
2340 * two so we can avoid division.
2341 */
2342 KU32 cbHashTab;
2343 KU32 cEntries;
2344 kHlpAssert(pDir->cChildren > 0);
2345 if (pDir->cChildren <= KU32_MAX / 4)
2346 {
2347#if defined(_MSC_VER) && 1
2348 KU32 cEntriesRaw = pDir->cChildren * 2;
2349 KU32 cEntriesShift;
2350 kHlpAssert(sizeof(cEntries) == (unsigned long));
2351 if (_BitScanReverse(&cEntriesShift, cEntriesRaw))
2352 {
2353 if ( K_BIT32(cEntriesShift) < cEntriesRaw
2354 && cEntriesShift < 31U)
2355 cEntriesShift++;
2356 cEntries = K_BIT32(cEntriesShift);
2357 }
2358 else
2359 {
2360 kHlpAssertFailed();
2361 cEntries = KU32_MAX / 2 + 1;
2362 }
2363#else
2364 cEntries = pDir->cChildren * 2 - 1;
2365 cEntries |= cEntries >> 1;
2366 cEntries |= cEntries >> 2;
2367 cEntries |= cEntries >> 4;
2368 cEntries |= cEntries >> 8;
2369 cEntries |= cEntries >> 16;
2370 cEntries++;
2371#endif
2372 }
2373 else
2374 cEntries = KU32_MAX / 2 + 1;
2375 kHlpAssert((cEntries & (cEntries - 1)) == 0);
2376
2377 cbHashTab = cEntries * sizeof(pDir->papHashTab[0]);
2378 pDir->papHashTab = (PKFSOBJ *)kHlpAllocZ(cbHashTab);
2379 if (pDir->papHashTab)
2380 {
2381 KU32 idx;
2382 pDir->fHashTabMask = cEntries - 1;
2383 pCache->cbObjects += cbHashTab;
2384 pCache->cChildHashTabs++;
2385 pCache->cChildHashEntriesTotal += cEntries;
2386
2387 /*
2388 * Insert it.
2389 */
2390 pChild->uNameHash = uNameHash;
2391 idx = uNameHash & (pDir->fHashTabMask);
2392 pChild->pNextNameHash = pDir->papHashTab[idx];
2393 pDir->papHashTab[idx] = pChild;
2394 pCache->cChildHashed++;
2395 }
2396 }
2397}
2398
2399
2400/**
2401 * Look up a child node, ANSI version.
2402 *
2403 * @returns Pointer to the child if found, NULL if not.
2404 * @param pCache The cache.
2405 * @param pParent The parent directory to search.
2406 * @param pchName The child name to search for (not terminated).
2407 * @param cchName The length of the child name.
2408 */
2409static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
2410{
2411 /*
2412 * Check for '.' first ('..' won't appear).
2413 */
2414 if (cchName != 1 || *pchName != '.')
2415 {
2416 PKFSOBJ *ppCur;
2417 KU32 cLeft;
2418 KU32 uNameHash;
2419
2420 /*
2421 * Do hash table lookup.
2422 *
2423 * This caches previous lookups, which should be useful when looking up
2424 * intermediate directories at least.
2425 */
2426 if (pParent->papHashTab != NULL)
2427 {
2428 PKFSOBJ pCur;
2429 uNameHash = kFsCacheStrHashN(pchName, cchName);
2430 pCur = pParent->papHashTab[uNameHash & pParent->fHashTabMask];
2431 while (pCur)
2432 {
2433 if ( pCur->uNameHash == uNameHash
2434 && ( ( pCur->cchName == cchName
2435 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2436#ifdef KFSCACHE_CFG_SHORT_NAMES
2437 || ( pCur->cchShortName == cchName
2438 && pCur->pszShortName != pCur->pszName
2439 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2440#endif
2441 )
2442 )
2443 {
2444 pCache->cChildHashHits++;
2445 pCache->cChildSearches++;
2446 return pCur;
2447 }
2448 pCur = pCur->pNextNameHash;
2449 }
2450 }
2451 else
2452 uNameHash = 0;
2453
2454 /*
2455 * Do linear search.
2456 */
2457 cLeft = pParent->cChildren;
2458 ppCur = pParent->papChildren;
2459 while (cLeft-- > 0)
2460 {
2461 PKFSOBJ pCur = *ppCur++;
2462 if ( ( pCur->cchName == cchName
2463 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2464#ifdef KFSCACHE_CFG_SHORT_NAMES
2465 || ( pCur->cchShortName == cchName
2466 && pCur->pszShortName != pCur->pszName
2467 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2468#endif
2469 )
2470 {
2471 /*
2472 * Consider entering it into the parent hash table.
2473 * Note! We hash the input, not the name we found.
2474 */
2475 if ( pCur->uNameHash == 0
2476 && pParent->cChildren >= 2)
2477 {
2478 if (pParent->papHashTab)
2479 {
2480 if (uNameHash != 0)
2481 {
2482 KU32 idxNameHash = uNameHash & pParent->fHashTabMask;
2483 pCur->uNameHash = uNameHash;
2484 pCur->pNextNameHash = pParent->papHashTab[idxNameHash];
2485 pParent->papHashTab[idxNameHash] = pCur;
2486 if (pCur->pNextNameHash)
2487 pCache->cChildHashCollisions++;
2488 pCache->cChildHashed++;
2489 }
2490 }
2491 else
2492 kFsCacheDirAllocHashTabAndEnterChild(pCache, pParent, kFsCacheStrHashN(pchName, cchName), pCur);
2493 }
2494
2495 pCache->cChildSearches++;
2496 return pCur;
2497 }
2498 }
2499
2500 pCache->cChildSearches++;
2501 return NULL;
2502 }
2503 return &pParent->Obj;
2504}
2505
2506
2507/**
2508 * Look up a child node, UTF-16 version.
2509 *
2510 * @returns Pointer to the child if found, NULL if not.
2511 * @param pCache The cache.
2512 * @param pParent The parent directory to search.
2513 * @param pwcName The child name to search for (not terminated).
2514 * @param cwcName The length of the child name (in wchar_t's).
2515 */
2516static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
2517{
2518 /*
2519 * Check for '.' first ('..' won't appear).
2520 */
2521 if (cwcName != 1 || *pwcName != '.')
2522 {
2523 PKFSOBJ *ppCur;
2524 KU32 cLeft;
2525 KU32 uNameHash;
2526
2527 /*
2528 * Do hash table lookup.
2529 *
2530 * This caches previous lookups, which should be useful when looking up
2531 * intermediate directories at least.
2532 */
2533 if (pParent->papHashTab != NULL)
2534 {
2535 PKFSOBJ pCur;
2536 uNameHash = kFsCacheUtf16HashN(pwcName, cwcName);
2537 pCur = pParent->papHashTab[uNameHash & pParent->fHashTabMask];
2538 while (pCur)
2539 {
2540 if ( pCur->uNameHash == uNameHash
2541 && ( ( pCur->cwcName == cwcName
2542 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2543#ifdef KFSCACHE_CFG_SHORT_NAMES
2544 || ( pCur->cwcShortName == cwcName
2545 && pCur->pwszShortName != pCur->pwszName
2546 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2547#endif
2548 )
2549 )
2550 {
2551 pCache->cChildHashHits++;
2552 pCache->cChildSearches++;
2553 return pCur;
2554 }
2555 pCur = pCur->pNextNameHash;
2556 }
2557 }
2558 else
2559 uNameHash = 0;
2560
2561 /*
2562 * Do linear search.
2563 */
2564 cLeft = pParent->cChildren;
2565 ppCur = pParent->papChildren;
2566 while (cLeft-- > 0)
2567 {
2568 PKFSOBJ pCur = *ppCur++;
2569 if ( ( pCur->cwcName == cwcName
2570 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2571#ifdef KFSCACHE_CFG_SHORT_NAMES
2572 || ( pCur->cwcShortName == cwcName
2573 && pCur->pwszShortName != pCur->pwszName
2574 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2575#endif
2576 )
2577 {
2578 /*
2579 * Consider entering it into the parent hash table.
2580 * Note! We hash the input, not the name we found.
2581 */
2582 if ( pCur->uNameHash == 0
2583 && pParent->cChildren >= 4)
2584 {
2585 if (pParent->papHashTab)
2586 {
2587 if (uNameHash != 0)
2588 {
2589 KU32 idxNameHash = uNameHash & pParent->fHashTabMask;
2590 pCur->uNameHash = uNameHash;
2591 pCur->pNextNameHash = pParent->papHashTab[idxNameHash];
2592 pParent->papHashTab[idxNameHash] = pCur;
2593 if (pCur->pNextNameHash)
2594 pCache->cChildHashCollisions++;
2595 pCache->cChildHashed++;
2596 }
2597 }
2598 else
2599 kFsCacheDirAllocHashTabAndEnterChild(pCache, pParent, kFsCacheUtf16HashN(pwcName, cwcName), pCur);
2600 }
2601
2602 pCache->cChildSearches++;
2603 return pCur;
2604 }
2605 }
2606 pCache->cChildSearches++;
2607 return NULL;
2608 }
2609 return &pParent->Obj;
2610}
2611
2612
2613/**
2614 * Looks up a UNC share, ANSI version.
2615 *
2616 * We keep both the server and share in the root directory entry. This means we
2617 * have to clean up the entry name before we can insert it.
2618 *
2619 * @returns Pointer to the share root directory or an update-to-date missing
2620 * node.
2621 * @param pCache The cache.
2622 * @param pszPath The path.
2623 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2624 * @param poff Where to return the root dire.
2625 * @param penmError Where to return details as to why the lookup
2626 * failed.
2627 */
2628static PKFSOBJ kFsCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 fFlags,
2629 KU32 *poff, KFSLOOKUPERROR *penmError)
2630{
2631 /*
2632 * Special case: Long path prefix w/ drive letter following it.
2633 * Note! Must've been converted from wide char to ANSI.
2634 */
2635 if ( IS_SLASH(pszPath[0])
2636 && IS_SLASH(pszPath[1])
2637 && pszPath[2] == '?'
2638 && IS_SLASH(pszPath[3])
2639 && IS_ALPHA(pszPath[4])
2640 && pszPath[5] == ':'
2641 && IS_SLASH(pszPath[6]) )
2642 {
2643 *poff = 4 + 2;
2644 return kFsCacheLookupDrive(pCache, pszPath[4], fFlags, penmError);
2645 }
2646
2647#if 0 /* later */
2648 KU32 offStartServer;
2649 KU32 offEndServer;
2650 KU32 offStartShare;
2651
2652 KU32 offEnd = 2;
2653 while (IS_SLASH(pszPath[offEnd]))
2654 offEnd++;
2655
2656 offStartServer = offEnd;
2657 while ( (ch = pszPath[offEnd]) != '\0'
2658 && !IS_SLASH(ch))
2659 offEnd++;
2660 offEndServer = offEnd;
2661
2662 if (ch != '\0')
2663 { /* likely */ }
2664 else
2665 {
2666 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2667 return NULL;
2668 }
2669
2670 while (IS_SLASH(pszPath[offEnd]))
2671 offEnd++;
2672 offStartServer = offEnd;
2673 while ( (ch = pszPath[offEnd]) != '\0'
2674 && !IS_SLASH(ch))
2675 offEnd++;
2676#endif
2677 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2678 return NULL;
2679}
2680
2681
2682/**
2683 * Looks up a UNC share, UTF-16 version.
2684 *
2685 * We keep both the server and share in the root directory entry. This means we
2686 * have to clean up the entry name before we can insert it.
2687 *
2688 * @returns Pointer to the share root directory or an update-to-date missing
2689 * node.
2690 * @param pCache The cache.
2691 * @param pwszPath The path.
2692 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2693 * @param poff Where to return the root dir.
2694 * @param penmError Where to return details as to why the lookup
2695 * failed.
2696 */
2697static PKFSOBJ kFsCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 fFlags,
2698 KU32 *poff, KFSLOOKUPERROR *penmError)
2699{
2700 /*
2701 * Special case: Long path prefix w/ drive letter following it.
2702 */
2703 if ( IS_SLASH(pwszPath[0])
2704 && IS_SLASH(pwszPath[1])
2705 && pwszPath[2] == '?'
2706 && IS_SLASH(pwszPath[3])
2707 && IS_ALPHA(pwszPath[4])
2708 && pwszPath[5] == ':'
2709 && IS_SLASH(pwszPath[6]) )
2710 {
2711 *poff = 4 + 2;
2712 return kFsCacheLookupDrive(pCache, (char)pwszPath[4], fFlags, penmError);
2713 }
2714
2715
2716#if 0 /* later */
2717 KU32 offStartServer;
2718 KU32 offEndServer;
2719 KU32 offStartShare;
2720
2721 KU32 offEnd = 2;
2722 while (IS_SLASH(pwszPath[offEnd]))
2723 offEnd++;
2724
2725 offStartServer = offEnd;
2726 while ( (ch = pwszPath[offEnd]) != '\0'
2727 && !IS_SLASH(ch))
2728 offEnd++;
2729 offEndServer = offEnd;
2730
2731 if (ch != '\0')
2732 { /* likely */ }
2733 else
2734 {
2735 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2736 return NULL;
2737 }
2738
2739 while (IS_SLASH(pwszPath[offEnd]))
2740 offEnd++;
2741 offStartServer = offEnd;
2742 while ( (ch = pwszPath[offEnd]) != '\0'
2743 && !IS_SLASH(ch))
2744 offEnd++;
2745#endif
2746 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2747 return NULL;
2748}
2749
2750
2751/**
2752 * Walks an full path relative to the given directory, ANSI version.
2753 *
2754 * This will create any missing nodes while walking.
2755 *
2756 * The caller will have to do the path hash table insertion of the result.
2757 *
2758 * @returns Pointer to the tree node corresponding to @a pszPath.
2759 * NULL on lookup failure, see @a penmError for details.
2760 * @param pCache The cache.
2761 * @param pParent The directory to start the lookup in.
2762 * @param pszPath The path to walk.
2763 * @param cchPath The length of the path.
2764 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2765 * @param penmError Where to return details as to why the lookup
2766 * failed.
2767 * @param ppLastAncestor Where to return the last parent element found
2768 * (referenced) in case of error like an path/file
2769 * not found problem. Optional.
2770 */
2771PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags,
2772 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2773{
2774 /*
2775 * Walk loop.
2776 */
2777 KU32 off = 0;
2778 if (ppLastAncestor)
2779 *ppLastAncestor = NULL;
2780 KFSCACHE_LOCK(pCache);
2781 for (;;)
2782 {
2783 PKFSOBJ pChild;
2784
2785 /*
2786 * Find the end of the component, counting trailing slashes.
2787 */
2788 char ch;
2789 KU32 cchSlashes = 0;
2790 KU32 offEnd = off + 1;
2791 while ((ch = pszPath[offEnd]) != '\0')
2792 {
2793 if (!IS_SLASH(ch))
2794 offEnd++;
2795 else
2796 {
2797 do
2798 cchSlashes++;
2799 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2800 break;
2801 }
2802 }
2803
2804 /*
2805 * Do we need to populate or refresh this directory first?
2806 */
2807 if ( !pParent->fNeedRePopulating
2808 && pParent->fPopulated
2809 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2810 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
2811 { /* likely */ }
2812 else if ( (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH))
2813 || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2814 { /* likely */ }
2815 else
2816 {
2817 if (ppLastAncestor)
2818 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2819 KFSCACHE_UNLOCK(pCache);
2820 return NULL;
2821 }
2822
2823 /*
2824 * Search the current node for the name.
2825 *
2826 * If we don't find it, we may insert a missing node depending on
2827 * the cache configuration.
2828 */
2829 pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
2830 if (pChild != NULL)
2831 { /* probably likely */ }
2832 else
2833 {
2834 if ( (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2835 && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
2836 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
2837 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
2838 {
2839 if (pChild)
2840 {
2841 kFsCacheObjRetainInternal(pChild);
2842 KFSCACHE_UNLOCK(pCache);
2843 return pChild;
2844 }
2845 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2846 }
2847 else
2848 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2849 if (ppLastAncestor)
2850 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2851 KFSCACHE_UNLOCK(pCache);
2852 return NULL;
2853 }
2854
2855 /* Advance off and check if we're done already. */
2856 off = offEnd + cchSlashes;
2857 if ( cchSlashes == 0
2858 || off >= cchPath)
2859 {
2860 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2861 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2862 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2863 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2864 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2865 { /* likely */ }
2866 else
2867 {
2868 if (ppLastAncestor)
2869 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2870 KFSCACHE_UNLOCK(pCache);
2871 return NULL;
2872 }
2873 kFsCacheObjRetainInternal(pChild);
2874 KFSCACHE_UNLOCK(pCache);
2875 return pChild;
2876 }
2877
2878 /*
2879 * Check that it's a directory. If a missing entry, we may have to
2880 * refresh it and re-examin it.
2881 */
2882 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2883 pParent = (PKFSDIR)pChild;
2884 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2885 {
2886 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2887 if (ppLastAncestor)
2888 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2889 KFSCACHE_UNLOCK(pCache);
2890 return NULL;
2891 }
2892 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2893 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2894 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
2895 {
2896 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2897 if (ppLastAncestor)
2898 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2899 KFSCACHE_UNLOCK(pCache);
2900 return NULL;
2901 }
2902 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2903 pParent = (PKFSDIR)pChild;
2904 else
2905 {
2906 if (ppLastAncestor)
2907 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2908 KFSCACHE_UNLOCK(pCache);
2909 return NULL;
2910 }
2911 }
2912
2913 /* not reached */
2914 KFSCACHE_UNLOCK(pCache);
2915 return NULL;
2916}
2917
2918
2919/**
2920 * Walks an full path relative to the given directory, UTF-16 version.
2921 *
2922 * This will create any missing nodes while walking.
2923 *
2924 * The caller will have to do the path hash table insertion of the result.
2925 *
2926 * @returns Pointer to the tree node corresponding to @a pszPath.
2927 * NULL on lookup failure, see @a penmError for details.
2928 * @param pCache The cache.
2929 * @param pParent The directory to start the lookup in.
2930 * @param pszPath The path to walk. No dot-dot bits allowed!
2931 * @param cchPath The length of the path.
2932 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2933 * @param penmError Where to return details as to why the lookup
2934 * failed.
2935 * @param ppLastAncestor Where to return the last parent element found
2936 * (referenced) in case of error like an path/file
2937 * not found problem. Optional.
2938 */
2939PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
2940 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2941{
2942 /*
2943 * Walk loop.
2944 */
2945 KU32 off = 0;
2946 if (ppLastAncestor)
2947 *ppLastAncestor = NULL;
2948 KFSCACHE_LOCK(pCache);
2949 for (;;)
2950 {
2951 PKFSOBJ pChild;
2952
2953 /*
2954 * Find the end of the component, counting trailing slashes.
2955 */
2956 wchar_t wc;
2957 KU32 cwcSlashes = 0;
2958 KU32 offEnd = off + 1;
2959 while ((wc = pwszPath[offEnd]) != '\0')
2960 {
2961 if (!IS_SLASH(wc))
2962 offEnd++;
2963 else
2964 {
2965 do
2966 cwcSlashes++;
2967 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2968 break;
2969 }
2970 }
2971
2972 /*
2973 * Do we need to populate or refresh this directory first?
2974 */
2975 if ( !pParent->fNeedRePopulating
2976 && pParent->fPopulated
2977 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2978 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
2979 { /* likely */ }
2980 else if ( (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH))
2981 || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2982 { /* likely */ }
2983 else
2984 {
2985 if (ppLastAncestor)
2986 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2987 KFSCACHE_UNLOCK(pCache);
2988 return NULL;
2989 }
2990
2991 /*
2992 * Search the current node for the name.
2993 *
2994 * If we don't find it, we may insert a missing node depending on
2995 * the cache configuration.
2996 */
2997 pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off);
2998 if (pChild != NULL)
2999 { /* probably likely */ }
3000 else
3001 {
3002 if ( (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
3003 && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
3004 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
3005 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
3006 {
3007 if (pChild)
3008 {
3009 kFsCacheObjRetainInternal(pChild);
3010 KFSCACHE_UNLOCK(pCache);
3011 return pChild;
3012 }
3013 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3014 }
3015 else
3016 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
3017 if (ppLastAncestor)
3018 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3019 KFSCACHE_UNLOCK(pCache);
3020 return NULL;
3021 }
3022
3023 /* Advance off and check if we're done already. */
3024 off = offEnd + cwcSlashes;
3025 if ( cwcSlashes == 0
3026 || off >= cwcPath)
3027 {
3028 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
3029 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3030 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3031 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
3032 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
3033 { /* likely */ }
3034 else
3035 {
3036 if (ppLastAncestor)
3037 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3038 KFSCACHE_UNLOCK(pCache);
3039 return NULL;
3040 }
3041 kFsCacheObjRetainInternal(pChild);
3042 KFSCACHE_UNLOCK(pCache);
3043 return pChild;
3044 }
3045
3046 /*
3047 * Check that it's a directory. If a missing entry, we may have to
3048 * refresh it and re-examin it.
3049 */
3050 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
3051 pParent = (PKFSDIR)pChild;
3052 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
3053 {
3054 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
3055 if (ppLastAncestor)
3056 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3057 KFSCACHE_UNLOCK(pCache);
3058 return NULL;
3059 }
3060 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3061 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3062 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) )
3063
3064 {
3065 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
3066 if (ppLastAncestor)
3067 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3068 KFSCACHE_UNLOCK(pCache);
3069 return NULL;
3070 }
3071 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
3072 pParent = (PKFSDIR)pChild;
3073 else
3074 {
3075 if (ppLastAncestor)
3076 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3077 KFSCACHE_UNLOCK(pCache);
3078 return NULL;
3079 }
3080 }
3081
3082 KFSCACHE_UNLOCK(pCache);
3083 return NULL;
3084}
3085
3086/**
3087 * Walk the file system tree for the given absolute path, entering it into the
3088 * hash table.
3089 *
3090 * This will create any missing nodes while walking.
3091 *
3092 * The caller will have to do the path hash table insertion of the result.
3093 *
3094 * @returns Pointer to the tree node corresponding to @a pszPath.
3095 * NULL on lookup failure, see @a penmError for details.
3096 * @param pCache The cache.
3097 * @param pszPath The path to walk. No dot-dot bits allowed!
3098 * @param cchPath The length of the path.
3099 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3100 * @param penmError Where to return details as to why the lookup
3101 * failed.
3102 * @param ppLastAncestor Where to return the last parent element found
3103 * (referenced) in case of error an path/file not
3104 * found problem. Optional.
3105 */
3106static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
3107 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3108{
3109 PKFSOBJ pRoot;
3110 KU32 cchSlashes;
3111 KU32 offEnd;
3112
3113 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
3114
3115 /*
3116 * The root "directory" needs special handling, so we keep it outside the
3117 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
3118 */
3119 cchSlashes = 0;
3120 if ( pszPath[1] == ':'
3121 && IS_ALPHA(pszPath[0]))
3122 {
3123 /* Drive letter. */
3124 offEnd = 2;
3125 kHlpAssert(IS_SLASH(pszPath[2]));
3126 pRoot = kFsCacheLookupDrive(pCache, toupper(pszPath[0]), fFlags, penmError);
3127 }
3128 else if ( IS_SLASH(pszPath[0])
3129 && IS_SLASH(pszPath[1]) )
3130 pRoot = kFsCacheLookupUncShareA(pCache, pszPath, fFlags, &offEnd, penmError);
3131 else
3132 {
3133 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
3134 return NULL;
3135 }
3136 if (pRoot)
3137 { /* likely */ }
3138 else
3139 return NULL;
3140
3141 /* Count slashes trailing the root spec. */
3142 if (offEnd < cchPath)
3143 {
3144 kHlpAssert(IS_SLASH(pszPath[offEnd]));
3145 do
3146 cchSlashes++;
3147 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
3148 }
3149
3150 /* Done already? */
3151 if (offEnd >= cchPath)
3152 {
3153 if ( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3154 || pRoot->uCacheGen == ( pRoot->bObjType != KFSOBJ_TYPE_MISSING
3155 ? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3156 : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
3157 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
3158 || kFsCacheRefreshObj(pCache, pRoot, penmError))
3159 return kFsCacheObjRetainInternal(pRoot);
3160 if (ppLastAncestor)
3161 *ppLastAncestor = kFsCacheObjRetainInternal(pRoot);
3162 return NULL;
3163 }
3164
3165 /* Check that we've got a valid result and not a cached negative one. */
3166 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
3167 { /* likely */ }
3168 else
3169 {
3170 kHlpAssert(pRoot->bObjType == KFSOBJ_TYPE_MISSING);
3171 kHlpAssert( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3172 || pRoot->uCacheGen == pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]);
3173 return pRoot;
3174 }
3175
3176 /*
3177 * Now that we've found a valid root directory, lookup the
3178 * remainder of the path starting with it.
3179 */
3180 return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pRoot, &pszPath[offEnd + cchSlashes],
3181 cchPath - offEnd - cchSlashes, fFlags, penmError, ppLastAncestor);
3182}
3183
3184
3185/**
3186 * Walk the file system tree for the given absolute path, UTF-16 version.
3187 *
3188 * This will create any missing nodes while walking.
3189 *
3190 * The caller will have to do the path hash table insertion of the result.
3191 *
3192 * @returns Pointer to the tree node corresponding to @a pszPath.
3193 * NULL on lookup failure, see @a penmError for details.
3194 * @param pCache The cache.
3195 * @param pwszPath The path to walk.
3196 * @param cwcPath The length of the path (in wchar_t's).
3197 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3198 * @param penmError Where to return details as to why the lookup
3199 * failed.
3200 * @param ppLastAncestor Where to return the last parent element found
3201 * (referenced) in case of error an path/file not
3202 * found problem. Optional.
3203 */
3204static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
3205 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3206{
3207 PKFSDIR pParent = &pCache->RootDir;
3208 PKFSOBJ pRoot;
3209 KU32 off;
3210 KU32 cwcSlashes;
3211 KU32 offEnd;
3212
3213 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath));
3214
3215 /*
3216 * The root "directory" needs special handling, so we keep it outside the
3217 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
3218 */
3219 cwcSlashes = 0;
3220 off = 0;
3221 if ( pwszPath[1] == ':'
3222 && IS_ALPHA(pwszPath[0]))
3223 {
3224 /* Drive letter. */
3225 offEnd = 2;
3226 kHlpAssert(IS_SLASH(pwszPath[2]));
3227 pRoot = kFsCacheLookupDrive(pCache, toupper(pwszPath[0]), fFlags, penmError);
3228 }
3229 else if ( IS_SLASH(pwszPath[0])
3230 && IS_SLASH(pwszPath[1]) )
3231 pRoot = kFsCacheLookupUncShareW(pCache, pwszPath, fFlags, &offEnd, penmError);
3232 else
3233 {
3234 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
3235 return NULL;
3236 }
3237 if (pRoot)
3238 { /* likely */ }
3239 else
3240 return NULL;
3241
3242 /* Count slashes trailing the root spec. */
3243 if (offEnd < cwcPath)
3244 {
3245 kHlpAssert(IS_SLASH(pwszPath[offEnd]));
3246 do
3247 cwcSlashes++;
3248 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
3249 }
3250
3251 /* Done already? */
3252 if (offEnd >= cwcPath)
3253 {
3254 if ( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3255 || pRoot->uCacheGen == (pRoot->bObjType != KFSOBJ_TYPE_MISSING
3256 ? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3257 : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
3258 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
3259 || kFsCacheRefreshObj(pCache, pRoot, penmError))
3260 return kFsCacheObjRetainInternal(pRoot);
3261 if (ppLastAncestor)
3262 *ppLastAncestor = kFsCacheObjRetainInternal(pRoot);
3263 return NULL;
3264 }
3265
3266 /* Check that we've got a valid result and not a cached negative one. */
3267 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
3268 { /* likely */ }
3269 else
3270 {
3271 kHlpAssert(pRoot->bObjType == KFSOBJ_TYPE_MISSING);
3272 kHlpAssert( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3273 || pRoot->uCacheGen == pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]);
3274 return pRoot;
3275 }
3276
3277 /*
3278 * Now that we've found a valid root directory, lookup the
3279 * remainder of the path starting with it.
3280 */
3281 return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pRoot, &pwszPath[offEnd + cwcSlashes],
3282 cwcPath - offEnd - cwcSlashes, fFlags, penmError, ppLastAncestor);
3283}
3284
3285
3286/**
3287 * This deals with paths that are relative and paths that contains '..'
3288 * elements, ANSI version.
3289 *
3290 * @returns Pointer to object corresponding to @a pszPath on success.
3291 * NULL if this isn't a path we care to cache.
3292 *
3293 * @param pCache The cache.
3294 * @param pszPath The path.
3295 * @param cchPath The length of the path.
3296 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3297 * @param penmError Where to return details as to why the lookup
3298 * failed.
3299 * @param ppLastAncestor Where to return the last parent element found
3300 * (referenced) in case of error an path/file not
3301 * found problem. Optional.
3302 */
3303static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
3304 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3305{
3306 /*
3307 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
3308 * ends up calling it anyway.
3309 */
3310 char szFull[KFSCACHE_CFG_MAX_PATH];
3311 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
3312 if ( cchFull >= 3
3313 && cchFull < sizeof(szFull))
3314 {
3315 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath));
3316 return kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, fFlags, penmError, ppLastAncestor);
3317 }
3318
3319 /* The path is too long! */
3320 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
3321 *penmError = cchFull >= 3 ? KFSLOOKUPERROR_PATH_TOO_LONG : KFSLOOKUPERROR_PATH_TOO_SHORT;
3322 return NULL;
3323}
3324
3325
3326/**
3327 * This deals with paths that are relative and paths that contains '..'
3328 * elements, UTF-16 version.
3329 *
3330 * @returns Pointer to object corresponding to @a pszPath on success.
3331 * NULL if this isn't a path we care to cache.
3332 *
3333 * @param pCache The cache.
3334 * @param pwszPath The path.
3335 * @param cwcPath The length of the path (in wchar_t's).
3336 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3337 * @param penmError Where to return details as to why the lookup
3338 * failed.
3339 * @param ppLastAncestor Where to return the last parent element found
3340 * (referenced) in case of error an path/file not
3341 * found problem. Optional.
3342 */
3343static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KU32 fFlags,
3344 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3345{
3346 /*
3347 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
3348 * ends up calling it anyway.
3349 */
3350 wchar_t wszFull[KFSCACHE_CFG_MAX_PATH];
3351 UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL);
3352 if ( cwcFull >= 3
3353 && cwcFull < KFSCACHE_CFG_MAX_PATH)
3354 {
3355 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
3356 return kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, fFlags, penmError, ppLastAncestor);
3357 }
3358
3359 /* The path is too long! */
3360 kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull));
3361 *penmError = cwcFull >= 3 ? KFSLOOKUPERROR_PATH_TOO_LONG : KFSLOOKUPERROR_PATH_TOO_SHORT;
3362 return NULL;
3363}
3364
3365
3366/**
3367 * Refreshes a path hash that has expired, ANSI version.
3368 *
3369 * @returns pHash on success, NULL if removed.
3370 * @param pCache The cache.
3371 * @param pHashEntry The path hash.
3372 * @param idxHashTab The hash table entry.
3373 */
3374static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
3375{
3376 PKFSOBJ pLastAncestor = NULL;
3377 if (!pHashEntry->pFsObj)
3378 {
3379 if (pHashEntry->fAbsolute)
3380 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3381 &pHashEntry->enmError, &pLastAncestor);
3382 else
3383 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3384 &pHashEntry->enmError, &pLastAncestor);
3385 }
3386 else
3387 {
3388 KU8 bOldType = pHashEntry->pFsObj->bObjType;
3389 KFSLOOKUPERROR enmError;
3390 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
3391 {
3392 if (pHashEntry->pFsObj->bObjType == bOldType)
3393 { }
3394 else
3395 {
3396 pHashEntry->pFsObj->cPathHashRefs -= 1;
3397 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
3398 if (pHashEntry->fAbsolute)
3399 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3400 &pHashEntry->enmError, &pLastAncestor);
3401 else
3402 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3403 &pHashEntry->enmError, &pLastAncestor);
3404 }
3405 }
3406 else
3407 {
3408 fprintf(stderr, "kFsCacheRefreshPathA - refresh failure handling not implemented!\n");
3409 __debugbreak();
3410 /** @todo just remove this entry. */
3411 return NULL;
3412 }
3413 }
3414
3415 if (pLastAncestor && !pHashEntry->pFsObj)
3416 pHashEntry->idxMissingGen = pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN;
3417 pHashEntry->uCacheGen = !pHashEntry->pFsObj
3418 ? pCache->auGenerationsMissing[pHashEntry->idxMissingGen]
3419 : pHashEntry->pFsObj->bObjType == KFSOBJ_TYPE_MISSING
3420 ? pCache->auGenerationsMissing[pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3421 : pCache->auGenerations[ pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
3422 if (pLastAncestor)
3423 kFsCacheObjRelease(pCache, pLastAncestor);
3424 return pHashEntry;
3425}
3426
3427
3428/**
3429 * Refreshes a path hash that has expired, UTF-16 version.
3430 *
3431 * @returns pHash on success, NULL if removed.
3432 * @param pCache The cache.
3433 * @param pHashEntry The path hash.
3434 * @param idxHashTab The hash table entry.
3435 */
3436static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab)
3437{
3438 PKFSOBJ pLastAncestor = NULL;
3439 if (!pHashEntry->pFsObj)
3440 {
3441 if (pHashEntry->fAbsolute)
3442 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3443 &pHashEntry->enmError, &pLastAncestor);
3444 else
3445 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3446 &pHashEntry->enmError, &pLastAncestor);
3447 }
3448 else
3449 {
3450 KU8 bOldType = pHashEntry->pFsObj->bObjType;
3451 KFSLOOKUPERROR enmError;
3452 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
3453 {
3454 if (pHashEntry->pFsObj->bObjType == bOldType)
3455 { }
3456 else
3457 {
3458 pHashEntry->pFsObj->cPathHashRefs -= 1;
3459 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
3460 if (pHashEntry->fAbsolute)
3461 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3462 &pHashEntry->enmError, &pLastAncestor);
3463 else
3464 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3465 &pHashEntry->enmError, &pLastAncestor);
3466 }
3467 }
3468 else
3469 {
3470 fprintf(stderr, "kFsCacheRefreshPathW - refresh failure handling not implemented!\n");
3471 fflush(stderr);
3472 __debugbreak();
3473 /** @todo just remove this entry. */
3474 return NULL;
3475 }
3476 }
3477 if (pLastAncestor && !pHashEntry->pFsObj)
3478 pHashEntry->idxMissingGen = pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN;
3479 pHashEntry->uCacheGen = !pHashEntry->pFsObj
3480 ? pCache->auGenerationsMissing[pHashEntry->idxMissingGen]
3481 : pHashEntry->pFsObj->bObjType == KFSOBJ_TYPE_MISSING
3482 ? pCache->auGenerationsMissing[pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3483 : pCache->auGenerations[ pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
3484 if (pLastAncestor)
3485 kFsCacheObjRelease(pCache, pLastAncestor);
3486 return pHashEntry;
3487}
3488
3489
3490/**
3491 * Internal lookup worker that looks up a KFSOBJ for the given ANSI path with
3492 * length and hash.
3493 *
3494 * This will first try the hash table. If not in the hash table, the file
3495 * system cache tree is walked, missing bits filled in and finally a hash table
3496 * entry is created.
3497 *
3498 * Only drive letter paths are cachable. We don't do any UNC paths at this
3499 * point.
3500 *
3501 * @returns Reference to object corresponding to @a pszPath on success, this
3502 * must be released by kFsCacheObjRelease.
3503 * NULL if not a path we care to cache.
3504 * @param pCache The cache.
3505 * @param pchPath The path to lookup.
3506 * @param cchPath The path length.
3507 * @param uHashPath The hash of the path.
3508 * @param penmError Where to return details as to why the lookup
3509 * failed.
3510 */
3511static PKFSOBJ kFsCacheLookupHashedA(PKFSCACHE pCache, const char *pchPath, KU32 cchPath, KU32 uHashPath,
3512 KFSLOOKUPERROR *penmError)
3513{
3514 /*
3515 * Do hash table lookup of the path.
3516 */
3517 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3518 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab];
3519 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3520 if (pHashEntry)
3521 {
3522 do
3523 {
3524 if ( pHashEntry->uHashPath == uHashPath
3525 && pHashEntry->cchPath == cchPath
3526 && kHlpMemComp(pHashEntry->pszPath, pchPath, cchPath) == 0)
3527 {
3528 PKFSOBJ pFsObj;
3529 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3530 || pHashEntry->uCacheGen == ( (pFsObj = pHashEntry->pFsObj) != NULL
3531 ? pFsObj->bObjType != KFSOBJ_TYPE_MISSING
3532 ? pCache->auGenerations[ pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3533 : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3534 : pCache->auGenerationsMissing[pHashEntry->idxMissingGen])
3535 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
3536 {
3537 pCache->cLookups++;
3538 pCache->cPathHashHits++;
3539 KFSCACHE_LOG2(("kFsCacheLookupA(%*.*s) - hit %p\n", cchPath, cchPath, pchPath, pHashEntry->pFsObj));
3540 *penmError = pHashEntry->enmError;
3541 if (pHashEntry->pFsObj)
3542 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3543 return NULL;
3544 }
3545 break;
3546 }
3547 pHashEntry = pHashEntry->pNext;
3548 } while (pHashEntry);
3549 }
3550
3551 /*
3552 * Create an entry for it by walking the file system cache and filling in the blanks.
3553 */
3554 if ( cchPath > 0
3555 && cchPath < KFSCACHE_CFG_MAX_PATH)
3556 {
3557 PKFSOBJ pFsObj;
3558 KBOOL fAbsolute;
3559 PKFSOBJ pLastAncestor = NULL;
3560
3561 /* Is absolute without any '..' bits? */
3562 if ( cchPath >= 3
3563 && ( ( pchPath[1] == ':' /* Drive letter */
3564 && IS_SLASH(pchPath[2])
3565 && IS_ALPHA(pchPath[0]) )
3566 || ( IS_SLASH(pchPath[0]) /* UNC */
3567 && IS_SLASH(pchPath[1]) ) )
3568 && !kFsCacheHasDotDotA(pchPath, cchPath) )
3569 {
3570 pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3571 fAbsolute = K_TRUE;
3572 }
3573 else
3574 {
3575 pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3576 fAbsolute = K_FALSE;
3577 }
3578 if ( pFsObj
3579 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3580 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3581 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3582 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pchPath, cchPath, uHashPath, idxHashTab, fAbsolute,
3583 pLastAncestor ? pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN : 0, *penmError);
3584 if (pLastAncestor)
3585 kFsCacheObjRelease(pCache, pLastAncestor);
3586
3587 pCache->cLookups++;
3588 if (pFsObj)
3589 pCache->cWalkHits++;
3590 return pFsObj;
3591 }
3592
3593 *penmError = cchPath > 0 ? KFSLOOKUPERROR_PATH_TOO_LONG : KFSLOOKUPERROR_PATH_TOO_SHORT;
3594 return NULL;
3595}
3596
3597
3598/**
3599 * Internal lookup worker that looks up a KFSOBJ for the given UTF-16 path with
3600 * length and hash.
3601 *
3602 * This will first try the hash table. If not in the hash table, the file
3603 * system cache tree is walked, missing bits filled in and finally a hash table
3604 * entry is created.
3605 *
3606 * Only drive letter paths are cachable. We don't do any UNC paths at this
3607 * point.
3608 *
3609 * @returns Reference to object corresponding to @a pwcPath on success, this
3610 * must be released by kFsCacheObjRelease.
3611 * NULL if not a path we care to cache.
3612 * @param pCache The cache.
3613 * @param pwcPath The path to lookup.
3614 * @param cwcPath The length of the path (in wchar_t's).
3615 * @param uHashPath The hash of the path.
3616 * @param penmError Where to return details as to why the lookup
3617 * failed.
3618 */
3619static PKFSOBJ kFsCacheLookupHashedW(PKFSCACHE pCache, const wchar_t *pwcPath, KU32 cwcPath, KU32 uHashPath,
3620 KFSLOOKUPERROR *penmError)
3621{
3622 /*
3623 * Do hash table lookup of the path.
3624 */
3625 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3626 PKFSHASHW pHashEntry = pCache->apUtf16Paths[idxHashTab];
3627 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3628 if (pHashEntry)
3629 {
3630 do
3631 {
3632 if ( pHashEntry->uHashPath == uHashPath
3633 && pHashEntry->cwcPath == cwcPath
3634 && kHlpMemComp(pHashEntry->pwszPath, pwcPath, cwcPath) == 0)
3635 {
3636 PKFSOBJ pFsObj;
3637 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3638 || pHashEntry->uCacheGen == ((pFsObj = pHashEntry->pFsObj) != NULL
3639 ? pFsObj->bObjType != KFSOBJ_TYPE_MISSING
3640 ? pCache->auGenerations[ pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3641 : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3642 : pCache->auGenerationsMissing[pHashEntry->idxMissingGen])
3643 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) )
3644 {
3645 pCache->cLookups++;
3646 pCache->cPathHashHits++;
3647 KFSCACHE_LOG2(("kFsCacheLookupW(%*.*ls) - hit %p\n", cwcPath, cwcPath, pwcPath, pHashEntry->pFsObj));
3648 *penmError = pHashEntry->enmError;
3649 if (pHashEntry->pFsObj)
3650 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3651 return NULL;
3652 }
3653 break;
3654 }
3655 pHashEntry = pHashEntry->pNext;
3656 } while (pHashEntry);
3657 }
3658
3659 /*
3660 * Create an entry for it by walking the file system cache and filling in the blanks.
3661 */
3662 if ( cwcPath > 0
3663 && cwcPath < KFSCACHE_CFG_MAX_PATH)
3664 {
3665 PKFSOBJ pFsObj;
3666 KBOOL fAbsolute;
3667 PKFSOBJ pLastAncestor = NULL;
3668
3669 /* Is absolute without any '..' bits? */
3670 if ( cwcPath >= 3
3671 && ( ( pwcPath[1] == ':' /* Drive letter */
3672 && IS_SLASH(pwcPath[2])
3673 && IS_ALPHA(pwcPath[0]) )
3674 || ( IS_SLASH(pwcPath[0]) /* UNC */
3675 && IS_SLASH(pwcPath[1]) ) )
3676 && !kFsCacheHasDotDotW(pwcPath, cwcPath) )
3677 {
3678 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3679 fAbsolute = K_TRUE;
3680 }
3681 else
3682 {
3683 pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3684 fAbsolute = K_FALSE;
3685 }
3686 if ( pFsObj
3687 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3688 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3689 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3690 kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwcPath, cwcPath, uHashPath, idxHashTab, fAbsolute,
3691 pLastAncestor ? pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN : 0, *penmError);
3692 if (pLastAncestor)
3693 kFsCacheObjRelease(pCache, pLastAncestor);
3694
3695 pCache->cLookups++;
3696 if (pFsObj)
3697 pCache->cWalkHits++;
3698 return pFsObj;
3699 }
3700
3701 *penmError = cwcPath > 0 ? KFSLOOKUPERROR_PATH_TOO_LONG : KFSLOOKUPERROR_PATH_TOO_SHORT;
3702 return NULL;
3703}
3704
3705
3706
3707/**
3708 * Looks up a KFSOBJ for the given ANSI path.
3709 *
3710 * This will first try the hash table. If not in the hash table, the file
3711 * system cache tree is walked, missing bits filled in and finally a hash table
3712 * entry is created.
3713 *
3714 * Only drive letter paths are cachable. We don't do any UNC paths at this
3715 * point.
3716 *
3717 * @returns Reference to object corresponding to @a pszPath on success, this
3718 * must be released by kFsCacheObjRelease.
3719 * NULL if not a path we care to cache.
3720 * @param pCache The cache.
3721 * @param pszPath The path to lookup.
3722 * @param penmError Where to return details as to why the lookup
3723 * failed.
3724 */
3725PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3726{
3727 KU32 uHashPath;
3728 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
3729 PKFSOBJ pObj;
3730 KFSCACHE_LOCK(pCache);
3731 pObj = kFsCacheLookupHashedA(pCache, pszPath, cchPath, uHashPath, penmError);
3732 KFSCACHE_UNLOCK(pCache);
3733 return pObj;
3734}
3735
3736
3737/**
3738 * Looks up a KFSOBJ for the given UTF-16 path.
3739 *
3740 * This will first try the hash table. If not in the hash table, the file
3741 * system cache tree is walked, missing bits filled in and finally a hash table
3742 * entry is created.
3743 *
3744 * Only drive letter paths are cachable. We don't do any UNC paths at this
3745 * point.
3746 *
3747 * @returns Reference to object corresponding to @a pwszPath on success, this
3748 * must be released by kFsCacheObjRelease.
3749 * NULL if not a path we care to cache.
3750 * @param pCache The cache.
3751 * @param pwszPath The path to lookup.
3752 * @param penmError Where to return details as to why the lookup
3753 * failed.
3754 */
3755PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3756{
3757 KU32 uHashPath;
3758 KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath);
3759 PKFSOBJ pObj;
3760 KFSCACHE_LOCK(pCache);
3761 pObj = kFsCacheLookupHashedW(pCache, pwszPath, cwcPath, uHashPath, penmError);
3762 KFSCACHE_UNLOCK(pCache);
3763 return pObj;
3764}
3765
3766
3767/**
3768 * Looks up a KFSOBJ for the given ANSI path.
3769 *
3770 * This will first try the hash table. If not in the hash table, the file
3771 * system cache tree is walked, missing bits filled in and finally a hash table
3772 * entry is created.
3773 *
3774 * Only drive letter paths are cachable. We don't do any UNC paths at this
3775 * point.
3776 *
3777 * @returns Reference to object corresponding to @a pchPath on success, this
3778 * must be released by kFsCacheObjRelease.
3779 * NULL if not a path we care to cache.
3780 * @param pCache The cache.
3781 * @param pchPath The path to lookup (does not need to be nul
3782 * terminated).
3783 * @param cchPath The path length.
3784 * @param penmError Where to return details as to why the lookup
3785 * failed.
3786 */
3787PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError)
3788{
3789 KU32 uHashPath = kFsCacheStrHashN(pchPath, cchPath);
3790 PKFSOBJ pObj;
3791 KFSCACHE_LOCK(pCache);
3792 pObj = kFsCacheLookupHashedA(pCache, pchPath, (KU32)cchPath, uHashPath, penmError);
3793 KFSCACHE_UNLOCK(pCache);
3794 return pObj;
3795}
3796
3797
3798/**
3799 * Looks up a KFSOBJ for the given UTF-16 path.
3800 *
3801 * This will first try the hash table. If not in the hash table, the file
3802 * system cache tree is walked, missing bits filled in and finally a hash table
3803 * entry is created.
3804 *
3805 * Only drive letter paths are cachable. We don't do any UNC paths at this
3806 * point.
3807 *
3808 * @returns Reference to object corresponding to @a pwchPath on success, this
3809 * must be released by kFsCacheObjRelease.
3810 * NULL if not a path we care to cache.
3811 * @param pCache The cache.
3812 * @param pwcPath The path to lookup (does not need to be nul
3813 * terminated).
3814 * @param cwcPath The path length (in wchar_t's).
3815 * @param penmError Where to return details as to why the lookup
3816 * failed.
3817 */
3818PKFSOBJ kFsCacheLookupWithLengthW(PKFSCACHE pCache, const wchar_t *pwcPath, KSIZE cwcPath, KFSLOOKUPERROR *penmError)
3819{
3820 KU32 uHashPath = kFsCacheUtf16HashN(pwcPath, cwcPath);
3821 PKFSOBJ pObj;
3822 KFSCACHE_LOCK(pCache);
3823 pObj = kFsCacheLookupHashedW(pCache, pwcPath, (KU32)cwcPath, uHashPath, penmError);
3824 KFSCACHE_UNLOCK(pCache);
3825 return pObj;
3826}
3827
3828
3829/**
3830 * Wrapper around kFsCacheLookupA that drops KFSOBJ_TYPE_MISSING and returns
3831 * KFSLOOKUPERROR_NOT_FOUND instead.
3832 *
3833 * @returns Reference to object corresponding to @a pszPath on success, this
3834 * must be released by kFsCacheObjRelease.
3835 * NULL if not a path we care to cache.
3836 * @param pCache The cache.
3837 * @param pszPath The path to lookup.
3838 * @param penmError Where to return details as to why the lookup
3839 * failed.
3840 */
3841PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3842{
3843 PKFSOBJ pObj;
3844 KFSCACHE_LOCK(pCache); /* probably not necessary */
3845 pObj = kFsCacheLookupA(pCache, pszPath, penmError);
3846 if (pObj)
3847 {
3848 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3849 {
3850 KFSCACHE_UNLOCK(pCache);
3851 return pObj;
3852 }
3853
3854 kFsCacheObjRelease(pCache, pObj);
3855 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3856 }
3857 KFSCACHE_UNLOCK(pCache);
3858 return NULL;
3859}
3860
3861
3862/**
3863 * Wrapper around kFsCacheLookupW that drops KFSOBJ_TYPE_MISSING and returns
3864 * KFSLOOKUPERROR_NOT_FOUND instead.
3865 *
3866 * @returns Reference to object corresponding to @a pszPath on success, this
3867 * must be released by kFsCacheObjRelease.
3868 * NULL if not a path we care to cache.
3869 * @param pCache The cache.
3870 * @param pwszPath The path to lookup.
3871 * @param penmError Where to return details as to why the lookup
3872 * failed.
3873 */
3874PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3875{
3876 PKFSOBJ pObj;
3877 KFSCACHE_LOCK(pCache); /* probably not necessary */
3878 pObj = kFsCacheLookupW(pCache, pwszPath, penmError);
3879 if (pObj)
3880 {
3881 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3882 {
3883 KFSCACHE_UNLOCK(pCache);
3884 return pObj;
3885 }
3886
3887 kFsCacheObjRelease(pCache, pObj);
3888 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3889 }
3890 KFSCACHE_UNLOCK(pCache);
3891 return NULL;
3892}
3893
3894
3895/**
3896 * Destroys a cache object which has a zero reference count.
3897 *
3898 * @returns 0
3899 * @param pCache The cache.
3900 * @param pObj The object.
3901 * @param pszWhere Where it was released from.
3902 */
3903KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere)
3904{
3905 kHlpAssert(pObj->cRefs == 0);
3906 kHlpAssert(pObj->pParent == NULL);
3907 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3908 KFSCACHE_LOCK(pCache);
3909
3910 KFSCACHE_LOG(("Destroying %s/%s, type=%d, pObj=%p, pszWhere=%s\n",
3911 pObj->pParent ? pObj->pParent->Obj.pszName : "", pObj->pszName, pObj->bObjType, pObj, pszWhere));
3912 if (pObj->cPathHashRefs != 0)
3913 {
3914 fprintf(stderr, "Destroying %s/%s, type=%d, path hash entries: %d!\n", pObj->pParent ? pObj->pParent->Obj.pszName : "",
3915 pObj->pszName, pObj->bObjType, pObj->cPathHashRefs);
3916 fflush(stderr);
3917 __debugbreak();
3918 }
3919
3920 /*
3921 * Invalidate the structure.
3922 */
3923 pObj->u32Magic = ~KFSOBJ_MAGIC;
3924
3925 /*
3926 * Destroy any user data first.
3927 */
3928 while (pObj->pUserDataHead != NULL)
3929 {
3930 PKFSUSERDATA pUserData = pObj->pUserDataHead;
3931 pObj->pUserDataHead = pUserData->pNext;
3932 if (pUserData->pfnDestructor)
3933 pUserData->pfnDestructor(pCache, pObj, pUserData);
3934 kHlpFree(pUserData);
3935 }
3936
3937 /*
3938 * Do type specific destruction
3939 */
3940 switch (pObj->bObjType)
3941 {
3942 case KFSOBJ_TYPE_MISSING:
3943 /* nothing else to do here */
3944 pCache->cbObjects -= sizeof(KFSDIR);
3945 break;
3946
3947 case KFSOBJ_TYPE_DIR:
3948 {
3949 PKFSDIR pDir = (PKFSDIR)pObj;
3950 KU32 cChildren = pDir->cChildren;
3951 pCache->cbObjects -= sizeof(*pDir)
3952 + K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
3953 + (pDir->fHashTabMask + !!pDir->fHashTabMask) * sizeof(pDir->papHashTab[0]);
3954
3955 pDir->cChildren = 0;
3956 while (cChildren-- > 0)
3957 kFsCacheObjRelease(pCache, pDir->papChildren[cChildren]);
3958 kHlpFree(pDir->papChildren);
3959 pDir->papChildren = NULL;
3960
3961 kHlpFree(pDir->papHashTab);
3962 pDir->papHashTab = NULL;
3963 break;
3964 }
3965
3966 case KFSOBJ_TYPE_FILE:
3967 case KFSOBJ_TYPE_OTHER:
3968 pCache->cbObjects -= sizeof(*pObj);
3969 break;
3970
3971 default:
3972 KFSCACHE_UNLOCK(pCache);
3973 return 0;
3974 }
3975
3976 /*
3977 * Common bits.
3978 */
3979 pCache->cbObjects -= pObj->cchName + 1;
3980#ifdef KFSCACHE_CFG_UTF16
3981 pCache->cbObjects -= (pObj->cwcName + 1) * sizeof(wchar_t);
3982#endif
3983#ifdef KFSCACHE_CFG_SHORT_NAMES
3984 if (pObj->pszName != pObj->pszShortName)
3985 {
3986 pCache->cbObjects -= pObj->cchShortName + 1;
3987# ifdef KFSCACHE_CFG_UTF16
3988 pCache->cbObjects -= (pObj->cwcShortName + 1) * sizeof(wchar_t);
3989# endif
3990 }
3991#endif
3992 pCache->cObjects--;
3993
3994 if (pObj->pNameAlloc)
3995 {
3996 pCache->cbObjects -= pObj->pNameAlloc->cb;
3997 kHlpFree(pObj->pNameAlloc);
3998 }
3999
4000 KFSCACHE_UNLOCK(pCache);
4001
4002 kHlpFree(pObj);
4003 return 0;
4004}
4005
4006
4007/**
4008 * Releases a reference to a cache object.
4009 *
4010 * @returns New reference count.
4011 * @param pCache The cache.
4012 * @param pObj The object.
4013 */
4014#undef kFsCacheObjRelease
4015KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
4016{
4017 if (pObj)
4018 {
4019 KU32 cRefs;
4020 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
4021 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4022
4023 cRefs = _InterlockedDecrement(&pObj->cRefs);
4024 if (cRefs)
4025 return cRefs;
4026 return kFsCacheObjDestroy(pCache, pObj, "kFsCacheObjRelease");
4027 }
4028 return 0;
4029}
4030
4031
4032/**
4033 * Debug version of kFsCacheObjRelease
4034 *
4035 * @returns New reference count.
4036 * @param pCache The cache.
4037 * @param pObj The object.
4038 * @param pszWhere Where it's invoked from.
4039 */
4040KU32 kFsCacheObjReleaseTagged(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere)
4041{
4042 if (pObj)
4043 {
4044 KU32 cRefs;
4045 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
4046 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4047
4048 cRefs = _InterlockedDecrement(&pObj->cRefs);
4049 if (cRefs)
4050 return cRefs;
4051 return kFsCacheObjDestroy(pCache, pObj, pszWhere);
4052 }
4053 return 0;
4054}
4055
4056
4057/**
4058 * Retains a reference to a cahce object.
4059 *
4060 * @returns New reference count.
4061 * @param pObj The object.
4062 */
4063KU32 kFsCacheObjRetain(PKFSOBJ pObj)
4064{
4065 KU32 cRefs;
4066 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
4067 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4068
4069 cRefs = _InterlockedIncrement(&pObj->cRefs);
4070 kHlpAssert(cRefs < 16384);
4071 return cRefs;
4072}
4073
4074
4075/**
4076 * Associates an item of user data with the given object.
4077 *
4078 * If the data needs cleaning up before being free, set the
4079 * PKFSUSERDATA::pfnDestructor member of the returned structure.
4080 *
4081 * @returns Pointer to the user data on success.
4082 * NULL if out of memory or key already in use.
4083 *
4084 * @param pCache The cache.
4085 * @param pObj The object.
4086 * @param uKey The user data key.
4087 * @param cbUserData The size of the user data.
4088 */
4089PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData)
4090{
4091 kHlpAssert(cbUserData >= sizeof(*pNew));
4092 KFSCACHE_OBJUSERDATA_LOCK(pCache, pObj);
4093
4094 if (kFsCacheObjGetUserData(pCache, pObj, uKey) == NULL)
4095 {
4096 PKFSUSERDATA pNew = (PKFSUSERDATA)kHlpAllocZ(cbUserData);
4097 if (pNew)
4098 {
4099 pNew->uKey = uKey;
4100 pNew->pfnDestructor = NULL;
4101 pNew->pNext = pObj->pUserDataHead;
4102 pObj->pUserDataHead = pNew;
4103 KFSCACHE_OBJUSERDATA_UNLOCK(pCache, pObj);
4104 return pNew;
4105 }
4106 }
4107
4108 KFSCACHE_OBJUSERDATA_UNLOCK(pCache, pObj);
4109 return NULL;
4110}
4111
4112
4113/**
4114 * Retrieves an item of user data associated with the given object.
4115 *
4116 * @returns Pointer to the associated user data if found, otherwise NULL.
4117 * @param pCache The cache.
4118 * @param pObj The object.
4119 * @param uKey The user data key.
4120 */
4121PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey)
4122{
4123 PKFSUSERDATA pCur;
4124
4125 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
4126 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4127 KFSCACHE_OBJUSERDATA_LOCK(pCache, pObj);
4128
4129 for (pCur = pObj->pUserDataHead; pCur; pCur = pCur->pNext)
4130 if (pCur->uKey == uKey)
4131 {
4132 KFSCACHE_OBJUSERDATA_UNLOCK(pCache, pObj);
4133 return pCur;
4134 }
4135
4136 KFSCACHE_OBJUSERDATA_UNLOCK(pCache, pObj);
4137 return NULL;
4138}
4139
4140
4141/**
4142 * Determins the idxUserDataLock value.
4143 *
4144 * Called by KFSCACHE_OBJUSERDATA_LOCK when idxUserDataLock is set to KU8_MAX.
4145 *
4146 * @returns The proper idxUserDataLock value.
4147 * @param pCache The cache.
4148 * @param pObj The object.
4149 */
4150KU8 kFsCacheObjGetUserDataLockIndex(PKFSCACHE pCache, PKFSOBJ pObj)
4151{
4152 KU8 idxUserDataLock = pObj->idxUserDataLock;
4153 if (idxUserDataLock == KU8_MAX)
4154 {
4155 KFSCACHE_LOCK(pCache);
4156 idxUserDataLock = pObj->idxUserDataLock;
4157 if (idxUserDataLock == KU8_MAX)
4158 {
4159 idxUserDataLock = pCache->idxUserDataNext++;
4160 idxUserDataLock %= K_ELEMENTS(pCache->auUserDataLocks);
4161 pObj->idxUserDataLock = idxUserDataLock;
4162 }
4163 KFSCACHE_UNLOCK(pCache);
4164 }
4165 return idxUserDataLock;
4166}
4167
4168/**
4169 * Gets the full path to @a pObj, ANSI version.
4170 *
4171 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4172 * @param pObj The object to get the full path to.
4173 * @param pszPath Where to return the path
4174 * @param cbPath The size of the output buffer.
4175 * @param chSlash The slash to use.
4176 */
4177KBOOL kFsCacheObjGetFullPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
4178{
4179 /** @todo No way of to do locking here w/o pCache parameter; need to verify
4180 * that we're only access static data! */
4181 KSIZE off = pObj->cchParent;
4182 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4183 if (off > 0)
4184 {
4185 KSIZE offEnd = off + pObj->cchName;
4186 if (offEnd < cbPath)
4187 {
4188 PKFSDIR pAncestor;
4189
4190 pszPath[off + pObj->cchName] = '\0';
4191 memcpy(&pszPath[off], pObj->pszName, pObj->cchName);
4192
4193 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4194 {
4195 kHlpAssert(off > 1);
4196 kHlpAssert(pAncestor != NULL);
4197 kHlpAssert(pAncestor->Obj.cchName > 0);
4198 pszPath[--off] = chSlash;
4199 off -= pAncestor->Obj.cchName;
4200 kHlpAssert(pAncestor->Obj.cchParent == off);
4201 memcpy(&pszPath[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4202 }
4203 return K_TRUE;
4204 }
4205 }
4206 else
4207 {
4208 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
4209 off = pObj->cchName;
4210 if (off + fDriveLetter < cbPath)
4211 {
4212 memcpy(pszPath, pObj->pszName, off);
4213 if (fDriveLetter)
4214 pszPath[off++] = chSlash;
4215 pszPath[off] = '\0';
4216 return K_TRUE;
4217 }
4218 }
4219
4220 return K_FALSE;
4221}
4222
4223
4224/**
4225 * Gets the full path to @a pObj, UTF-16 version.
4226 *
4227 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4228 * @param pObj The object to get the full path to.
4229 * @param pszPath Where to return the path
4230 * @param cbPath The size of the output buffer.
4231 * @param wcSlash The slash to use.
4232 */
4233KBOOL kFsCacheObjGetFullPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
4234{
4235 /** @todo No way of to do locking here w/o pCache parameter; need to verify
4236 * that we're only access static data! */
4237 KSIZE off = pObj->cwcParent;
4238 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4239 if (off > 0)
4240 {
4241 KSIZE offEnd = off + pObj->cwcName;
4242 if (offEnd < cwcPath)
4243 {
4244 PKFSDIR pAncestor;
4245
4246 pwszPath[off + pObj->cwcName] = '\0';
4247 memcpy(&pwszPath[off], pObj->pwszName, pObj->cwcName * sizeof(wchar_t));
4248
4249 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4250 {
4251 kHlpAssert(off > 1);
4252 kHlpAssert(pAncestor != NULL);
4253 kHlpAssert(pAncestor->Obj.cwcName > 0);
4254 pwszPath[--off] = wcSlash;
4255 off -= pAncestor->Obj.cwcName;
4256 kHlpAssert(pAncestor->Obj.cwcParent == off);
4257 memcpy(&pwszPath[off], pAncestor->Obj.pwszName, pAncestor->Obj.cwcName * sizeof(wchar_t));
4258 }
4259 return K_TRUE;
4260 }
4261 }
4262 else
4263 {
4264 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
4265 off = pObj->cwcName;
4266 if (off + fDriveLetter < cwcPath)
4267 {
4268 memcpy(pwszPath, pObj->pwszName, off * sizeof(wchar_t));
4269 if (fDriveLetter)
4270 pwszPath[off++] = wcSlash;
4271 pwszPath[off] = '\0';
4272 return K_TRUE;
4273 }
4274 }
4275
4276 return K_FALSE;
4277}
4278
4279
4280#ifdef KFSCACHE_CFG_SHORT_NAMES
4281
4282/**
4283 * Gets the full short path to @a pObj, ANSI version.
4284 *
4285 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4286 * @param pObj The object to get the full path to.
4287 * @param pszPath Where to return the path
4288 * @param cbPath The size of the output buffer.
4289 * @param chSlash The slash to use.
4290 */
4291KBOOL kFsCacheObjGetFullShortPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
4292{
4293 /** @todo No way of to do locking here w/o pCache parameter; need to verify
4294 * that we're only access static data! */
4295 KSIZE off = pObj->cchShortParent;
4296 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4297 if (off > 0)
4298 {
4299 KSIZE offEnd = off + pObj->cchShortName;
4300 if (offEnd < cbPath)
4301 {
4302 PKFSDIR pAncestor;
4303
4304 pszPath[off + pObj->cchShortName] = '\0';
4305 memcpy(&pszPath[off], pObj->pszShortName, pObj->cchShortName);
4306
4307 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4308 {
4309 kHlpAssert(off > 1);
4310 kHlpAssert(pAncestor != NULL);
4311 kHlpAssert(pAncestor->Obj.cchShortName > 0);
4312 pszPath[--off] = chSlash;
4313 off -= pAncestor->Obj.cchShortName;
4314 kHlpAssert(pAncestor->Obj.cchShortParent == off);
4315 memcpy(&pszPath[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
4316 }
4317 return K_TRUE;
4318 }
4319 }
4320 else
4321 {
4322 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
4323 off = pObj->cchShortName;
4324 if (off + fDriveLetter < cbPath)
4325 {
4326 memcpy(pszPath, pObj->pszShortName, off);
4327 if (fDriveLetter)
4328 pszPath[off++] = chSlash;
4329 pszPath[off] = '\0';
4330 return K_TRUE;
4331 }
4332 }
4333
4334 return K_FALSE;
4335}
4336
4337
4338/**
4339 * Gets the full short path to @a pObj, UTF-16 version.
4340 *
4341 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4342 * @param pObj The object to get the full path to.
4343 * @param pszPath Where to return the path
4344 * @param cbPath The size of the output buffer.
4345 * @param wcSlash The slash to use.
4346 */
4347KBOOL kFsCacheObjGetFullShortPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
4348{
4349 /** @todo No way of to do locking here w/o pCache parameter; need to verify
4350 * that we're only access static data! */
4351 KSIZE off = pObj->cwcShortParent;
4352 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4353 if (off > 0)
4354 {
4355 KSIZE offEnd = off + pObj->cwcShortName;
4356 if (offEnd < cwcPath)
4357 {
4358 PKFSDIR pAncestor;
4359
4360 pwszPath[off + pObj->cwcShortName] = '\0';
4361 memcpy(&pwszPath[off], pObj->pwszShortName, pObj->cwcShortName * sizeof(wchar_t));
4362
4363 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4364 {
4365 kHlpAssert(off > 1);
4366 kHlpAssert(pAncestor != NULL);
4367 kHlpAssert(pAncestor->Obj.cwcShortName > 0);
4368 pwszPath[--off] = wcSlash;
4369 off -= pAncestor->Obj.cwcShortName;
4370 kHlpAssert(pAncestor->Obj.cwcShortParent == off);
4371 memcpy(&pwszPath[off], pAncestor->Obj.pwszShortName, pAncestor->Obj.cwcShortName * sizeof(wchar_t));
4372 }
4373 return K_TRUE;
4374 }
4375 }
4376 else
4377 {
4378 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
4379 off = pObj->cwcShortName;
4380 if (off + fDriveLetter < cwcPath)
4381 {
4382 memcpy(pwszPath, pObj->pwszShortName, off * sizeof(wchar_t));
4383 if (fDriveLetter)
4384 pwszPath[off++] = wcSlash;
4385 pwszPath[off] = '\0';
4386 return K_TRUE;
4387 }
4388 }
4389
4390 return K_FALSE;
4391}
4392
4393#endif /* KFSCACHE_CFG_SHORT_NAMES */
4394
4395
4396
4397/**
4398 * Read the specified bits from the files into the given buffer, simple version.
4399 *
4400 * @returns K_TRUE on success (all requested bytes read),
4401 * K_FALSE on any kind of failure.
4402 *
4403 * @param pCache The cache.
4404 * @param pFileObj The file object.
4405 * @param offStart Where to start reading.
4406 * @param pvBuf Where to store what we read.
4407 * @param cbToRead How much to read (exact).
4408 */
4409KBOOL kFsCacheFileSimpleOpenReadClose(PKFSCACHE pCache, PKFSOBJ pFileObj, KU64 offStart, void *pvBuf, KSIZE cbToRead)
4410{
4411 /*
4412 * Open the file relative to the parent directory.
4413 */
4414 MY_NTSTATUS rcNt;
4415 HANDLE hFile;
4416 MY_IO_STATUS_BLOCK Ios;
4417 MY_OBJECT_ATTRIBUTES ObjAttr;
4418 MY_UNICODE_STRING UniStr;
4419
4420 kHlpAssertReturn(pFileObj->bObjType == KFSOBJ_TYPE_FILE, K_FALSE);
4421 kHlpAssert(pFileObj->pParent);
4422 kHlpAssertReturn(pFileObj->pParent->hDir != INVALID_HANDLE_VALUE, K_FALSE);
4423 kHlpAssertReturn(offStart == 0, K_FALSE); /** @todo when needed */
4424
4425 Ios.Information = -1;
4426 Ios.u.Status = -1;
4427
4428 UniStr.Buffer = (wchar_t *)pFileObj->pwszName;
4429 UniStr.Length = (USHORT)(pFileObj->cwcName * sizeof(wchar_t));
4430 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
4431
4432/** @todo potential race against kFsCacheInvalidateDeletedDirectoryA */
4433 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFileObj->pParent->hDir, NULL /*pSecAttr*/);
4434
4435 rcNt = g_pfnNtCreateFile(&hFile,
4436 GENERIC_READ | SYNCHRONIZE,
4437 &ObjAttr,
4438 &Ios,
4439 NULL, /*cbFileInitialAlloc */
4440 FILE_ATTRIBUTE_NORMAL,
4441 FILE_SHARE_READ,
4442 FILE_OPEN,
4443 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
4444 NULL, /*pEaBuffer*/
4445 0); /*cbEaBuffer*/
4446 if (MY_NT_SUCCESS(rcNt))
4447 {
4448 LARGE_INTEGER offFile;
4449 offFile.QuadPart = offStart;
4450
4451 Ios.Information = -1;
4452 Ios.u.Status = -1;
4453 rcNt = g_pfnNtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApcComplete*/, NULL /*pvApcCtx*/, &Ios,
4454 pvBuf, (KU32)cbToRead, !offStart ? &offFile : NULL, NULL /*puKey*/);
4455 if (MY_NT_SUCCESS(rcNt))
4456 rcNt = Ios.u.Status;
4457 if (MY_NT_SUCCESS(rcNt))
4458 {
4459 if (Ios.Information == cbToRead)
4460 {
4461 g_pfnNtClose(hFile);
4462 return K_TRUE;
4463 }
4464 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': Information=%p\n", pFileObj->pwszName, Ios.Information));
4465 }
4466 else
4467 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': %#x\n", pFileObj->pwszName, rcNt));
4468 g_pfnNtClose(hFile);
4469 }
4470 else
4471 KFSCACHE_LOG(("Error opening '%ls' for caching: %#x\n", pFileObj->pwszName, rcNt));
4472 return K_FALSE;
4473}
4474
4475
4476/**
4477 * Invalidate all cache entries of missing files.
4478 *
4479 * @param pCache The cache.
4480 */
4481void kFsCacheInvalidateMissing(PKFSCACHE pCache)
4482{
4483 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4484 KFSCACHE_LOCK(pCache);
4485
4486 pCache->auGenerationsMissing[0]++;
4487 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
4488
4489 KFSCACHE_LOG(("Invalidate missing %#x\n", pCache->auGenerationsMissing[0]));
4490 KFSCACHE_UNLOCK(pCache);
4491}
4492
4493
4494/**
4495 * Recursively close directories.
4496 */
4497static void kFsCacheCloseDirs(PKFSOBJ *papChildren, KU32 cChildren)
4498{
4499 while (cChildren-- > 0)
4500 {
4501 PKFSDIR pDir = (PKFSDIR)papChildren[cChildren];
4502 if (pDir && pDir->Obj.bObjType == KFSOBJ_TYPE_DIR)
4503 {
4504 if (pDir->hDir != INVALID_HANDLE_VALUE)
4505 {
4506 g_pfnNtClose(pDir->hDir);
4507 pDir->hDir = INVALID_HANDLE_VALUE;
4508 }
4509 kFsCacheCloseDirs(pDir->papChildren, pDir->cChildren);
4510 }
4511 }
4512}
4513
4514
4515/**
4516 * Worker for kFsCacheInvalidateAll and kFsCacheInvalidateAllAndCloseDirs
4517 */
4518static void kFsCacheInvalidateAllWorker(PKFSCACHE pCache, KBOOL fCloseDirs, KBOOL fIncludingRoot)
4519{
4520 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4521 KFSCACHE_LOCK(pCache);
4522
4523 pCache->auGenerationsMissing[0]++;
4524 kHlpAssert(pCache->auGenerationsMissing[0] < KU32_MAX);
4525 pCache->auGenerationsMissing[1]++;
4526 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4527
4528 pCache->auGenerations[0]++;
4529 kHlpAssert(pCache->auGenerations[0] < KU32_MAX);
4530 pCache->auGenerations[1]++;
4531 kHlpAssert(pCache->auGenerations[1] < KU32_MAX);
4532
4533 if (fCloseDirs)
4534 {
4535 kFsCacheCloseDirs(pCache->RootDir.papChildren, pCache->RootDir.cChildren);
4536 if (fCloseDirs && pCache->RootDir.hDir != INVALID_HANDLE_VALUE)
4537 {
4538 g_pfnNtClose(pCache->RootDir.hDir);
4539 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
4540 }
4541 }
4542
4543 KFSCACHE_LOG(("Invalidate all - default: %#x/%#x, custom: %#x/%#x\n",
4544 pCache->auGenerationsMissing[0], pCache->auGenerations[0],
4545 pCache->auGenerationsMissing[1], pCache->auGenerations[1]));
4546 KFSCACHE_UNLOCK(pCache);
4547}
4548
4549
4550/**
4551 * Invalidate all cache entries (regular, custom & missing).
4552 *
4553 * @param pCache The cache.
4554 */
4555void kFsCacheInvalidateAll(PKFSCACHE pCache)
4556{
4557 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4558 kFsCacheInvalidateAllWorker(pCache, K_FALSE, K_FALSE);
4559}
4560
4561
4562/**
4563 * Invalidate all cache entries (regular, custom & missing) and close all the
4564 * directory handles.
4565 *
4566 * @param pCache The cache.
4567 * @param fIncludingRoot Close the root directory handle too.
4568 */
4569void kFsCacheInvalidateAllAndCloseDirs(PKFSCACHE pCache, KBOOL fIncludingRoot)
4570{
4571 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4572 kFsCacheInvalidateAllWorker(pCache, K_TRUE, fIncludingRoot);
4573}
4574
4575
4576/**
4577 * Invalidate all cache entries with custom generation handling set.
4578 *
4579 * @see kFsCacheSetupCustomRevisionForTree, KFSOBJ_F_USE_CUSTOM_GEN
4580 * @param pCache The cache.
4581 */
4582void kFsCacheInvalidateCustomMissing(PKFSCACHE pCache)
4583{
4584 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4585 KFSCACHE_LOCK(pCache);
4586
4587 pCache->auGenerationsMissing[1]++;
4588 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4589
4590 KFSCACHE_LOG(("Invalidate missing custom %#x\n", pCache->auGenerationsMissing[1]));
4591 KFSCACHE_UNLOCK(pCache);
4592}
4593
4594
4595/**
4596 * Invalidate all cache entries with custom generation handling set, both
4597 * missing and regular present entries.
4598 *
4599 * @see kFsCacheSetupCustomRevisionForTree, KFSOBJ_F_USE_CUSTOM_GEN
4600 * @param pCache The cache.
4601 */
4602void kFsCacheInvalidateCustomBoth(PKFSCACHE pCache)
4603{
4604 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4605 KFSCACHE_LOCK(pCache);
4606
4607 pCache->auGenerations[1]++;
4608 kHlpAssert(pCache->auGenerations[1] < KU32_MAX);
4609 pCache->auGenerationsMissing[1]++;
4610 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4611
4612 KFSCACHE_LOG(("Invalidate both custom %#x/%#x\n", pCache->auGenerationsMissing[1], pCache->auGenerations[1]));
4613 KFSCACHE_UNLOCK(pCache);
4614}
4615
4616
4617
4618/**
4619 * Applies the given flags to all the objects in a tree.
4620 *
4621 * @param pRoot Where to start applying the flag changes.
4622 * @param fAndMask The AND mask.
4623 * @param fOrMask The OR mask.
4624 */
4625static void kFsCacheApplyFlagsToTree(PKFSDIR pRoot, KU32 fAndMask, KU32 fOrMask)
4626{
4627 PKFSOBJ *ppCur = ((PKFSDIR)pRoot)->papChildren;
4628 KU32 cLeft = ((PKFSDIR)pRoot)->cChildren;
4629 while (cLeft-- > 0)
4630 {
4631 PKFSOBJ pCur = *ppCur++;
4632 if (pCur->bObjType != KFSOBJ_TYPE_DIR)
4633 pCur->fFlags = (fAndMask & pCur->fFlags) | fOrMask;
4634 else
4635 kFsCacheApplyFlagsToTree((PKFSDIR)pCur, fAndMask, fOrMask);
4636 }
4637
4638 pRoot->Obj.fFlags = (fAndMask & pRoot->Obj.fFlags) | fOrMask;
4639}
4640
4641
4642/**
4643 * Sets up using custom revisioning for the specified directory tree or file.
4644 *
4645 * There are some restrictions of the current implementation:
4646 * - If the root of the sub-tree is ever deleted from the cache (i.e.
4647 * deleted in real life and reflected in the cache), the setting is lost.
4648 * - It is not automatically applied to the lookup paths caches.
4649 *
4650 * @returns K_TRUE on success, K_FALSE on failure.
4651 * @param pCache The cache.
4652 * @param pRoot The root of the subtree. A non-directory is
4653 * fine, like a missing node.
4654 */
4655KBOOL kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot)
4656{
4657 if (pRoot)
4658 {
4659 KFSCACHE_LOCK(pCache);
4660 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
4661 kFsCacheApplyFlagsToTree((PKFSDIR)pRoot, KU32_MAX, KFSOBJ_F_USE_CUSTOM_GEN);
4662 else
4663 pRoot->fFlags |= KFSOBJ_F_USE_CUSTOM_GEN;
4664 KFSCACHE_UNLOCK(pCache);
4665 return K_TRUE;
4666 }
4667 return K_FALSE;
4668}
4669
4670
4671/**
4672 * Invalidates a deleted directory, ANSI version.
4673 *
4674 * @returns K_TRUE if found and is a non-root directory. Otherwise K_FALSE.
4675 * @param pCache The cache.
4676 * @param pszDir The directory.
4677 */
4678KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir)
4679{
4680 KU32 cchDir = (KU32)kHlpStrLen(pszDir);
4681 KFSLOOKUPERROR enmError;
4682 PKFSOBJ pFsObj;
4683
4684 KFSCACHE_LOCK(pCache);
4685
4686 /* Is absolute without any '..' bits? */
4687 if ( cchDir >= 3
4688 && ( ( pszDir[1] == ':' /* Drive letter */
4689 && IS_SLASH(pszDir[2])
4690 && IS_ALPHA(pszDir[0]) )
4691 || ( IS_SLASH(pszDir[0]) /* UNC */
4692 && IS_SLASH(pszDir[1]) ) )
4693 && !kFsCacheHasDotDotA(pszDir, cchDir) )
4694 pFsObj = kFsCacheLookupAbsoluteA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
4695 &enmError, NULL);
4696 else
4697 pFsObj = kFsCacheLookupSlowA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
4698 &enmError, NULL);
4699 if (pFsObj)
4700 {
4701 /* Is directory? */
4702 if (pFsObj->bObjType == KFSOBJ_TYPE_DIR)
4703 {
4704 if (pFsObj->pParent != &pCache->RootDir)
4705 {
4706 PKFSDIR pDir = (PKFSDIR)pFsObj;
4707 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: %s hDir=%p\n", pszDir, pDir->hDir));
4708 if (pDir->hDir != INVALID_HANDLE_VALUE)
4709 {
4710 g_pfnNtClose(pDir->hDir);
4711 pDir->hDir = INVALID_HANDLE_VALUE;
4712 }
4713 pDir->fNeedRePopulating = K_TRUE;
4714 pDir->Obj.uCacheGen = pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN] - 1;
4715 kFsCacheObjRelease(pCache, &pDir->Obj);
4716 KFSCACHE_UNLOCK(pCache);
4717 return K_TRUE;
4718 }
4719 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a root directory was deleted! %s\n", pszDir));
4720 }
4721 else
4722 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a non-directory: bObjType=%d %s\n",
4723 pFsObj->bObjType, pszDir));
4724 kFsCacheObjRelease(pCache, pFsObj);
4725 }
4726 else
4727 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: '%s' was not found\n", pszDir));
4728 KFSCACHE_UNLOCK(pCache);
4729 return K_FALSE;
4730}
4731
4732
4733PKFSCACHE kFsCacheCreate(KU32 fFlags)
4734{
4735 PKFSCACHE pCache;
4736 birdResolveImports();
4737
4738 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
4739 if (pCache)
4740 {
4741 /* Dummy root dir entry. */
4742 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC;
4743 pCache->RootDir.Obj.cRefs = 1;
4744 pCache->RootDir.Obj.uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
4745 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR;
4746 pCache->RootDir.Obj.fHaveStats = K_FALSE;
4747 pCache->RootDir.Obj.pParent = NULL;
4748 pCache->RootDir.Obj.pszName = "";
4749 pCache->RootDir.Obj.cchName = 0;
4750 pCache->RootDir.Obj.cchParent = 0;
4751#ifdef KFSCACHE_CFG_UTF16
4752 pCache->RootDir.Obj.cwcName = 0;
4753 pCache->RootDir.Obj.cwcParent = 0;
4754 pCache->RootDir.Obj.pwszName = L"";
4755#endif
4756
4757#ifdef KFSCACHE_CFG_SHORT_NAMES
4758 pCache->RootDir.Obj.pszShortName = NULL;
4759 pCache->RootDir.Obj.cchShortName = 0;
4760 pCache->RootDir.Obj.cchShortParent = 0;
4761# ifdef KFSCACHE_CFG_UTF16
4762 pCache->RootDir.Obj.cwcShortName;
4763 pCache->RootDir.Obj.cwcShortParent;
4764 pCache->RootDir.Obj.pwszShortName;
4765# endif
4766#endif
4767 pCache->RootDir.cChildren = 0;
4768 pCache->RootDir.cChildrenAllocated = 0;
4769 pCache->RootDir.papChildren = NULL;
4770 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
4771 pCache->RootDir.fHashTabMask = 255; /* 256: 26 drive letters and 102 UNCs before we're half ways. */
4772 pCache->RootDir.papHashTab = (PKFSOBJ *)kHlpAllocZ(256 * sizeof(pCache->RootDir.papHashTab[0]));
4773 if (pCache->RootDir.papHashTab)
4774 {
4775 /* The cache itself. */
4776 pCache->u32Magic = KFSCACHE_MAGIC;
4777 pCache->fFlags = fFlags;
4778 pCache->auGenerations[0] = KU32_MAX / 4;
4779 pCache->auGenerations[1] = KU32_MAX / 32;
4780 pCache->auGenerationsMissing[0] = KU32_MAX / 256;
4781 pCache->auGenerationsMissing[1] = 1;
4782 pCache->cObjects = 1;
4783 pCache->cbObjects = sizeof(pCache->RootDir)
4784 + (pCache->RootDir.fHashTabMask + 1) * sizeof(pCache->RootDir.papHashTab[0]);
4785 pCache->cPathHashHits = 0;
4786 pCache->cWalkHits = 0;
4787 pCache->cChildSearches = 0;
4788 pCache->cChildHashHits = 0;
4789 pCache->cChildHashed = 0;
4790 pCache->cChildHashTabs = 1;
4791 pCache->cChildHashEntriesTotal = pCache->RootDir.fHashTabMask + 1;
4792 pCache->cChildHashCollisions = 0;
4793 pCache->cNameChanges = 0;
4794 pCache->cNameGrowths = 0;
4795 pCache->cAnsiPaths = 0;
4796 pCache->cAnsiPathCollisions = 0;
4797 pCache->cbAnsiPaths = 0;
4798#ifdef KFSCACHE_CFG_UTF16
4799 pCache->cUtf16Paths = 0;
4800 pCache->cUtf16PathCollisions = 0;
4801 pCache->cbUtf16Paths = 0;
4802#endif
4803
4804#ifdef KFSCACHE_CFG_LOCKING
4805 {
4806 KSIZE idx = K_ELEMENTS(pCache->auUserDataLocks);
4807 while (idx-- > 0)
4808 InitializeCriticalSection(&pCache->auUserDataLocks[idx].CritSect);
4809 InitializeCriticalSection(&pCache->u.CritSect);
4810 }
4811#endif
4812 return pCache;
4813 }
4814
4815 kHlpFree(pCache);
4816 }
4817 return NULL;
4818}
4819
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