VirtualBox

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

Last change on this file since 2989 was 2985, checked in by bird, 8 years ago

lib/nt: Got fts-nt halfways working, quite a few NT interface changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 167.9 KB
Line 
1/* $Id: kFsCache.c 2985 2016-11-01 18:26:35Z 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 fflush(stderr);
1985 __debugbreak();
1986 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1987 /** @todo implement as needed. */
1988 }
1989
1990 pObj->Stats.st_size = uBuf.WithId.EndOfFile.QuadPart;
1991 birdNtTimeToTimeSpec(uBuf.WithId.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1992 birdNtTimeToTimeSpec(uBuf.WithId.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1993 birdNtTimeToTimeSpec(uBuf.WithId.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1994 birdNtTimeToTimeSpec(uBuf.WithId.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1995 pObj->Stats.st_attribs = uBuf.WithId.FileAttributes;
1996 pObj->Stats.st_blksize = 65536;
1997 pObj->Stats.st_blocks = (uBuf.WithId.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1998 / BIRD_STAT_BLOCK_SIZE;
1999 }
2000#endif
2001 if (MY_NT_SUCCESS(rcNt))
2002 {
2003 pObj->uCacheGen = pCache->auGenerations[pObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
2004 fRc = K_TRUE;
2005 }
2006 else
2007 {
2008 /* ouch! */
2009 kHlpAssertMsgFailed(("%#x\n", rcNt));
2010 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on non-dir - not implemented!\n", rcNt);
2011 __debugbreak();
2012 fRc = K_FALSE;
2013 }
2014 }
2015 else
2016 {
2017 /*
2018 * An open directory. Query information via the handle, the
2019 * file ID shouldn't have been able to change, so we can use
2020 * NtQueryInformationFile. Right...
2021 */
2022 PKFSDIR pDir = (PKFSDIR)pObj;
2023 Ios.Information = -1;
2024 Ios.u.Status = -1;
2025 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &uBuf.FullInfo, sizeof(uBuf.FullInfo),
2026 MyFileNetworkOpenInformation);
2027 if (MY_NT_SUCCESS(rcNt))
2028 rcNt = Ios.u.Status;
2029 if (MY_NT_SUCCESS(rcNt))
2030 {
2031 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
2032 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
2033 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
2034 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
2035 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
2036 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
2037 pObj->Stats.st_blksize = 65536;
2038 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
2039 / BIRD_STAT_BLOCK_SIZE;
2040
2041 if ( pDir->iLastWrite == uBuf.FullInfo.LastWriteTime.QuadPart
2042 && (pObj->fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
2043 KFSCACHE_LOG(("Refreshing %s/%s/ - no re-populating necessary.\n",
2044 pObj->pParent->Obj.pszName, pObj->pszName));
2045 else
2046 {
2047 KFSCACHE_LOG(("Refreshing %s/%s/ - needs re-populating...\n",
2048 pObj->pParent->Obj.pszName, pObj->pszName));
2049 pDir->fNeedRePopulating = K_TRUE;
2050#if 0
2051 /* Refresh the link count. */
2052 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
2053 if (MY_NT_SUCCESS(rcNt))
2054 rcNt = Ios.s.Status;
2055 if (MY_NT_SUCCESS(rcNt))
2056 pObj->Stats.st_nlink = StdInfo.NumberOfLinks;
2057#endif
2058 }
2059 }
2060 if (MY_NT_SUCCESS(rcNt))
2061 {
2062 pObj->uCacheGen = pCache->auGenerations[pObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
2063 fRc = K_TRUE;
2064 }
2065 else
2066 {
2067 /* ouch! */
2068 kHlpAssertMsgFailed(("%#x\n", rcNt));
2069 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on dir - not implemented!\n", rcNt);
2070 fflush(stderr);
2071 __debugbreak();
2072 fRc = K_FALSE;
2073 }
2074 }
2075 }
2076
2077 return fRc;
2078}
2079
2080
2081
2082/**
2083 * Looks up a drive letter.
2084 *
2085 * Will enter the drive if necessary.
2086 *
2087 * @returns Pointer to the root directory of the drive or an update-to-date
2088 * missing node.
2089 * @param pCache The cache.
2090 * @param chLetter The uppercased drive letter.
2091 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2092 * @param penmError Where to return details as to why the lookup
2093 * failed.
2094 */
2095static PKFSOBJ kFsCacheLookupDrive(PKFSCACHE pCache, char chLetter, KU32 fFlags, KFSLOOKUPERROR *penmError)
2096{
2097 KU32 const uNameHash = chLetter - 'A';
2098 PKFSOBJ pCur = pCache->RootDir.papHashTab[uNameHash];
2099
2100 KU32 cLeft;
2101 PKFSOBJ *ppCur;
2102 MY_UNICODE_STRING NtPath;
2103 wchar_t wszTmp[8];
2104 char szTmp[4];
2105
2106 /*
2107 * Custom drive letter hashing.
2108 */
2109 kHlpAssert((uNameHash & pCache->RootDir.fHashTabMask) == uNameHash);
2110 while (pCur)
2111 {
2112 if ( pCur->uNameHash == uNameHash
2113 && pCur->cchName == 2
2114 && pCur->pszName[0] == chLetter
2115 && pCur->pszName[1] == ':')
2116 {
2117 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
2118 return pCur;
2119 if ( (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2120 || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
2121 return pCur;
2122 return NULL;
2123 }
2124 pCur = pCur->pNextNameHash;
2125 }
2126
2127 /*
2128 * Make 100% sure it's not there.
2129 */
2130 cLeft = pCache->RootDir.cChildren;
2131 ppCur = pCache->RootDir.papChildren;
2132 while (cLeft-- > 0)
2133 {
2134 pCur = *ppCur++;
2135 if ( pCur->cchName == 2
2136 && pCur->pszName[0] == chLetter
2137 && pCur->pszName[1] == ':')
2138 {
2139 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
2140 return pCur;
2141 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
2142 if ( (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2143 || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
2144 return pCur;
2145 return NULL;
2146 }
2147 }
2148
2149 if (fFlags & KFSCACHE_LOOKUP_F_NO_INSERT)
2150 {
2151 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; /* close enough */
2152 return NULL;
2153 }
2154
2155 /*
2156 * Need to add it. We always keep the drive letters open for the benefit
2157 * of kFsCachePopuplateOrRefreshDir and others.
2158 */
2159 wszTmp[0] = szTmp[0] = chLetter;
2160 wszTmp[1] = szTmp[1] = ':';
2161 wszTmp[2] = szTmp[2] = '\\';
2162 wszTmp[3] = '.';
2163 wszTmp[4] = '\0';
2164 szTmp[2] = '\0';
2165
2166 NtPath.Buffer = NULL;
2167 NtPath.Length = 0;
2168 NtPath.MaximumLength = 0;
2169 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
2170 {
2171 HANDLE hDir;
2172 MY_NTSTATUS rcNt;
2173 rcNt = birdOpenFileUniStr(NULL /*hRoot*/,
2174 &NtPath,
2175 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
2176 FILE_ATTRIBUTE_NORMAL,
2177 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2178 FILE_OPEN,
2179 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
2180 OBJ_CASE_INSENSITIVE,
2181 &hDir);
2182 birdFreeNtPath(&NtPath);
2183 if (MY_NT_SUCCESS(rcNt))
2184 {
2185 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
2186#ifdef KFSCACHE_CFG_SHORT_NAMES
2187 NULL, 0, NULL, 0,
2188#endif
2189 KFSOBJ_TYPE_DIR, penmError);
2190 if (pDir)
2191 {
2192 /*
2193 * We need a little bit of extra info for a drive root. These things are typically
2194 * inherited by subdirectories down the tree, so, we do it all here for till that changes.
2195 */
2196 union
2197 {
2198 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
2199 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
2200 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
2201 } uBuf;
2202 MY_IO_STATUS_BLOCK Ios;
2203 KBOOL fRc;
2204
2205 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
2206 pDir->hDir = hDir;
2207
2208 if (birdStatHandle(hDir, &pDir->Obj.Stats, pDir->Obj.pszName) == 0)
2209 {
2210 pDir->Obj.fHaveStats = K_TRUE;
2211 pDir->uDevNo = pDir->Obj.Stats.st_dev;
2212 }
2213 else
2214 {
2215 /* Just in case. */
2216 pDir->Obj.fHaveStats = K_FALSE;
2217 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
2218 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
2219 }
2220
2221 /* Get the file system. */
2222 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
2223 Ios.Information = -1;
2224 Ios.u.Status = -1;
2225 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
2226 MyFileFsAttributeInformation);
2227 if (MY_NT_SUCCESS(rcNt))
2228 rcNt = Ios.u.Status;
2229 if (MY_NT_SUCCESS(rcNt))
2230 {
2231 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N'
2232 && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
2233 && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
2234 && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
2235 && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
2236 {
2237 DWORD dwDriveType = GetDriveTypeW(wszTmp);
2238 if ( dwDriveType == DRIVE_FIXED
2239 || dwDriveType == DRIVE_RAMDISK)
2240 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
2241 }
2242 }
2243
2244 /*
2245 * Link the new drive letter into the root dir.
2246 */
2247 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
2248 kFsCacheObjRelease(pCache, &pDir->Obj);
2249 if (fRc)
2250 {
2251 pDir->Obj.pNextNameHash = pCache->RootDir.papHashTab[uNameHash];
2252 pCache->RootDir.papHashTab[uNameHash] = &pDir->Obj;
2253 return &pDir->Obj;
2254 }
2255 return NULL;
2256 }
2257
2258 g_pfnNtClose(hDir);
2259 return NULL;
2260 }
2261
2262 /* Assume it doesn't exist if this happens... This may be a little to
2263 restrictive wrt status code checks. */
2264 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
2265 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
2266 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
2267 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
2268 ("%#x\n", rcNt),
2269 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
2270 NULL);
2271 }
2272 else
2273 {
2274 kHlpAssertFailed();
2275 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
2276 return NULL;
2277 }
2278
2279 /*
2280 * Maybe create a missing entry.
2281 */
2282 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2283 {
2284 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
2285#ifdef KFSCACHE_CFG_SHORT_NAMES
2286 NULL, 0, NULL, 0,
2287#endif
2288 KFSOBJ_TYPE_MISSING, penmError);
2289 if (pMissing)
2290 {
2291 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
2292 kFsCacheObjRelease(pCache, pMissing);
2293 return fRc ? pMissing : NULL;
2294 }
2295 }
2296 else
2297 {
2298 /** @todo this isn't necessary correct for a root spec. */
2299 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2300 }
2301 return NULL;
2302}
2303
2304
2305/**
2306 * Slow path that allocates the child hash table and enters the given one.
2307 *
2308 * Allocation fialures are ignored.
2309 *
2310 * @param pCache The cache (for stats).
2311 * @param pDir The directory.
2312 * @param uNameHash The name hash to enter @a pChild under.
2313 * @param pChild The child to enter into the hash table.
2314 */
2315static void kFsCacheDirAllocHashTabAndEnterChild(PKFSCACHE pCache, PKFSDIR pDir, KU32 uNameHash, PKFSOBJ pChild)
2316{
2317 if (uNameHash != 0) /* paranoia ^ 4! */
2318 {
2319 /*
2320 * Double the current number of children and round up to a multiple of
2321 * two so we can avoid division.
2322 */
2323 KU32 cbHashTab;
2324 KU32 cEntries;
2325 kHlpAssert(pDir->cChildren > 0);
2326 if (pDir->cChildren <= KU32_MAX / 4)
2327 {
2328#if defined(_MSC_VER) && 1
2329 KU32 cEntriesRaw = pDir->cChildren * 2;
2330 KU32 cEntriesShift;
2331 kHlpAssert(sizeof(cEntries) == (unsigned long));
2332 if (_BitScanReverse(&cEntriesShift, cEntriesRaw))
2333 {
2334 if ( K_BIT32(cEntriesShift) < cEntriesRaw
2335 && cEntriesShift < 31U)
2336 cEntriesShift++;
2337 cEntries = K_BIT32(cEntriesShift);
2338 }
2339 else
2340 {
2341 kHlpAssertFailed();
2342 cEntries = KU32_MAX / 2 + 1;
2343 }
2344#else
2345 cEntries = pDir->cChildren * 2 - 1;
2346 cEntries |= cEntries >> 1;
2347 cEntries |= cEntries >> 2;
2348 cEntries |= cEntries >> 4;
2349 cEntries |= cEntries >> 8;
2350 cEntries |= cEntries >> 16;
2351 cEntries++;
2352#endif
2353 }
2354 else
2355 cEntries = KU32_MAX / 2 + 1;
2356 kHlpAssert((cEntries & (cEntries - 1)) == 0);
2357
2358 cbHashTab = cEntries * sizeof(pDir->papHashTab[0]);
2359 pDir->papHashTab = (PKFSOBJ *)kHlpAllocZ(cbHashTab);
2360 if (pDir->papHashTab)
2361 {
2362 KU32 idx;
2363 pDir->fHashTabMask = cEntries - 1;
2364 pCache->cbObjects += cbHashTab;
2365 pCache->cChildHashTabs++;
2366 pCache->cChildHashEntriesTotal += cEntries;
2367
2368 /*
2369 * Insert it.
2370 */
2371 pChild->uNameHash = uNameHash;
2372 idx = uNameHash & (pDir->fHashTabMask);
2373 pChild->pNextNameHash = pDir->papHashTab[idx];
2374 pDir->papHashTab[idx] = pChild;
2375 pCache->cChildHashed++;
2376 }
2377 }
2378}
2379
2380
2381/**
2382 * Look up a child node, ANSI version.
2383 *
2384 * @returns Pointer to the child if found, NULL if not.
2385 * @param pCache The cache.
2386 * @param pParent The parent directory to search.
2387 * @param pchName The child name to search for (not terminated).
2388 * @param cchName The length of the child name.
2389 */
2390static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
2391{
2392 /*
2393 * Check for '.' first ('..' won't appear).
2394 */
2395 if (cchName != 1 || *pchName != '.')
2396 {
2397 PKFSOBJ *ppCur;
2398 KU32 cLeft;
2399 KU32 uNameHash;
2400
2401 /*
2402 * Do hash table lookup.
2403 *
2404 * This caches previous lookups, which should be useful when looking up
2405 * intermediate directories at least.
2406 */
2407 if (pParent->papHashTab != NULL)
2408 {
2409 PKFSOBJ pCur;
2410 uNameHash = kFsCacheStrHashN(pchName, cchName);
2411 pCur = pParent->papHashTab[uNameHash & pParent->fHashTabMask];
2412 while (pCur)
2413 {
2414 if ( pCur->uNameHash == uNameHash
2415 && ( ( pCur->cchName == cchName
2416 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2417#ifdef KFSCACHE_CFG_SHORT_NAMES
2418 || ( pCur->cchShortName == cchName
2419 && pCur->pszShortName != pCur->pszName
2420 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2421#endif
2422 )
2423 )
2424 {
2425 pCache->cChildHashHits++;
2426 pCache->cChildSearches++;
2427 return pCur;
2428 }
2429 pCur = pCur->pNextNameHash;
2430 }
2431 }
2432 else
2433 uNameHash = 0;
2434
2435 /*
2436 * Do linear search.
2437 */
2438 cLeft = pParent->cChildren;
2439 ppCur = pParent->papChildren;
2440 while (cLeft-- > 0)
2441 {
2442 PKFSOBJ pCur = *ppCur++;
2443 if ( ( pCur->cchName == cchName
2444 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2445#ifdef KFSCACHE_CFG_SHORT_NAMES
2446 || ( pCur->cchShortName == cchName
2447 && pCur->pszShortName != pCur->pszName
2448 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2449#endif
2450 )
2451 {
2452 /*
2453 * Consider entering it into the parent hash table.
2454 * Note! We hash the input, not the name we found.
2455 */
2456 if ( pCur->uNameHash == 0
2457 && pParent->cChildren >= 2)
2458 {
2459 if (pParent->papHashTab)
2460 {
2461 if (uNameHash != 0)
2462 {
2463 KU32 idxNameHash = uNameHash & pParent->fHashTabMask;
2464 pCur->uNameHash = uNameHash;
2465 pCur->pNextNameHash = pParent->papHashTab[idxNameHash];
2466 pParent->papHashTab[idxNameHash] = pCur;
2467 if (pCur->pNextNameHash)
2468 pCache->cChildHashCollisions++;
2469 pCache->cChildHashed++;
2470 }
2471 }
2472 else
2473 kFsCacheDirAllocHashTabAndEnterChild(pCache, pParent, kFsCacheStrHashN(pchName, cchName), pCur);
2474 }
2475
2476 pCache->cChildSearches++;
2477 return pCur;
2478 }
2479 }
2480
2481 pCache->cChildSearches++;
2482 return NULL;
2483 }
2484 return &pParent->Obj;
2485}
2486
2487
2488/**
2489 * Look up a child node, UTF-16 version.
2490 *
2491 * @returns Pointer to the child if found, NULL if not.
2492 * @param pCache The cache.
2493 * @param pParent The parent directory to search.
2494 * @param pwcName The child name to search for (not terminated).
2495 * @param cwcName The length of the child name (in wchar_t's).
2496 */
2497static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
2498{
2499 /*
2500 * Check for '.' first ('..' won't appear).
2501 */
2502 if (cwcName != 1 || *pwcName != '.')
2503 {
2504 PKFSOBJ *ppCur;
2505 KU32 cLeft;
2506 KU32 uNameHash;
2507
2508 /*
2509 * Do hash table lookup.
2510 *
2511 * This caches previous lookups, which should be useful when looking up
2512 * intermediate directories at least.
2513 */
2514 if (pParent->papHashTab != NULL)
2515 {
2516 PKFSOBJ pCur;
2517 uNameHash = kFsCacheUtf16HashN(pwcName, cwcName);
2518 pCur = pParent->papHashTab[uNameHash & pParent->fHashTabMask];
2519 while (pCur)
2520 {
2521 if ( pCur->uNameHash == uNameHash
2522 && ( ( pCur->cwcName == cwcName
2523 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2524#ifdef KFSCACHE_CFG_SHORT_NAMES
2525 || ( pCur->cwcShortName == cwcName
2526 && pCur->pwszShortName != pCur->pwszName
2527 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2528#endif
2529 )
2530 )
2531 {
2532 pCache->cChildHashHits++;
2533 pCache->cChildSearches++;
2534 return pCur;
2535 }
2536 pCur = pCur->pNextNameHash;
2537 }
2538 }
2539 else
2540 uNameHash = 0;
2541
2542 /*
2543 * Do linear search.
2544 */
2545 cLeft = pParent->cChildren;
2546 ppCur = pParent->papChildren;
2547 while (cLeft-- > 0)
2548 {
2549 PKFSOBJ pCur = *ppCur++;
2550 if ( ( pCur->cwcName == cwcName
2551 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2552#ifdef KFSCACHE_CFG_SHORT_NAMES
2553 || ( pCur->cwcShortName == cwcName
2554 && pCur->pwszShortName != pCur->pwszName
2555 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2556#endif
2557 )
2558 {
2559 /*
2560 * Consider entering it into the parent hash table.
2561 * Note! We hash the input, not the name we found.
2562 */
2563 if ( pCur->uNameHash == 0
2564 && pParent->cChildren >= 4)
2565 {
2566 if (pParent->papHashTab)
2567 {
2568 if (uNameHash != 0)
2569 {
2570 KU32 idxNameHash = uNameHash & pParent->fHashTabMask;
2571 pCur->uNameHash = uNameHash;
2572 pCur->pNextNameHash = pParent->papHashTab[idxNameHash];
2573 pParent->papHashTab[idxNameHash] = pCur;
2574 if (pCur->pNextNameHash)
2575 pCache->cChildHashCollisions++;
2576 pCache->cChildHashed++;
2577 }
2578 }
2579 else
2580 kFsCacheDirAllocHashTabAndEnterChild(pCache, pParent, kFsCacheUtf16HashN(pwcName, cwcName), pCur);
2581 }
2582
2583 pCache->cChildSearches++;
2584 return pCur;
2585 }
2586 }
2587 pCache->cChildSearches++;
2588 return NULL;
2589 }
2590 return &pParent->Obj;
2591}
2592
2593
2594/**
2595 * Looks up a UNC share, ANSI version.
2596 *
2597 * We keep both the server and share in the root directory entry. This means we
2598 * have to clean up the entry name before we can insert it.
2599 *
2600 * @returns Pointer to the share root directory or an update-to-date missing
2601 * node.
2602 * @param pCache The cache.
2603 * @param pszPath The path.
2604 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2605 * @param poff Where to return the root dire.
2606 * @param penmError Where to return details as to why the lookup
2607 * failed.
2608 */
2609static PKFSOBJ kFsCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 fFlags,
2610 KU32 *poff, KFSLOOKUPERROR *penmError)
2611{
2612 /*
2613 * Special case: Long path prefix w/ drive letter following it.
2614 * Note! Must've been converted from wide char to ANSI.
2615 */
2616 if ( IS_SLASH(pszPath[0])
2617 && IS_SLASH(pszPath[1])
2618 && pszPath[2] == '?'
2619 && IS_SLASH(pszPath[3])
2620 && IS_ALPHA(pszPath[4])
2621 && pszPath[5] == ':'
2622 && IS_SLASH(pszPath[6]) )
2623 {
2624 *poff = 4 + 2;
2625 return kFsCacheLookupDrive(pCache, pszPath[4], fFlags, penmError);
2626 }
2627
2628#if 0 /* later */
2629 KU32 offStartServer;
2630 KU32 offEndServer;
2631 KU32 offStartShare;
2632
2633 KU32 offEnd = 2;
2634 while (IS_SLASH(pszPath[offEnd]))
2635 offEnd++;
2636
2637 offStartServer = offEnd;
2638 while ( (ch = pszPath[offEnd]) != '\0'
2639 && !IS_SLASH(ch))
2640 offEnd++;
2641 offEndServer = offEnd;
2642
2643 if (ch != '\0')
2644 { /* likely */ }
2645 else
2646 {
2647 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2648 return NULL;
2649 }
2650
2651 while (IS_SLASH(pszPath[offEnd]))
2652 offEnd++;
2653 offStartServer = offEnd;
2654 while ( (ch = pszPath[offEnd]) != '\0'
2655 && !IS_SLASH(ch))
2656 offEnd++;
2657#endif
2658 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2659 return NULL;
2660}
2661
2662
2663/**
2664 * Looks up a UNC share, UTF-16 version.
2665 *
2666 * We keep both the server and share in the root directory entry. This means we
2667 * have to clean up the entry name before we can insert it.
2668 *
2669 * @returns Pointer to the share root directory or an update-to-date missing
2670 * node.
2671 * @param pCache The cache.
2672 * @param pwszPath The path.
2673 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2674 * @param poff Where to return the root dir.
2675 * @param penmError Where to return details as to why the lookup
2676 * failed.
2677 */
2678static PKFSOBJ kFsCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 fFlags,
2679 KU32 *poff, KFSLOOKUPERROR *penmError)
2680{
2681 /*
2682 * Special case: Long path prefix w/ drive letter following it.
2683 */
2684 if ( IS_SLASH(pwszPath[0])
2685 && IS_SLASH(pwszPath[1])
2686 && pwszPath[2] == '?'
2687 && IS_SLASH(pwszPath[3])
2688 && IS_ALPHA(pwszPath[4])
2689 && pwszPath[5] == ':'
2690 && IS_SLASH(pwszPath[6]) )
2691 {
2692 *poff = 4 + 2;
2693 return kFsCacheLookupDrive(pCache, (char)pwszPath[4], fFlags, penmError);
2694 }
2695
2696
2697#if 0 /* later */
2698 KU32 offStartServer;
2699 KU32 offEndServer;
2700 KU32 offStartShare;
2701
2702 KU32 offEnd = 2;
2703 while (IS_SLASH(pwszPath[offEnd]))
2704 offEnd++;
2705
2706 offStartServer = offEnd;
2707 while ( (ch = pwszPath[offEnd]) != '\0'
2708 && !IS_SLASH(ch))
2709 offEnd++;
2710 offEndServer = offEnd;
2711
2712 if (ch != '\0')
2713 { /* likely */ }
2714 else
2715 {
2716 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2717 return NULL;
2718 }
2719
2720 while (IS_SLASH(pwszPath[offEnd]))
2721 offEnd++;
2722 offStartServer = offEnd;
2723 while ( (ch = pwszPath[offEnd]) != '\0'
2724 && !IS_SLASH(ch))
2725 offEnd++;
2726#endif
2727 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2728 return NULL;
2729}
2730
2731
2732/**
2733 * Walks an full path relative to the given directory, ANSI version.
2734 *
2735 * This will create any missing nodes while walking.
2736 *
2737 * The caller will have to do the path hash table insertion of the result.
2738 *
2739 * @returns Pointer to the tree node corresponding to @a pszPath.
2740 * NULL on lookup failure, see @a penmError for details.
2741 * @param pCache The cache.
2742 * @param pParent The directory to start the lookup in.
2743 * @param pszPath The path to walk.
2744 * @param cchPath The length of the path.
2745 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2746 * @param penmError Where to return details as to why the lookup
2747 * failed.
2748 * @param ppLastAncestor Where to return the last parent element found
2749 * (referenced) in case of error an path/file not
2750 * found problem. Optional.
2751 */
2752PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags,
2753 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2754{
2755 /*
2756 * Walk loop.
2757 */
2758 KU32 off = 0;
2759 if (ppLastAncestor)
2760 *ppLastAncestor = NULL;
2761 for (;;)
2762 {
2763 PKFSOBJ pChild;
2764
2765 /*
2766 * Find the end of the component, counting trailing slashes.
2767 */
2768 char ch;
2769 KU32 cchSlashes = 0;
2770 KU32 offEnd = off + 1;
2771 while ((ch = pszPath[offEnd]) != '\0')
2772 {
2773 if (!IS_SLASH(ch))
2774 offEnd++;
2775 else
2776 {
2777 do
2778 cchSlashes++;
2779 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2780 break;
2781 }
2782 }
2783
2784 /*
2785 * Do we need to populate or refresh this directory first?
2786 */
2787 if ( !pParent->fNeedRePopulating
2788 && pParent->fPopulated
2789 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2790 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
2791 { /* likely */ }
2792 else if ( (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
2793 || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2794 { /* likely */ }
2795 else
2796 return NULL;
2797
2798 /*
2799 * Search the current node for the name.
2800 *
2801 * If we don't find it, we may insert a missing node depending on
2802 * the cache configuration.
2803 */
2804 pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
2805 if (pChild != NULL)
2806 { /* probably likely */ }
2807 else
2808 {
2809 if ( (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2810 && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
2811 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
2812 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
2813 {
2814 if (pChild)
2815 return kFsCacheObjRetainInternal(pChild);
2816 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2817 }
2818 else
2819 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2820 if (ppLastAncestor)
2821 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2822 return NULL;
2823 }
2824
2825 /* Advance off and check if we're done already. */
2826 off = offEnd + cchSlashes;
2827 if ( cchSlashes == 0
2828 || off >= cchPath)
2829 {
2830 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2831 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2832 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2833 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2834 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2835 { /* likely */ }
2836 else
2837 return NULL;
2838 return kFsCacheObjRetainInternal(pChild);
2839 }
2840
2841 /*
2842 * Check that it's a directory. If a missing entry, we may have to
2843 * refresh it and re-examin it.
2844 */
2845 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2846 pParent = (PKFSDIR)pChild;
2847 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2848 {
2849 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2850 if (ppLastAncestor)
2851 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2852 return NULL;
2853 }
2854 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2855 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2856 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
2857 {
2858 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2859 if (ppLastAncestor)
2860 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2861 return NULL;
2862 }
2863 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2864 pParent = (PKFSDIR)pChild;
2865 else
2866 {
2867 if (ppLastAncestor)
2868 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2869 return NULL;
2870 }
2871 }
2872
2873 return NULL;
2874
2875}
2876
2877
2878/**
2879 * Walks an full path relative to the given directory, UTF-16 version.
2880 *
2881 * This will create any missing nodes while walking.
2882 *
2883 * The caller will have to do the path hash table insertion of the result.
2884 *
2885 * @returns Pointer to the tree node corresponding to @a pszPath.
2886 * NULL on lookup failure, see @a penmError for details.
2887 * @param pCache The cache.
2888 * @param pParent The directory to start the lookup in.
2889 * @param pszPath The path to walk. No dot-dot bits allowed!
2890 * @param cchPath The length of the path.
2891 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
2892 * @param penmError Where to return details as to why the lookup
2893 * failed.
2894 * @param ppLastAncestor Where to return the last parent element found
2895 * (referenced) in case of error an path/file not
2896 * found problem. Optional.
2897 */
2898PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
2899 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2900{
2901 /*
2902 * Walk loop.
2903 */
2904 KU32 off = 0;
2905 if (ppLastAncestor)
2906 *ppLastAncestor = NULL;
2907 for (;;)
2908 {
2909 PKFSOBJ pChild;
2910
2911 /*
2912 * Find the end of the component, counting trailing slashes.
2913 */
2914 wchar_t wc;
2915 KU32 cwcSlashes = 0;
2916 KU32 offEnd = off + 1;
2917 while ((wc = pwszPath[offEnd]) != '\0')
2918 {
2919 if (!IS_SLASH(wc))
2920 offEnd++;
2921 else
2922 {
2923 do
2924 cwcSlashes++;
2925 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2926 break;
2927 }
2928 }
2929
2930 /*
2931 * Do we need to populate or refresh this directory first?
2932 */
2933 if ( !pParent->fNeedRePopulating
2934 && pParent->fPopulated
2935 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2936 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
2937 { /* likely */ }
2938 else if ( (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
2939 || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2940 { /* likely */ }
2941 else
2942 return NULL;
2943
2944 /*
2945 * Search the current node for the name.
2946 *
2947 * If we don't find it, we may insert a missing node depending on
2948 * the cache configuration.
2949 */
2950 pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off);
2951 if (pChild != NULL)
2952 { /* probably likely */ }
2953 else
2954 {
2955 if ( (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2956 && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
2957 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
2958 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
2959 {
2960 if (pChild)
2961 return kFsCacheObjRetainInternal(pChild);
2962 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2963 }
2964 else
2965 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2966 if (ppLastAncestor)
2967 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2968 return NULL;
2969 }
2970
2971 /* Advance off and check if we're done already. */
2972 off = offEnd + cwcSlashes;
2973 if ( cwcSlashes == 0
2974 || off >= cwcPath)
2975 {
2976 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2977 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2978 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2979 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
2980 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2981 { /* likely */ }
2982 else
2983 return NULL;
2984 return kFsCacheObjRetainInternal(pChild);
2985 }
2986
2987 /*
2988 * Check that it's a directory. If a missing entry, we may have to
2989 * refresh it and re-examin it.
2990 */
2991 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2992 pParent = (PKFSDIR)pChild;
2993 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2994 {
2995 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2996 if (ppLastAncestor)
2997 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2998 return NULL;
2999 }
3000 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3001 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3002 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) )
3003
3004 {
3005 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
3006 if (ppLastAncestor)
3007 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3008 return NULL;
3009 }
3010 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
3011 pParent = (PKFSDIR)pChild;
3012 else
3013 {
3014 if (ppLastAncestor)
3015 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
3016 return NULL;
3017 }
3018 }
3019
3020 return NULL;
3021
3022}
3023
3024/**
3025 * Walk the file system tree for the given absolute path, entering it into the
3026 * hash table.
3027 *
3028 * This will create any missing nodes while walking.
3029 *
3030 * The caller will have to do the path hash table insertion of the result.
3031 *
3032 * @returns Pointer to the tree node corresponding to @a pszPath.
3033 * NULL on lookup failure, see @a penmError for details.
3034 * @param pCache The cache.
3035 * @param pszPath The path to walk. No dot-dot bits allowed!
3036 * @param cchPath The length of the path.
3037 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3038 * @param penmError Where to return details as to why the lookup
3039 * failed.
3040 * @param ppLastAncestor Where to return the last parent element found
3041 * (referenced) in case of error an path/file not
3042 * found problem. Optional.
3043 */
3044static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
3045 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3046{
3047 PKFSOBJ pRoot;
3048 KU32 cchSlashes;
3049 KU32 offEnd;
3050
3051 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
3052
3053 /*
3054 * The root "directory" needs special handling, so we keep it outside the
3055 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
3056 */
3057 cchSlashes = 0;
3058 if ( pszPath[1] == ':'
3059 && IS_ALPHA(pszPath[0]))
3060 {
3061 /* Drive letter. */
3062 offEnd = 2;
3063 kHlpAssert(IS_SLASH(pszPath[2]));
3064 pRoot = kFsCacheLookupDrive(pCache, toupper(pszPath[0]), fFlags, penmError);
3065 }
3066 else if ( IS_SLASH(pszPath[0])
3067 && IS_SLASH(pszPath[1]) )
3068 pRoot = kFsCacheLookupUncShareA(pCache, pszPath, fFlags, &offEnd, penmError);
3069 else
3070 {
3071 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
3072 return NULL;
3073 }
3074 if (pRoot)
3075 { /* likely */ }
3076 else
3077 return NULL;
3078
3079 /* Count slashes trailing the root spec. */
3080 if (offEnd < cchPath)
3081 {
3082 kHlpAssert(IS_SLASH(pszPath[offEnd]));
3083 do
3084 cchSlashes++;
3085 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
3086 }
3087
3088 /* Done already? */
3089 if (offEnd >= cchPath)
3090 {
3091 if ( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3092 || pRoot->uCacheGen == ( pRoot->bObjType != KFSOBJ_TYPE_MISSING
3093 ? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3094 : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
3095 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
3096 || kFsCacheRefreshObj(pCache, pRoot, penmError))
3097 return kFsCacheObjRetainInternal(pRoot);
3098 return NULL;
3099 }
3100
3101 /* Check that we've got a valid result and not a cached negative one. */
3102 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
3103 { /* likely */ }
3104 else
3105 {
3106 kHlpAssert(pRoot->bObjType == KFSOBJ_TYPE_MISSING);
3107 kHlpAssert( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3108 || pRoot->uCacheGen == pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]);
3109 return pRoot;
3110 }
3111
3112 /*
3113 * Now that we've found a valid root directory, lookup the
3114 * remainder of the path starting with it.
3115 */
3116 return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pRoot, &pszPath[offEnd + cchSlashes],
3117 cchPath - offEnd - cchSlashes, fFlags, penmError, ppLastAncestor);
3118}
3119
3120
3121/**
3122 * Walk the file system tree for the given absolute path, UTF-16 version.
3123 *
3124 * This will create any missing nodes while walking.
3125 *
3126 * The caller will have to do the path hash table insertion of the result.
3127 *
3128 * @returns Pointer to the tree node corresponding to @a pszPath.
3129 * NULL on lookup failure, see @a penmError for details.
3130 * @param pCache The cache.
3131 * @param pwszPath The path to walk.
3132 * @param cwcPath The length of the path (in wchar_t's).
3133 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3134 * @param penmError Where to return details as to why the lookup
3135 * failed.
3136 * @param ppLastAncestor Where to return the last parent element found
3137 * (referenced) in case of error an path/file not
3138 * found problem. Optional.
3139 */
3140static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
3141 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3142{
3143 PKFSDIR pParent = &pCache->RootDir;
3144 PKFSOBJ pRoot;
3145 KU32 off;
3146 KU32 cwcSlashes;
3147 KU32 offEnd;
3148
3149 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath));
3150
3151 /*
3152 * The root "directory" needs special handling, so we keep it outside the
3153 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
3154 */
3155 cwcSlashes = 0;
3156 off = 0;
3157 if ( pwszPath[1] == ':'
3158 && IS_ALPHA(pwszPath[0]))
3159 {
3160 /* Drive letter. */
3161 offEnd = 2;
3162 kHlpAssert(IS_SLASH(pwszPath[2]));
3163 pRoot = kFsCacheLookupDrive(pCache, toupper(pwszPath[0]), fFlags, penmError);
3164 }
3165 else if ( IS_SLASH(pwszPath[0])
3166 && IS_SLASH(pwszPath[1]) )
3167 pRoot = kFsCacheLookupUncShareW(pCache, pwszPath, fFlags, &offEnd, penmError);
3168 else
3169 {
3170 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
3171 return NULL;
3172 }
3173 if (pRoot)
3174 { /* likely */ }
3175 else
3176 return NULL;
3177
3178 /* Count slashes trailing the root spec. */
3179 if (offEnd < cwcPath)
3180 {
3181 kHlpAssert(IS_SLASH(pwszPath[offEnd]));
3182 do
3183 cwcSlashes++;
3184 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
3185 }
3186
3187 /* Done already? */
3188 if (offEnd >= cwcPath)
3189 {
3190 if ( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3191 || pRoot->uCacheGen == (pRoot->bObjType != KFSOBJ_TYPE_MISSING
3192 ? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3193 : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
3194 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
3195 || kFsCacheRefreshObj(pCache, pRoot, penmError))
3196 return kFsCacheObjRetainInternal(pRoot);
3197 return NULL;
3198 }
3199
3200 /* Check that we've got a valid result and not a cached negative one. */
3201 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
3202 { /* likely */ }
3203 else
3204 {
3205 kHlpAssert(pRoot->bObjType == KFSOBJ_TYPE_MISSING);
3206 kHlpAssert( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3207 || pRoot->uCacheGen == pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]);
3208 return pRoot;
3209 }
3210
3211 /*
3212 * Now that we've found a valid root directory, lookup the
3213 * remainder of the path starting with it.
3214 */
3215 return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pRoot, &pwszPath[offEnd + cwcSlashes],
3216 cwcPath - offEnd - cwcSlashes, fFlags, penmError, ppLastAncestor);
3217}
3218
3219
3220/**
3221 * This deals with paths that are relative and paths that contains '..'
3222 * elements, ANSI version.
3223 *
3224 * @returns Pointer to object corresponding to @a pszPath on success.
3225 * NULL if this isn't a path we care to cache.
3226 *
3227 * @param pCache The cache.
3228 * @param pszPath The path.
3229 * @param cchPath The length of the path.
3230 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3231 * @param penmError Where to return details as to why the lookup
3232 * failed.
3233 * @param ppLastAncestor Where to return the last parent element found
3234 * (referenced) in case of error an path/file not
3235 * found problem. Optional.
3236 */
3237static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
3238 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3239{
3240 /*
3241 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
3242 * ends up calling it anyway.
3243 */
3244 char szFull[KFSCACHE_CFG_MAX_PATH];
3245 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
3246 if ( cchFull >= 3
3247 && cchFull < sizeof(szFull))
3248 {
3249 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath));
3250 return kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, fFlags, penmError, ppLastAncestor);
3251 }
3252
3253 /* The path is too long! */
3254 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
3255 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3256 return NULL;
3257}
3258
3259
3260/**
3261 * This deals with paths that are relative and paths that contains '..'
3262 * elements, UTF-16 version.
3263 *
3264 * @returns Pointer to object corresponding to @a pszPath on success.
3265 * NULL if this isn't a path we care to cache.
3266 *
3267 * @param pCache The cache.
3268 * @param pwszPath The path.
3269 * @param cwcPath The length of the path (in wchar_t's).
3270 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
3271 * @param penmError Where to return details as to why the lookup
3272 * failed.
3273 * @param ppLastAncestor Where to return the last parent element found
3274 * (referenced) in case of error an path/file not
3275 * found problem. Optional.
3276 */
3277static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KU32 fFlags,
3278 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
3279{
3280 /*
3281 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
3282 * ends up calling it anyway.
3283 */
3284 wchar_t wszFull[KFSCACHE_CFG_MAX_PATH];
3285 UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL);
3286 if ( cwcFull >= 3
3287 && cwcFull < KFSCACHE_CFG_MAX_PATH)
3288 {
3289 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
3290 return kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, fFlags, penmError, ppLastAncestor);
3291 }
3292
3293 /* The path is too long! */
3294 kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull));
3295 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3296 return NULL;
3297}
3298
3299
3300/**
3301 * Refreshes a path hash that has expired, ANSI version.
3302 *
3303 * @returns pHash on success, NULL if removed.
3304 * @param pCache The cache.
3305 * @param pHashEntry The path hash.
3306 * @param idxHashTab The hash table entry.
3307 */
3308static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
3309{
3310 PKFSOBJ pLastAncestor = NULL;
3311 if (!pHashEntry->pFsObj)
3312 {
3313 if (pHashEntry->fAbsolute)
3314 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3315 &pHashEntry->enmError, &pLastAncestor);
3316 else
3317 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3318 &pHashEntry->enmError, &pLastAncestor);
3319 }
3320 else
3321 {
3322 KU8 bOldType = pHashEntry->pFsObj->bObjType;
3323 KFSLOOKUPERROR enmError;
3324 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
3325 {
3326 if (pHashEntry->pFsObj->bObjType == bOldType)
3327 { }
3328 else
3329 {
3330 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
3331 if (pHashEntry->fAbsolute)
3332 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3333 &pHashEntry->enmError, &pLastAncestor);
3334 else
3335 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
3336 &pHashEntry->enmError, &pLastAncestor);
3337 }
3338 }
3339 else
3340 {
3341 fprintf(stderr, "kFsCacheRefreshPathA - refresh failure handling not implemented!\n");
3342 __debugbreak();
3343 /** @todo just remove this entry. */
3344 return NULL;
3345 }
3346 }
3347
3348 if (pLastAncestor && !pHashEntry->pFsObj)
3349 pHashEntry->idxMissingGen = pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN;
3350 pHashEntry->uCacheGen = !pHashEntry->pFsObj
3351 ? pCache->auGenerationsMissing[pHashEntry->idxMissingGen]
3352 : pHashEntry->pFsObj->bObjType == KFSOBJ_TYPE_MISSING
3353 ? pCache->auGenerationsMissing[pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3354 : pCache->auGenerations[ pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
3355 if (pLastAncestor)
3356 kFsCacheObjRelease(pCache, pLastAncestor);
3357 return pHashEntry;
3358}
3359
3360
3361/**
3362 * Refreshes a path hash that has expired, UTF-16 version.
3363 *
3364 * @returns pHash on success, NULL if removed.
3365 * @param pCache The cache.
3366 * @param pHashEntry The path hash.
3367 * @param idxHashTab The hash table entry.
3368 */
3369static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab)
3370{
3371 PKFSOBJ pLastAncestor = NULL;
3372 if (!pHashEntry->pFsObj)
3373 {
3374 if (pHashEntry->fAbsolute)
3375 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3376 &pHashEntry->enmError, &pLastAncestor);
3377 else
3378 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3379 &pHashEntry->enmError, &pLastAncestor);
3380 }
3381 else
3382 {
3383 KU8 bOldType = pHashEntry->pFsObj->bObjType;
3384 KFSLOOKUPERROR enmError;
3385 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
3386 {
3387 if (pHashEntry->pFsObj->bObjType == bOldType)
3388 { }
3389 else
3390 {
3391 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
3392 if (pHashEntry->fAbsolute)
3393 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3394 &pHashEntry->enmError, &pLastAncestor);
3395 else
3396 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
3397 &pHashEntry->enmError, &pLastAncestor);
3398 }
3399 }
3400 else
3401 {
3402 fprintf(stderr, "kFsCacheRefreshPathW - refresh failure handling not implemented!\n");
3403 fflush(stderr);
3404 __debugbreak();
3405 /** @todo just remove this entry. */
3406 return NULL;
3407 }
3408 }
3409 if (pLastAncestor && !pHashEntry->pFsObj)
3410 pHashEntry->idxMissingGen = pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN;
3411 pHashEntry->uCacheGen = !pHashEntry->pFsObj
3412 ? pCache->auGenerationsMissing[pHashEntry->idxMissingGen]
3413 : pHashEntry->pFsObj->bObjType == KFSOBJ_TYPE_MISSING
3414 ? pCache->auGenerationsMissing[pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3415 : pCache->auGenerations[ pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
3416 if (pLastAncestor)
3417 kFsCacheObjRelease(pCache, pLastAncestor);
3418 return pHashEntry;
3419}
3420
3421
3422/**
3423 * Internal lookup worker that looks up a KFSOBJ for the given ANSI path with
3424 * length and hash.
3425 *
3426 * This will first try the hash table. If not in the hash table, the file
3427 * system cache tree is walked, missing bits filled in and finally a hash table
3428 * entry is created.
3429 *
3430 * Only drive letter paths are cachable. We don't do any UNC paths at this
3431 * point.
3432 *
3433 * @returns Reference to object corresponding to @a pszPath on success, this
3434 * must be released by kFsCacheObjRelease.
3435 * NULL if not a path we care to cache.
3436 * @param pCache The cache.
3437 * @param pchPath The path to lookup.
3438 * @param cchPath The path length.
3439 * @param uHashPath The hash of the path.
3440 * @param penmError Where to return details as to why the lookup
3441 * failed.
3442 */
3443static PKFSOBJ kFsCacheLookupHashedA(PKFSCACHE pCache, const char *pchPath, KU32 cchPath, KU32 uHashPath,
3444 KFSLOOKUPERROR *penmError)
3445{
3446 /*
3447 * Do hash table lookup of the path.
3448 */
3449 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3450 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab];
3451 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3452 if (pHashEntry)
3453 {
3454 do
3455 {
3456 if ( pHashEntry->uHashPath == uHashPath
3457 && pHashEntry->cchPath == cchPath
3458 && kHlpMemComp(pHashEntry->pszPath, pchPath, cchPath) == 0)
3459 {
3460 PKFSOBJ pFsObj;
3461 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3462 || pHashEntry->uCacheGen == ( (pFsObj = pHashEntry->pFsObj) != NULL
3463 ? pFsObj->bObjType != KFSOBJ_TYPE_MISSING
3464 ? pCache->auGenerations[ pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3465 : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3466 : pCache->auGenerationsMissing[pHashEntry->idxMissingGen])
3467 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
3468 {
3469 pCache->cLookups++;
3470 pCache->cPathHashHits++;
3471 KFSCACHE_LOG2(("kFsCacheLookupA(%*.*s) - hit %p\n", cchPath, cchPath, pchPath, pHashEntry->pFsObj));
3472 *penmError = pHashEntry->enmError;
3473 if (pHashEntry->pFsObj)
3474 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3475 return NULL;
3476 }
3477 break;
3478 }
3479 pHashEntry = pHashEntry->pNext;
3480 } while (pHashEntry);
3481 }
3482
3483 /*
3484 * Create an entry for it by walking the file system cache and filling in the blanks.
3485 */
3486 if ( cchPath > 0
3487 && cchPath < KFSCACHE_CFG_MAX_PATH)
3488 {
3489 PKFSOBJ pFsObj;
3490 KBOOL fAbsolute;
3491 PKFSOBJ pLastAncestor = NULL;
3492
3493 /* Is absolute without any '..' bits? */
3494 if ( cchPath >= 3
3495 && ( ( pchPath[1] == ':' /* Drive letter */
3496 && IS_SLASH(pchPath[2])
3497 && IS_ALPHA(pchPath[0]) )
3498 || ( IS_SLASH(pchPath[0]) /* UNC */
3499 && IS_SLASH(pchPath[1]) ) )
3500 && !kFsCacheHasDotDotA(pchPath, cchPath) )
3501 {
3502 pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3503 fAbsolute = K_TRUE;
3504 }
3505 else
3506 {
3507 pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3508 fAbsolute = K_FALSE;
3509 }
3510 if ( pFsObj
3511 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3512 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3513 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3514 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pchPath, cchPath, uHashPath, idxHashTab, fAbsolute,
3515 pLastAncestor ? pLastAncestor->bObjType & KFSOBJ_F_USE_CUSTOM_GEN : 0, *penmError);
3516 if (pLastAncestor)
3517 kFsCacheObjRelease(pCache, pLastAncestor);
3518
3519 pCache->cLookups++;
3520 if (pFsObj)
3521 pCache->cWalkHits++;
3522 return pFsObj;
3523 }
3524
3525 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3526 return NULL;
3527}
3528
3529
3530/**
3531 * Internal lookup worker that looks up a KFSOBJ for the given UTF-16 path with
3532 * length and hash.
3533 *
3534 * This will first try the hash table. If not in the hash table, the file
3535 * system cache tree is walked, missing bits filled in and finally a hash table
3536 * entry is created.
3537 *
3538 * Only drive letter paths are cachable. We don't do any UNC paths at this
3539 * point.
3540 *
3541 * @returns Reference to object corresponding to @a pwcPath on success, this
3542 * must be released by kFsCacheObjRelease.
3543 * NULL if not a path we care to cache.
3544 * @param pCache The cache.
3545 * @param pwcPath The path to lookup.
3546 * @param cwcPath The length of the path (in wchar_t's).
3547 * @param uHashPath The hash of the path.
3548 * @param penmError Where to return details as to why the lookup
3549 * failed.
3550 */
3551static PKFSOBJ kFsCacheLookupHashedW(PKFSCACHE pCache, const wchar_t *pwcPath, KU32 cwcPath, KU32 uHashPath,
3552 KFSLOOKUPERROR *penmError)
3553{
3554 /*
3555 * Do hash table lookup of the path.
3556 */
3557 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3558 PKFSHASHW pHashEntry = pCache->apUtf16Paths[idxHashTab];
3559 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3560 if (pHashEntry)
3561 {
3562 do
3563 {
3564 if ( pHashEntry->uHashPath == uHashPath
3565 && pHashEntry->cwcPath == cwcPath
3566 && kHlpMemComp(pHashEntry->pwszPath, pwcPath, cwcPath) == 0)
3567 {
3568 PKFSOBJ pFsObj;
3569 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3570 || pHashEntry->uCacheGen == ((pFsObj = pHashEntry->pFsObj) != NULL
3571 ? pFsObj->bObjType != KFSOBJ_TYPE_MISSING
3572 ? pCache->auGenerations[ pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3573 : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3574 : pCache->auGenerationsMissing[pHashEntry->idxMissingGen])
3575 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) )
3576 {
3577 pCache->cLookups++;
3578 pCache->cPathHashHits++;
3579 KFSCACHE_LOG2(("kFsCacheLookupW(%*.*ls) - hit %p\n", cwcPath, cwcPath, pwcPath, pHashEntry->pFsObj));
3580 *penmError = pHashEntry->enmError;
3581 if (pHashEntry->pFsObj)
3582 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3583 return NULL;
3584 }
3585 break;
3586 }
3587 pHashEntry = pHashEntry->pNext;
3588 } while (pHashEntry);
3589 }
3590
3591 /*
3592 * Create an entry for it by walking the file system cache and filling in the blanks.
3593 */
3594 if ( cwcPath > 0
3595 && cwcPath < KFSCACHE_CFG_MAX_PATH)
3596 {
3597 PKFSOBJ pFsObj;
3598 KBOOL fAbsolute;
3599 PKFSOBJ pLastAncestor = NULL;
3600
3601 /* Is absolute without any '..' bits? */
3602 if ( cwcPath >= 3
3603 && ( ( pwcPath[1] == ':' /* Drive letter */
3604 && IS_SLASH(pwcPath[2])
3605 && IS_ALPHA(pwcPath[0]) )
3606 || ( IS_SLASH(pwcPath[0]) /* UNC */
3607 && IS_SLASH(pwcPath[1]) ) )
3608 && !kFsCacheHasDotDotW(pwcPath, cwcPath) )
3609 {
3610 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3611 fAbsolute = K_TRUE;
3612 }
3613 else
3614 {
3615 pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
3616 fAbsolute = K_FALSE;
3617 }
3618 if ( pFsObj
3619 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3620 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3621 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3622 kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwcPath, cwcPath, uHashPath, idxHashTab, fAbsolute,
3623 pLastAncestor ? pLastAncestor->bObjType & KFSOBJ_F_USE_CUSTOM_GEN : 0, *penmError);
3624 if (pLastAncestor)
3625 kFsCacheObjRelease(pCache, pLastAncestor);
3626
3627 pCache->cLookups++;
3628 if (pFsObj)
3629 pCache->cWalkHits++;
3630 return pFsObj;
3631 }
3632
3633 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3634 return NULL;
3635}
3636
3637
3638
3639/**
3640 * Looks up a KFSOBJ for the given ANSI path.
3641 *
3642 * This will first try the hash table. If not in the hash table, the file
3643 * system cache tree is walked, missing bits filled in and finally a hash table
3644 * entry is created.
3645 *
3646 * Only drive letter paths are cachable. We don't do any UNC paths at this
3647 * point.
3648 *
3649 * @returns Reference to object corresponding to @a pszPath on success, this
3650 * must be released by kFsCacheObjRelease.
3651 * NULL if not a path we care to cache.
3652 * @param pCache The cache.
3653 * @param pszPath The path to lookup.
3654 * @param penmError Where to return details as to why the lookup
3655 * failed.
3656 */
3657PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3658{
3659 KU32 uHashPath;
3660 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
3661 return kFsCacheLookupHashedA(pCache, pszPath, cchPath, uHashPath, penmError);
3662}
3663
3664
3665/**
3666 * Looks up a KFSOBJ for the given UTF-16 path.
3667 *
3668 * This will first try the hash table. If not in the hash table, the file
3669 * system cache tree is walked, missing bits filled in and finally a hash table
3670 * entry is created.
3671 *
3672 * Only drive letter paths are cachable. We don't do any UNC paths at this
3673 * point.
3674 *
3675 * @returns Reference to object corresponding to @a pwszPath on success, this
3676 * must be released by kFsCacheObjRelease.
3677 * NULL if not a path we care to cache.
3678 * @param pCache The cache.
3679 * @param pwszPath The path to lookup.
3680 * @param penmError Where to return details as to why the lookup
3681 * failed.
3682 */
3683PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3684{
3685 KU32 uHashPath;
3686 KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath);
3687 return kFsCacheLookupHashedW(pCache, pwszPath, cwcPath, uHashPath, penmError);
3688}
3689
3690
3691/**
3692 * Looks up a KFSOBJ for the given ANSI path.
3693 *
3694 * This will first try the hash table. If not in the hash table, the file
3695 * system cache tree is walked, missing bits filled in and finally a hash table
3696 * entry is created.
3697 *
3698 * Only drive letter paths are cachable. We don't do any UNC paths at this
3699 * point.
3700 *
3701 * @returns Reference to object corresponding to @a pchPath on success, this
3702 * must be released by kFsCacheObjRelease.
3703 * NULL if not a path we care to cache.
3704 * @param pCache The cache.
3705 * @param pchPath The path to lookup (does not need to be nul
3706 * terminated).
3707 * @param cchPath The path length.
3708 * @param penmError Where to return details as to why the lookup
3709 * failed.
3710 */
3711PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError)
3712{
3713 return kFsCacheLookupHashedA(pCache, pchPath, (KU32)cchPath, kFsCacheStrHashN(pchPath, cchPath), penmError);
3714}
3715
3716
3717/**
3718 * Looks up a KFSOBJ for the given UTF-16 path.
3719 *
3720 * This will first try the hash table. If not in the hash table, the file
3721 * system cache tree is walked, missing bits filled in and finally a hash table
3722 * entry is created.
3723 *
3724 * Only drive letter paths are cachable. We don't do any UNC paths at this
3725 * point.
3726 *
3727 * @returns Reference to object corresponding to @a pwchPath on success, this
3728 * must be released by kFsCacheObjRelease.
3729 * NULL if not a path we care to cache.
3730 * @param pCache The cache.
3731 * @param pwcPath The path to lookup (does not need to be nul
3732 * terminated).
3733 * @param cwcPath The path length (in wchar_t's).
3734 * @param penmError Where to return details as to why the lookup
3735 * failed.
3736 */
3737PKFSOBJ kFsCacheLookupWithLengthW(PKFSCACHE pCache, const wchar_t *pwcPath, KSIZE cwcPath, KFSLOOKUPERROR *penmError)
3738{
3739 return kFsCacheLookupHashedW(pCache, pwcPath, (KU32)cwcPath, kFsCacheUtf16HashN(pwcPath, cwcPath), penmError);
3740}
3741
3742
3743/**
3744 * Wrapper around kFsCacheLookupA that drops KFSOBJ_TYPE_MISSING and returns
3745 * KFSLOOKUPERROR_NOT_FOUND instead.
3746 *
3747 * @returns Reference to object corresponding to @a pszPath on success, this
3748 * must be released by kFsCacheObjRelease.
3749 * NULL if not a path we care to cache.
3750 * @param pCache The cache.
3751 * @param pszPath The path to lookup.
3752 * @param penmError Where to return details as to why the lookup
3753 * failed.
3754 */
3755PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3756{
3757 PKFSOBJ pObj = kFsCacheLookupA(pCache, pszPath, penmError);
3758 if (pObj)
3759 {
3760 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3761 return pObj;
3762
3763 kFsCacheObjRelease(pCache, pObj);
3764 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3765 }
3766 return NULL;
3767}
3768
3769
3770/**
3771 * Wrapper around kFsCacheLookupW that drops KFSOBJ_TYPE_MISSING and returns
3772 * KFSLOOKUPERROR_NOT_FOUND instead.
3773 *
3774 * @returns Reference to object corresponding to @a pszPath on success, this
3775 * must be released by kFsCacheObjRelease.
3776 * NULL if not a path we care to cache.
3777 * @param pCache The cache.
3778 * @param pwszPath The path to lookup.
3779 * @param penmError Where to return details as to why the lookup
3780 * failed.
3781 */
3782PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3783{
3784 PKFSOBJ pObj = kFsCacheLookupW(pCache, pwszPath, penmError);
3785 if (pObj)
3786 {
3787 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3788 return pObj;
3789
3790 kFsCacheObjRelease(pCache, pObj);
3791 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3792 }
3793 return NULL;
3794}
3795
3796
3797/**
3798 * Destroys a cache object which has a zero reference count.
3799 *
3800 * @returns 0
3801 * @param pCache The cache.
3802 * @param pObj The object.
3803 * @param pszWhere Where it was released from.
3804 */
3805KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere)
3806{
3807 kHlpAssert(pObj->cRefs == 0);
3808 kHlpAssert(pObj->pParent == NULL);
3809 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3810
3811 KFSCACHE_LOG(("Destroying %s/%s, type=%d, pObj=%p, pszWhere=%s\n",
3812 pObj->pParent ? pObj->pParent->Obj.pszName : "", pObj->pszName, pObj->bObjType, pObj, pszWhere));
3813 if (pObj->abUnused[1] != 0)
3814 {
3815 fprintf(stderr, "Destroying %s/%s, type=%d, path hash entries: %d!\n", pObj->pParent ? pObj->pParent->Obj.pszName : "",
3816 pObj->pszName, pObj->bObjType, pObj->abUnused[0]);
3817 fflush(stderr);
3818 __debugbreak();
3819 }
3820
3821 /*
3822 * Invalidate the structure.
3823 */
3824 pObj->u32Magic = ~KFSOBJ_MAGIC;
3825
3826 /*
3827 * Destroy any user data first.
3828 */
3829 while (pObj->pUserDataHead != NULL)
3830 {
3831 PKFSUSERDATA pUserData = pObj->pUserDataHead;
3832 pObj->pUserDataHead = pUserData->pNext;
3833 if (pUserData->pfnDestructor)
3834 pUserData->pfnDestructor(pCache, pObj, pUserData);
3835 kHlpFree(pUserData);
3836 }
3837
3838 /*
3839 * Do type specific destruction
3840 */
3841 switch (pObj->bObjType)
3842 {
3843 case KFSOBJ_TYPE_MISSING:
3844 /* nothing else to do here */
3845 pCache->cbObjects -= sizeof(KFSDIR);
3846 break;
3847
3848 case KFSOBJ_TYPE_DIR:
3849 {
3850 PKFSDIR pDir = (PKFSDIR)pObj;
3851 KU32 cChildren = pDir->cChildren;
3852 pCache->cbObjects -= sizeof(*pDir)
3853 + K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
3854 + (pDir->fHashTabMask + !!pDir->fHashTabMask) * sizeof(pDir->papHashTab[0]);
3855
3856 pDir->cChildren = 0;
3857 while (cChildren-- > 0)
3858 kFsCacheObjRelease(pCache, pDir->papChildren[cChildren]);
3859 kHlpFree(pDir->papChildren);
3860 pDir->papChildren = NULL;
3861
3862 kHlpFree(pDir->papHashTab);
3863 pDir->papHashTab = NULL;
3864 break;
3865 }
3866
3867 case KFSOBJ_TYPE_FILE:
3868 case KFSOBJ_TYPE_OTHER:
3869 pCache->cbObjects -= sizeof(*pObj);
3870 break;
3871
3872 default:
3873 return 0;
3874 }
3875
3876 /*
3877 * Common bits.
3878 */
3879 pCache->cbObjects -= pObj->cchName + 1;
3880#ifdef KFSCACHE_CFG_UTF16
3881 pCache->cbObjects -= (pObj->cwcName + 1) * sizeof(wchar_t);
3882#endif
3883#ifdef KFSCACHE_CFG_SHORT_NAMES
3884 if (pObj->pszName != pObj->pszShortName)
3885 {
3886 pCache->cbObjects -= pObj->cchShortName + 1;
3887# ifdef KFSCACHE_CFG_UTF16
3888 pCache->cbObjects -= (pObj->cwcShortName + 1) * sizeof(wchar_t);
3889# endif
3890 }
3891#endif
3892 pCache->cObjects--;
3893
3894 if (pObj->pNameAlloc)
3895 {
3896 pCache->cbObjects -= pObj->pNameAlloc->cb;
3897 kHlpFree(pObj->pNameAlloc);
3898 }
3899
3900 kHlpFree(pObj);
3901 return 0;
3902}
3903
3904
3905/**
3906 * Releases a reference to a cache object.
3907 *
3908 * @returns New reference count.
3909 * @param pCache The cache.
3910 * @param pObj The object.
3911 */
3912#undef kFsCacheObjRelease
3913KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
3914{
3915 if (pObj)
3916 {
3917 KU32 cRefs;
3918 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3919 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3920
3921 cRefs = --pObj->cRefs;
3922 if (cRefs)
3923 return cRefs;
3924 return kFsCacheObjDestroy(pCache, pObj, "kFsCacheObjRelease");
3925 }
3926 return 0;
3927}
3928
3929
3930/**
3931 * Debug version of kFsCacheObjRelease
3932 *
3933 * @returns New reference count.
3934 * @param pCache The cache.
3935 * @param pObj The object.
3936 * @param pszWhere Where it's invoked from.
3937 */
3938KU32 kFsCacheObjReleaseTagged(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere)
3939{
3940 if (pObj)
3941 {
3942 KU32 cRefs;
3943 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3944 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3945
3946 cRefs = --pObj->cRefs;
3947 if (cRefs)
3948 return cRefs;
3949 return kFsCacheObjDestroy(pCache, pObj, pszWhere);
3950 }
3951 return 0;
3952}
3953
3954
3955/**
3956 * Retains a reference to a cahce object.
3957 *
3958 * @returns New reference count.
3959 * @param pObj The object.
3960 */
3961KU32 kFsCacheObjRetain(PKFSOBJ pObj)
3962{
3963 KU32 cRefs;
3964 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3965 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3966
3967 cRefs = ++pObj->cRefs;
3968 kHlpAssert(cRefs < 16384);
3969 return cRefs;
3970}
3971
3972
3973/**
3974 * Associates an item of user data with the given object.
3975 *
3976 * If the data needs cleaning up before being free, set the
3977 * PKFSUSERDATA::pfnDestructor member of the returned structure.
3978 *
3979 * @returns Pointer to the user data on success.
3980 * NULL if out of memory or key already in use.
3981 *
3982 * @param pCache The cache.
3983 * @param pObj The object.
3984 * @param uKey The user data key.
3985 * @param cbUserData The size of the user data.
3986 */
3987PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData)
3988{
3989 kHlpAssert(cbUserData >= sizeof(*pNew));
3990 if (kFsCacheObjGetUserData(pCache, pObj, uKey) == NULL)
3991 {
3992 PKFSUSERDATA pNew = (PKFSUSERDATA)kHlpAllocZ(cbUserData);
3993 if (pNew)
3994 {
3995 pNew->uKey = uKey;
3996 pNew->pfnDestructor = NULL;
3997 pNew->pNext = pObj->pUserDataHead;
3998 pObj->pUserDataHead = pNew;
3999 return pNew;
4000 }
4001 }
4002
4003 return NULL;
4004}
4005
4006
4007/**
4008 * Retrieves an item of user data associated with the given object.
4009 *
4010 * @returns Pointer to the associated user data if found, otherwise NULL.
4011 * @param pCache The cache.
4012 * @param pObj The object.
4013 * @param uKey The user data key.
4014 */
4015PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey)
4016{
4017 PKFSUSERDATA pCur;
4018
4019 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
4020 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4021
4022 for (pCur = pObj->pUserDataHead; pCur; pCur = pCur->pNext)
4023 if (pCur->uKey == uKey)
4024 return pCur;
4025 return NULL;
4026}
4027
4028
4029/**
4030 * Gets the full path to @a pObj, ANSI version.
4031 *
4032 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4033 * @param pObj The object to get the full path to.
4034 * @param pszPath Where to return the path
4035 * @param cbPath The size of the output buffer.
4036 * @param chSlash The slash to use.
4037 */
4038KBOOL kFsCacheObjGetFullPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
4039{
4040 KSIZE off = pObj->cchParent;
4041 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4042 if (off > 0)
4043 {
4044 KSIZE offEnd = off + pObj->cchName;
4045 if (offEnd < cbPath)
4046 {
4047 PKFSDIR pAncestor;
4048
4049 pszPath[off + pObj->cchName] = '\0';
4050 memcpy(&pszPath[off], pObj->pszName, pObj->cchName);
4051
4052 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4053 {
4054 kHlpAssert(off > 1);
4055 kHlpAssert(pAncestor != NULL);
4056 kHlpAssert(pAncestor->Obj.cchName > 0);
4057 pszPath[--off] = chSlash;
4058 off -= pAncestor->Obj.cchName;
4059 kHlpAssert(pAncestor->Obj.cchParent == off);
4060 memcpy(&pszPath[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4061 }
4062 return K_TRUE;
4063 }
4064 }
4065 else
4066 {
4067 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
4068 off = pObj->cchName;
4069 if (off + fDriveLetter < cbPath)
4070 {
4071 memcpy(pszPath, pObj->pszName, off);
4072 if (fDriveLetter)
4073 pszPath[off++] = chSlash;
4074 pszPath[off] = '\0';
4075 return K_TRUE;
4076 }
4077 }
4078
4079 return K_FALSE;
4080}
4081
4082
4083/**
4084 * Gets the full path to @a pObj, UTF-16 version.
4085 *
4086 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4087 * @param pObj The object to get the full path to.
4088 * @param pszPath Where to return the path
4089 * @param cbPath The size of the output buffer.
4090 * @param wcSlash The slash to use.
4091 */
4092KBOOL kFsCacheObjGetFullPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
4093{
4094 KSIZE off = pObj->cwcParent;
4095 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4096 if (off > 0)
4097 {
4098 KSIZE offEnd = off + pObj->cwcName;
4099 if (offEnd < cwcPath)
4100 {
4101 PKFSDIR pAncestor;
4102
4103 pwszPath[off + pObj->cwcName] = '\0';
4104 memcpy(&pwszPath[off], pObj->pwszName, pObj->cwcName * sizeof(wchar_t));
4105
4106 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4107 {
4108 kHlpAssert(off > 1);
4109 kHlpAssert(pAncestor != NULL);
4110 kHlpAssert(pAncestor->Obj.cwcName > 0);
4111 pwszPath[--off] = wcSlash;
4112 off -= pAncestor->Obj.cwcName;
4113 kHlpAssert(pAncestor->Obj.cwcParent == off);
4114 memcpy(&pwszPath[off], pAncestor->Obj.pwszName, pAncestor->Obj.cwcName * sizeof(wchar_t));
4115 }
4116 return K_TRUE;
4117 }
4118 }
4119 else
4120 {
4121 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
4122 off = pObj->cwcName;
4123 if (off + fDriveLetter < cwcPath)
4124 {
4125 memcpy(pwszPath, pObj->pwszName, off * sizeof(wchar_t));
4126 if (fDriveLetter)
4127 pwszPath[off++] = wcSlash;
4128 pwszPath[off] = '\0';
4129 return K_TRUE;
4130 }
4131 }
4132
4133 return K_FALSE;
4134}
4135
4136
4137#ifdef KFSCACHE_CFG_SHORT_NAMES
4138
4139/**
4140 * Gets the full short path to @a pObj, ANSI version.
4141 *
4142 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4143 * @param pObj The object to get the full path to.
4144 * @param pszPath Where to return the path
4145 * @param cbPath The size of the output buffer.
4146 * @param chSlash The slash to use.
4147 */
4148KBOOL kFsCacheObjGetFullShortPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
4149{
4150 KSIZE off = pObj->cchShortParent;
4151 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4152 if (off > 0)
4153 {
4154 KSIZE offEnd = off + pObj->cchShortName;
4155 if (offEnd < cbPath)
4156 {
4157 PKFSDIR pAncestor;
4158
4159 pszPath[off + pObj->cchShortName] = '\0';
4160 memcpy(&pszPath[off], pObj->pszShortName, pObj->cchShortName);
4161
4162 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4163 {
4164 kHlpAssert(off > 1);
4165 kHlpAssert(pAncestor != NULL);
4166 kHlpAssert(pAncestor->Obj.cchShortName > 0);
4167 pszPath[--off] = chSlash;
4168 off -= pAncestor->Obj.cchShortName;
4169 kHlpAssert(pAncestor->Obj.cchShortParent == off);
4170 memcpy(&pszPath[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
4171 }
4172 return K_TRUE;
4173 }
4174 }
4175 else
4176 {
4177 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
4178 off = pObj->cchShortName;
4179 if (off + fDriveLetter < cbPath)
4180 {
4181 memcpy(pszPath, pObj->pszShortName, off);
4182 if (fDriveLetter)
4183 pszPath[off++] = chSlash;
4184 pszPath[off] = '\0';
4185 return K_TRUE;
4186 }
4187 }
4188
4189 return K_FALSE;
4190}
4191
4192
4193/**
4194 * Gets the full short path to @a pObj, UTF-16 version.
4195 *
4196 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
4197 * @param pObj The object to get the full path to.
4198 * @param pszPath Where to return the path
4199 * @param cbPath The size of the output buffer.
4200 * @param wcSlash The slash to use.
4201 */
4202KBOOL kFsCacheObjGetFullShortPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
4203{
4204 KSIZE off = pObj->cwcShortParent;
4205 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
4206 if (off > 0)
4207 {
4208 KSIZE offEnd = off + pObj->cwcShortName;
4209 if (offEnd < cwcPath)
4210 {
4211 PKFSDIR pAncestor;
4212
4213 pwszPath[off + pObj->cwcShortName] = '\0';
4214 memcpy(&pwszPath[off], pObj->pwszShortName, pObj->cwcShortName * sizeof(wchar_t));
4215
4216 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4217 {
4218 kHlpAssert(off > 1);
4219 kHlpAssert(pAncestor != NULL);
4220 kHlpAssert(pAncestor->Obj.cwcShortName > 0);
4221 pwszPath[--off] = wcSlash;
4222 off -= pAncestor->Obj.cwcShortName;
4223 kHlpAssert(pAncestor->Obj.cwcShortParent == off);
4224 memcpy(&pwszPath[off], pAncestor->Obj.pwszShortName, pAncestor->Obj.cwcShortName * sizeof(wchar_t));
4225 }
4226 return K_TRUE;
4227 }
4228 }
4229 else
4230 {
4231 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
4232 off = pObj->cwcShortName;
4233 if (off + fDriveLetter < cwcPath)
4234 {
4235 memcpy(pwszPath, pObj->pwszShortName, off * sizeof(wchar_t));
4236 if (fDriveLetter)
4237 pwszPath[off++] = wcSlash;
4238 pwszPath[off] = '\0';
4239 return K_TRUE;
4240 }
4241 }
4242
4243 return K_FALSE;
4244}
4245
4246#endif /* KFSCACHE_CFG_SHORT_NAMES */
4247
4248
4249
4250/**
4251 * Read the specified bits from the files into the given buffer, simple version.
4252 *
4253 * @returns K_TRUE on success (all requested bytes read),
4254 * K_FALSE on any kind of failure.
4255 *
4256 * @param pCache The cache.
4257 * @param pFileObj The file object.
4258 * @param offStart Where to start reading.
4259 * @param pvBuf Where to store what we read.
4260 * @param cbToRead How much to read (exact).
4261 */
4262KBOOL kFsCacheFileSimpleOpenReadClose(PKFSCACHE pCache, PKFSOBJ pFileObj, KU64 offStart, void *pvBuf, KSIZE cbToRead)
4263{
4264 /*
4265 * Open the file relative to the parent directory.
4266 */
4267 MY_NTSTATUS rcNt;
4268 HANDLE hFile;
4269 MY_IO_STATUS_BLOCK Ios;
4270 MY_OBJECT_ATTRIBUTES ObjAttr;
4271 MY_UNICODE_STRING UniStr;
4272
4273 kHlpAssertReturn(pFileObj->bObjType == KFSOBJ_TYPE_FILE, K_FALSE);
4274 kHlpAssert(pFileObj->pParent);
4275 kHlpAssertReturn(pFileObj->pParent->hDir != INVALID_HANDLE_VALUE, K_FALSE);
4276 kHlpAssertReturn(offStart == 0, K_FALSE); /** @todo when needed */
4277
4278 Ios.Information = -1;
4279 Ios.u.Status = -1;
4280
4281 UniStr.Buffer = (wchar_t *)pFileObj->pwszName;
4282 UniStr.Length = (USHORT)(pFileObj->cwcName * sizeof(wchar_t));
4283 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
4284
4285 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFileObj->pParent->hDir, NULL /*pSecAttr*/);
4286
4287 rcNt = g_pfnNtCreateFile(&hFile,
4288 GENERIC_READ | SYNCHRONIZE,
4289 &ObjAttr,
4290 &Ios,
4291 NULL, /*cbFileInitialAlloc */
4292 FILE_ATTRIBUTE_NORMAL,
4293 FILE_SHARE_READ,
4294 FILE_OPEN,
4295 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
4296 NULL, /*pEaBuffer*/
4297 0); /*cbEaBuffer*/
4298 if (MY_NT_SUCCESS(rcNt))
4299 {
4300 LARGE_INTEGER offFile;
4301 offFile.QuadPart = offStart;
4302
4303 Ios.Information = -1;
4304 Ios.u.Status = -1;
4305 rcNt = g_pfnNtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApcComplete*/, NULL /*pvApcCtx*/, &Ios,
4306 pvBuf, (KU32)cbToRead, !offStart ? &offFile : NULL, NULL /*puKey*/);
4307 if (MY_NT_SUCCESS(rcNt))
4308 rcNt = Ios.u.Status;
4309 if (MY_NT_SUCCESS(rcNt))
4310 {
4311 if (Ios.Information == cbToRead)
4312 {
4313 g_pfnNtClose(hFile);
4314 return K_TRUE;
4315 }
4316 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': Information=%p\n", pFileObj->pwszName, Ios.Information));
4317 }
4318 else
4319 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': %#x\n", pFileObj->pwszName, rcNt));
4320 g_pfnNtClose(hFile);
4321 }
4322 else
4323 KFSCACHE_LOG(("Error opening '%ls' for caching: %#x\n", pFileObj->pwszName, rcNt));
4324 return K_FALSE;
4325}
4326
4327
4328/**
4329 * Invalidate all cache entries of missing files.
4330 *
4331 * @param pCache The cache.
4332 */
4333void kFsCacheInvalidateMissing(PKFSCACHE pCache)
4334{
4335 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4336 pCache->auGenerationsMissing[0]++;
4337 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
4338 KFSCACHE_LOG(("Invalidate missing %#x\n", pCache->auGenerationsMissing[0]));
4339}
4340
4341
4342/**
4343 * Invalidate all cache entries (regular, custom & missing).
4344 *
4345 * @param pCache The cache.
4346 */
4347void kFsCacheInvalidateAll(PKFSCACHE pCache)
4348{
4349 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4350
4351 pCache->auGenerationsMissing[0]++;
4352 kHlpAssert(pCache->auGenerationsMissing[0] < KU32_MAX);
4353 pCache->auGenerationsMissing[1]++;
4354 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4355
4356 pCache->auGenerations[0]++;
4357 kHlpAssert(pCache->auGenerations[0] < KU32_MAX);
4358 pCache->auGenerations[1]++;
4359 kHlpAssert(pCache->auGenerations[1] < KU32_MAX);
4360
4361 KFSCACHE_LOG(("Invalidate all - default: %#x/%#x, custom: %#x/%#x\n",
4362 pCache->auGenerationsMissing[0], pCache->auGenerations[0],
4363 pCache->auGenerationsMissing[1], pCache->auGenerations[1]));
4364}
4365
4366
4367/**
4368 * Invalidate all cache entries with custom generation handling set.
4369 *
4370 * @see kFsCacheSetupCustomRevisionForTree, KFSOBJ_F_USE_CUSTOM_GEN
4371 * @param pCache The cache.
4372 */
4373void kFsCacheInvalidateCustomMissing(PKFSCACHE pCache)
4374{
4375 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4376 pCache->auGenerationsMissing[1]++;
4377 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4378 KFSCACHE_LOG(("Invalidate missing custom %#x\n", pCache->auGenerationsMissing[1]));
4379}
4380
4381
4382/**
4383 * Invalidate all cache entries with custom generation handling set, both
4384 * missing and regular present entries.
4385 *
4386 * @see kFsCacheSetupCustomRevisionForTree, KFSOBJ_F_USE_CUSTOM_GEN
4387 * @param pCache The cache.
4388 */
4389void kFsCacheInvalidateCustomBoth(PKFSCACHE pCache)
4390{
4391 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
4392 pCache->auGenerations[1]++;
4393 kHlpAssert(pCache->auGenerations[1] < KU32_MAX);
4394 pCache->auGenerationsMissing[1]++;
4395 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
4396 KFSCACHE_LOG(("Invalidate both custom %#x/%#x\n", pCache->auGenerationsMissing[1], pCache->auGenerations[1]));
4397}
4398
4399
4400
4401/**
4402 * Applies the given flags to all the objects in a tree.
4403 *
4404 * @param pRoot Where to start applying the flag changes.
4405 * @param fAndMask The AND mask.
4406 * @param fOrMask The OR mask.
4407 */
4408static void kFsCacheApplyFlagsToTree(PKFSDIR pRoot, KU32 fAndMask, KU32 fOrMask)
4409{
4410 PKFSOBJ *ppCur = ((PKFSDIR)pRoot)->papChildren;
4411 KU32 cLeft = ((PKFSDIR)pRoot)->cChildren;
4412 while (cLeft-- > 0)
4413 {
4414 PKFSOBJ pCur = *ppCur++;
4415 if (pCur->bObjType != KFSOBJ_TYPE_DIR)
4416 pCur->fFlags = (fAndMask & pCur->fFlags) | fOrMask;
4417 else
4418 kFsCacheApplyFlagsToTree((PKFSDIR)pCur, fAndMask, fOrMask);
4419 }
4420
4421 pRoot->Obj.fFlags = (fAndMask & pRoot->Obj.fFlags) | fOrMask;
4422}
4423
4424
4425/**
4426 * Sets up using custom revisioning for the specified directory tree or file.
4427 *
4428 * There are some restrictions of the current implementation:
4429 * - If the root of the sub-tree is ever deleted from the cache (i.e.
4430 * deleted in real life and reflected in the cache), the setting is lost.
4431 * - It is not automatically applied to the lookup paths caches.
4432 *
4433 * @returns K_TRUE on success, K_FALSE on failure.
4434 * @param pCache The cache.
4435 * @param pRoot The root of the subtree. A non-directory is
4436 * fine, like a missing node.
4437 */
4438KBOOL kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot)
4439{
4440 if (pRoot)
4441 {
4442 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
4443 kFsCacheApplyFlagsToTree((PKFSDIR)pRoot, KU32_MAX, KFSOBJ_F_USE_CUSTOM_GEN);
4444 else
4445 pRoot->fFlags |= KFSOBJ_F_USE_CUSTOM_GEN;
4446 return K_TRUE;
4447 }
4448 return K_FALSE;
4449}
4450
4451
4452/**
4453 * Invalidates a deleted directory, ANSI version.
4454 *
4455 * @returns K_TRUE if found and is a non-root directory. Otherwise K_FALSE.
4456 * @param pCache The cache.
4457 * @param pszDir The directory.
4458 */
4459KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir)
4460{
4461 KU32 cchDir = (KU32)kHlpStrLen(pszDir);
4462 KFSLOOKUPERROR enmError;
4463 PKFSOBJ pFsObj;
4464
4465 /* Is absolute without any '..' bits? */
4466 if ( cchDir >= 3
4467 && ( ( pszDir[1] == ':' /* Drive letter */
4468 && IS_SLASH(pszDir[2])
4469 && IS_ALPHA(pszDir[0]) )
4470 || ( IS_SLASH(pszDir[0]) /* UNC */
4471 && IS_SLASH(pszDir[1]) ) )
4472 && !kFsCacheHasDotDotA(pszDir, cchDir) )
4473 pFsObj = kFsCacheLookupAbsoluteA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
4474 &enmError, NULL);
4475 else
4476 pFsObj = kFsCacheLookupSlowA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
4477 &enmError, NULL);
4478 if (pFsObj)
4479 {
4480 /* Is directory? */
4481 if (pFsObj->bObjType == KFSOBJ_TYPE_DIR)
4482 {
4483 if (pFsObj->pParent != &pCache->RootDir)
4484 {
4485 PKFSDIR pDir = (PKFSDIR)pFsObj;
4486 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: %s hDir=%p\n", pszDir, pDir->hDir));
4487 if (pDir->hDir != INVALID_HANDLE_VALUE)
4488 {
4489 g_pfnNtClose(pDir->hDir);
4490 pDir->hDir = INVALID_HANDLE_VALUE;
4491 }
4492 pDir->fNeedRePopulating = K_TRUE;
4493 pDir->Obj.uCacheGen = pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN] - 1;
4494 kFsCacheObjRetainInternal(&pDir->Obj);
4495 return K_TRUE;
4496 }
4497 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a root directory was deleted! %s\n", pszDir));
4498 }
4499 else
4500 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a non-directory: bObjType=%d %s\n",
4501 pFsObj->bObjType, pszDir));
4502 kFsCacheObjRetainInternal(pFsObj);
4503 }
4504 else
4505 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: '%s' was not found\n", pszDir));
4506 return K_FALSE;
4507}
4508
4509
4510PKFSCACHE kFsCacheCreate(KU32 fFlags)
4511{
4512 PKFSCACHE pCache;
4513 birdResolveImports();
4514
4515 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
4516 if (pCache)
4517 {
4518 /* Dummy root dir entry. */
4519 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC;
4520 pCache->RootDir.Obj.cRefs = 1;
4521 pCache->RootDir.Obj.uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
4522 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR;
4523 pCache->RootDir.Obj.fHaveStats = K_FALSE;
4524 pCache->RootDir.Obj.pParent = NULL;
4525 pCache->RootDir.Obj.pszName = "";
4526 pCache->RootDir.Obj.cchName = 0;
4527 pCache->RootDir.Obj.cchParent = 0;
4528#ifdef KFSCACHE_CFG_UTF16
4529 pCache->RootDir.Obj.cwcName = 0;
4530 pCache->RootDir.Obj.cwcParent = 0;
4531 pCache->RootDir.Obj.pwszName = L"";
4532#endif
4533
4534#ifdef KFSCACHE_CFG_SHORT_NAMES
4535 pCache->RootDir.Obj.pszShortName = NULL;
4536 pCache->RootDir.Obj.cchShortName = 0;
4537 pCache->RootDir.Obj.cchShortParent = 0;
4538# ifdef KFSCACHE_CFG_UTF16
4539 pCache->RootDir.Obj.cwcShortName;
4540 pCache->RootDir.Obj.cwcShortParent;
4541 pCache->RootDir.Obj.pwszShortName;
4542# endif
4543#endif
4544 pCache->RootDir.cChildren = 0;
4545 pCache->RootDir.cChildrenAllocated = 0;
4546 pCache->RootDir.papChildren = NULL;
4547 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
4548 pCache->RootDir.fHashTabMask = 255; /* 256: 26 drive letters and 102 UNCs before we're half ways. */
4549 pCache->RootDir.papHashTab = (PKFSOBJ *)kHlpAllocZ(256 * sizeof(pCache->RootDir.papHashTab[0]));
4550 if (pCache->RootDir.papHashTab)
4551 {
4552 /* The cache itself. */
4553 pCache->u32Magic = KFSCACHE_MAGIC;
4554 pCache->fFlags = fFlags;
4555 pCache->auGenerations[0] = KU32_MAX / 4;
4556 pCache->auGenerations[1] = KU32_MAX / 32;
4557 pCache->auGenerationsMissing[0] = KU32_MAX / 256;
4558 pCache->auGenerationsMissing[1] = 1;
4559 pCache->cObjects = 1;
4560 pCache->cbObjects = sizeof(pCache->RootDir)
4561 + (pCache->RootDir.fHashTabMask + 1) * sizeof(pCache->RootDir.papHashTab[0]);
4562 pCache->cPathHashHits = 0;
4563 pCache->cWalkHits = 0;
4564 pCache->cChildSearches = 0;
4565 pCache->cChildHashHits = 0;
4566 pCache->cChildHashed = 0;
4567 pCache->cChildHashTabs = 1;
4568 pCache->cChildHashEntriesTotal = pCache->RootDir.fHashTabMask + 1;
4569 pCache->cChildHashCollisions = 0;
4570 pCache->cNameChanges = 0;
4571 pCache->cNameGrowths = 0;
4572 pCache->cAnsiPaths = 0;
4573 pCache->cAnsiPathCollisions = 0;
4574 pCache->cbAnsiPaths = 0;
4575#ifdef KFSCACHE_CFG_UTF16
4576 pCache->cUtf16Paths = 0;
4577 pCache->cUtf16PathCollisions = 0;
4578 pCache->cbUtf16Paths = 0;
4579#endif
4580 return pCache;
4581 }
4582
4583 kHlpFree(pCache);
4584 }
4585 return NULL;
4586}
4587
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