source: trunk/src/lib/nt/kFsCache.c@ 2871

Last change on this file since 2871 was 2868, checked in by bird, 9 years ago

Only invalidate the PATH_OUT and TEMP in kWorker.

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