VirtualBox

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

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

rewrote kmk_redirect to skip the separate process. Added chache invalidation after directory deletion for addressing kmk rebuild and fetching.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette