VirtualBox

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

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

kFsCache: Implemented object names changing to a longer ones.

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

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