VirtualBox

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

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

flush stderr before debugbreak

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