source: trunk/src/lib/nt/kFsCache.c@ 2948

Last change on this file since 2948 was 2948, checked in by bird, 9 years ago

kWorker/kDep: save a few header stat calls while optimizing dependencies.

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