VirtualBox

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

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

kFsCache: Account of race between us reading a directory and someone changing it, requiring re-reading the directory till at least 10ms has passed since the last direction change.

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