VirtualBox

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

Last change on this file since 3372 was 3372, checked in by bird, 5 years ago

kFsCache: KFSLOOKUPERROR_PATH_TOO_SHORT.

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