VirtualBox

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

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

lib/nt: Deal better with NTFS mount points.

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