VirtualBox

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

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

kFsCache: w7 fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 148.2 KB
Line 
1/* $Id: kFsCache.c 2872 2016-09-04 18:42:10Z bird $ */
2/** @file
3 * ntdircache.c - NT directory content cache.
4 */
5
6/*
7 * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#include <k/kHlp.h>
36
37#include "nthlp.h"
38#include "ntstat.h"
39
40#include <stdio.h>
41#include <mbstring.h>
42#include <wchar.h>
43//#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 1
1716 /* This always works and doesn't mess up NtQueryDirectoryFile. */
1717 MY_UNICODE_STRING UniStr;
1718 MY_OBJECT_ATTRIBUTES ObjAttr;
1719
1720 UniStr.Buffer = (wchar_t *)pObj->pwszName;
1721 UniStr.Length = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1722 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1723
1724 kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1725 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pObj->pParent->hDir, NULL /*pSecAttr*/);
1726
1727 rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.FullInfo);
1728 if (MY_NT_SUCCESS(rcNt))
1729 {
1730 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
1731 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1732 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1733 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1734 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1735 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
1736 pObj->Stats.st_blksize = 65536;
1737 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1738 / BIRD_STAT_BLOCK_SIZE;
1739 }
1740#else
1741 /* This alternative lets us keep the inode number up to date and
1742 detect name case changes.
1743 Update: This doesn't work on windows 7, it ignores the UniStr
1744 and continue with the "*" search. So, we're using the
1745 above query instead for the time being. */
1746 MY_UNICODE_STRING UniStr;
1747# ifdef KFSCACHE_CFG_SHORT_NAMES
1748 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
1749# else
1750 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
1751# endif
1752
1753 UniStr.Buffer = (wchar_t *)pObj->pwszName;
1754 UniStr.Length = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1755 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1756
1757 kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1758
1759 Ios.Information = -1;
1760 Ios.u.Status = -1;
1761 rcNt = g_pfnNtQueryDirectoryFile(pObj->pParent->hDir,
1762 NULL, /* hEvent */
1763 NULL, /* pfnApcComplete */
1764 NULL, /* pvApcCompleteCtx */
1765 &Ios,
1766 &uBuf,
1767 sizeof(uBuf),
1768 enmInfoClass,
1769 TRUE, /* fReturnSingleEntry */
1770 &UniStr, /* Filter / restart pos. */
1771 TRUE); /* fRestartScan */
1772
1773 if (MY_NT_SUCCESS(rcNt))
1774 {
1775 if (pObj->Stats.st_ino == uBuf.WithId.FileId.QuadPart)
1776 KFSCACHE_LOG(("Refreshing %s/%s, no ID change...\n", pObj->pParent->Obj.pszName, pObj->pszName));
1777 else if ( pObj->cwcName == uBuf.WithId.FileNameLength / sizeof(wchar_t)
1778# ifdef KFSCACHE_CFG_SHORT_NAMES
1779 && ( uBuf.WithId.ShortNameLength == 0
1780 ? pObj->pwszName == pObj->pwszShortName
1781 || ( pObj->cwcName == pObj->cwcShortName
1782 && memcmp(pObj->pwszName, pObj->pwszShortName, pObj->cwcName * sizeof(wchar_t)) == 0)
1783 : pObj->cwcShortName == uBuf.WithId.ShortNameLength / sizeof(wchar_t)
1784 && memcmp(pObj->pwszShortName, uBuf.WithId.ShortName, uBuf.WithId.ShortNameLength) == 0
1785 )
1786# endif
1787 && memcmp(pObj->pwszName, uBuf.WithId.FileName, uBuf.WithId.FileNameLength) == 0
1788 )
1789 {
1790 KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx...\n",
1791 pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1792 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1793 }
1794 else
1795 {
1796 KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx and names too...\n",
1797 pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1798 fprintf(stderr, "kFsCacheRefreshObj - ID + name change not implemented!!\n");
1799 __debugbreak();
1800 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1801 /** @todo implement as needed. */
1802 }
1803
1804 pObj->Stats.st_size = uBuf.WithId.EndOfFile.QuadPart;
1805 birdNtTimeToTimeSpec(uBuf.WithId.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1806 birdNtTimeToTimeSpec(uBuf.WithId.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1807 birdNtTimeToTimeSpec(uBuf.WithId.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1808 birdNtTimeToTimeSpec(uBuf.WithId.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1809 pObj->Stats.st_attribs = uBuf.WithId.FileAttributes;
1810 pObj->Stats.st_blksize = 65536;
1811 pObj->Stats.st_blocks = (uBuf.WithId.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1812 / BIRD_STAT_BLOCK_SIZE;
1813 }
1814#endif
1815 if (MY_NT_SUCCESS(rcNt))
1816 {
1817 pObj->uCacheGen = pCache->auGenerations[pObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1818 fRc = K_TRUE;
1819 }
1820 else
1821 {
1822 /* ouch! */
1823 kHlpAssertMsgFailed(("%#x\n", rcNt));
1824 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on non-dir - not implemented!\n", rcNt);
1825 __debugbreak();
1826 fRc = K_FALSE;
1827 }
1828 }
1829 else
1830 {
1831 /*
1832 * An open directory. Query information via the handle, the
1833 * file ID shouldn't have been able to change, so we can use
1834 * NtQueryInformationFile. Right...
1835 */
1836 PKFSDIR pDir = (PKFSDIR)pObj;
1837 Ios.Information = -1;
1838 Ios.u.Status = -1;
1839 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &uBuf.FullInfo, sizeof(uBuf.FullInfo),
1840 MyFileNetworkOpenInformation);
1841 if (MY_NT_SUCCESS(rcNt))
1842 rcNt = Ios.u.Status;
1843 if (MY_NT_SUCCESS(rcNt))
1844 {
1845 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
1846 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1847 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1848 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1849 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1850 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
1851 pObj->Stats.st_blksize = 65536;
1852 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1853 / BIRD_STAT_BLOCK_SIZE;
1854
1855 if ( pDir->iLastWrite == uBuf.FullInfo.LastWriteTime.QuadPart
1856 && (pObj->fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
1857 KFSCACHE_LOG(("Refreshing %s/%s/ - no re-populating necessary.\n",
1858 pObj->pParent->Obj.pszName, pObj->pszName));
1859 else
1860 {
1861 KFSCACHE_LOG(("Refreshing %s/%s/ - needs re-populating...\n",
1862 pObj->pParent->Obj.pszName, pObj->pszName));
1863 pDir->fNeedRePopulating = K_TRUE;
1864#if 0
1865 /* Refresh the link count. */
1866 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
1867 if (MY_NT_SUCCESS(rcNt))
1868 rcNt = Ios.s.Status;
1869 if (MY_NT_SUCCESS(rcNt))
1870 pObj->Stats.st_nlink = StdInfo.NumberOfLinks;
1871#endif
1872 }
1873 }
1874 if (MY_NT_SUCCESS(rcNt))
1875 {
1876 pObj->uCacheGen = pCache->auGenerations[pObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
1877 fRc = K_TRUE;
1878 }
1879 else
1880 {
1881 /* ouch! */
1882 kHlpAssertMsgFailed(("%#x\n", rcNt));
1883 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on dir - not implemented!\n", rcNt);
1884 __debugbreak();
1885 fRc = K_FALSE;
1886 }
1887 }
1888 }
1889
1890 return fRc;
1891}
1892
1893
1894
1895/**
1896 * Looks up a drive letter.
1897 *
1898 * Will enter the drive if necessary.
1899 *
1900 * @returns Pointer to the root directory of the drive or an update-to-date
1901 * missing node.
1902 * @param pCache The cache.
1903 * @param chLetter The uppercased drive letter.
1904 * @param penmError Where to return details as to why the lookup
1905 * failed.
1906 */
1907static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError)
1908{
1909 KU32 const uHash = chLetter - 'A';
1910 KU32 cLeft;
1911 PKFSOBJ *ppCur;
1912
1913 MY_UNICODE_STRING NtPath;
1914 wchar_t wszTmp[8];
1915 char szTmp[4];
1916
1917 /*
1918 * Custom drive letter hashing.
1919 */
1920 if (pCache->RootDir.paHashTab)
1921 {
1922 /** @todo PKFSOBJHASH pHash = */
1923 }
1924
1925 /*
1926 * Special cased lookup.
1927 */
1928 cLeft = pCache->RootDir.cChildren;
1929 ppCur = pCache->RootDir.papChildren;
1930 while (cLeft-- > 0)
1931 {
1932 PKFSOBJ pCur = *ppCur++;
1933 if ( pCur->cchName == 2
1934 && pCur->pszName[0] == chLetter
1935 && pCur->pszName[1] == ':')
1936 {
1937 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
1938 return pCur;
1939 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
1940 if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
1941 return pCur;
1942 return NULL;
1943 }
1944 }
1945
1946 /*
1947 * Need to add it. We always keep the drive letters open for the benefit
1948 * of kFsCachePopuplateOrRefreshDir and others.
1949 */
1950 wszTmp[0] = szTmp[0] = chLetter;
1951 wszTmp[1] = szTmp[1] = ':';
1952 wszTmp[2] = szTmp[2] = '\\';
1953 wszTmp[3] = '.';
1954 wszTmp[4] = '\0';
1955 szTmp[2] = '\0';
1956
1957 NtPath.Buffer = NULL;
1958 NtPath.Length = 0;
1959 NtPath.MaximumLength = 0;
1960 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
1961 {
1962 HANDLE hDir;
1963 MY_NTSTATUS rcNt;
1964 rcNt = birdOpenFileUniStr(&NtPath,
1965 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1966 FILE_ATTRIBUTE_NORMAL,
1967 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1968 FILE_OPEN,
1969 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1970 OBJ_CASE_INSENSITIVE,
1971 &hDir);
1972 birdFreeNtPath(&NtPath);
1973 if (MY_NT_SUCCESS(rcNt))
1974 {
1975 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1976#ifdef KFSCACHE_CFG_SHORT_NAMES
1977 NULL, 0, NULL, 0,
1978#endif
1979 KFSOBJ_TYPE_DIR, penmError);
1980 if (pDir)
1981 {
1982 /*
1983 * We need a little bit of extra info for a drive root. These things are typically
1984 * inherited by subdirectories down the tree, so, we do it all here for till that changes.
1985 */
1986 union
1987 {
1988 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
1989 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
1990 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
1991 } uBuf;
1992 MY_IO_STATUS_BLOCK Ios;
1993 KBOOL fRc;
1994
1995 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
1996 pDir->hDir = hDir;
1997
1998 if (birdStatHandle(hDir, &pDir->Obj.Stats, pDir->Obj.pszName) == 0)
1999 {
2000 pDir->Obj.fHaveStats = K_TRUE;
2001 pDir->uDevNo = pDir->Obj.Stats.st_dev;
2002 }
2003 else
2004 {
2005 /* Just in case. */
2006 pDir->Obj.fHaveStats = K_FALSE;
2007 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
2008 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
2009 }
2010
2011 /* Get the file system. */
2012 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
2013 Ios.Information = -1;
2014 Ios.u.Status = -1;
2015 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
2016 MyFileFsAttributeInformation);
2017 if (MY_NT_SUCCESS(rcNt))
2018 rcNt = Ios.u.Status;
2019 if (MY_NT_SUCCESS(rcNt))
2020 {
2021 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N'
2022 && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
2023 && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
2024 && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
2025 && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
2026 {
2027 DWORD dwDriveType = GetDriveTypeW(wszTmp);
2028 if ( dwDriveType == DRIVE_FIXED
2029 || dwDriveType == DRIVE_RAMDISK)
2030 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
2031 }
2032 }
2033
2034 /*
2035 * Link the new drive letter into the root dir.
2036 */
2037 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
2038 kFsCacheObjRelease(pCache, &pDir->Obj);
2039 return fRc ? &pDir->Obj : NULL;
2040 }
2041
2042 g_pfnNtClose(hDir);
2043 return NULL;
2044 }
2045
2046 /* Assume it doesn't exist if this happens... This may be a little to
2047 restrictive wrt status code checks. */
2048 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
2049 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
2050 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
2051 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
2052 ("%#x\n", rcNt),
2053 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
2054 NULL);
2055 }
2056 else
2057 {
2058 kHlpAssertFailed();
2059 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
2060 return NULL;
2061 }
2062
2063 /*
2064 * Maybe create a missing entry.
2065 */
2066 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2067 {
2068 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
2069#ifdef KFSCACHE_CFG_SHORT_NAMES
2070 NULL, 0, NULL, 0,
2071#endif
2072 KFSOBJ_TYPE_MISSING, penmError);
2073 if (pMissing)
2074 {
2075 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
2076 kFsCacheObjRelease(pCache, pMissing);
2077 return fRc ? pMissing : NULL;
2078 }
2079 }
2080 else
2081 {
2082 /** @todo this isn't necessary correct for a root spec. */
2083 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2084 }
2085 return NULL;
2086}
2087
2088
2089/**
2090 * Look up a child node, ANSI version.
2091 *
2092 * @returns Pointer to the child if found, NULL if not.
2093 * @param pCache The cache.
2094 * @param pParent The parent directory to search.
2095 * @param pchName The child name to search for (not terminated).
2096 * @param cchName The length of the child name.
2097 */
2098static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
2099{
2100 /* Check for '.' first. */
2101 if (cchName != 1 || *pchName != '.')
2102 {
2103 KU32 cLeft;
2104 PKFSOBJ *ppCur;
2105
2106 if (pParent->paHashTab != NULL)
2107 {
2108 /** @todo directory hash table lookup. */
2109 }
2110
2111 /* Linear search. */
2112 cLeft = pParent->cChildren;
2113 ppCur = pParent->papChildren;
2114 while (cLeft-- > 0)
2115 {
2116 PKFSOBJ pCur = *ppCur++;
2117 if ( ( pCur->cchName == cchName
2118 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2119#ifdef KFSCACHE_CFG_SHORT_NAMES
2120 || ( pCur->cchShortName == cchName
2121 && pCur->pszShortName != pCur->pszName
2122 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2123#endif
2124 )
2125 return pCur;
2126 }
2127 return NULL;
2128 }
2129 return &pParent->Obj;
2130}
2131
2132
2133/**
2134 * Look up a child node, UTF-16 version.
2135 *
2136 * @returns Pointer to the child if found, NULL if not.
2137 * @param pCache The cache.
2138 * @param pParent The parent directory to search.
2139 * @param pwcName The child name to search for (not terminated).
2140 * @param cwcName The length of the child name (in wchar_t's).
2141 */
2142static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
2143{
2144 /* Check for '.' first. */
2145 if (cwcName != 1 || *pwcName != '.')
2146 {
2147 KU32 cLeft;
2148 PKFSOBJ *ppCur;
2149
2150 if (pParent->paHashTab != NULL)
2151 {
2152 /** @todo directory hash table lookup. */
2153 }
2154
2155 /* Linear search. */
2156 cLeft = pParent->cChildren;
2157 ppCur = pParent->papChildren;
2158 while (cLeft-- > 0)
2159 {
2160 PKFSOBJ pCur = *ppCur++;
2161 if ( ( pCur->cwcName == cwcName
2162 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2163#ifdef KFSCACHE_CFG_SHORT_NAMES
2164 || ( pCur->cwcShortName == cwcName
2165 && pCur->pwszShortName != pCur->pwszName
2166 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2167#endif
2168 )
2169 return pCur;
2170 }
2171 return NULL;
2172 }
2173 return &pParent->Obj;
2174}
2175
2176
2177/**
2178 * Looks up a UNC share, ANSI version.
2179 *
2180 * We keep both the server and share in the root directory entry. This means we
2181 * have to clean up the entry name before we can insert it.
2182 *
2183 * @returns Pointer to the share root directory or an update-to-date missing
2184 * node.
2185 * @param pCache The cache.
2186 * @param pszPath The path.
2187 * @param poff Where to return the root dire.
2188 * @param penmError Where to return details as to why the lookup
2189 * failed.
2190 */
2191static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
2192{
2193#if 0 /* later */
2194 KU32 offStartServer;
2195 KU32 offEndServer;
2196 KU32 offStartShare;
2197
2198 KU32 offEnd = 2;
2199 while (IS_SLASH(pszPath[offEnd]))
2200 offEnd++;
2201
2202 offStartServer = offEnd;
2203 while ( (ch = pszPath[offEnd]) != '\0'
2204 && !IS_SLASH(ch))
2205 offEnd++;
2206 offEndServer = offEnd;
2207
2208 if (ch != '\0')
2209 { /* likely */ }
2210 else
2211 {
2212 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2213 return NULL;
2214 }
2215
2216 while (IS_SLASH(pszPath[offEnd]))
2217 offEnd++;
2218 offStartServer = offEnd;
2219 while ( (ch = pszPath[offEnd]) != '\0'
2220 && !IS_SLASH(ch))
2221 offEnd++;
2222#endif
2223 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2224 return NULL;
2225}
2226
2227
2228/**
2229 * Looks up a UNC share, UTF-16 version.
2230 *
2231 * We keep both the server and share in the root directory entry. This means we
2232 * have to clean up the entry name before we can insert it.
2233 *
2234 * @returns Pointer to the share root directory or an update-to-date missing
2235 * node.
2236 * @param pCache The cache.
2237 * @param pwszPath The path.
2238 * @param poff Where to return the root dire.
2239 * @param penmError Where to return details as to why the lookup
2240 * failed.
2241 */
2242static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
2243{
2244#if 0 /* later */
2245 KU32 offStartServer;
2246 KU32 offEndServer;
2247 KU32 offStartShare;
2248
2249 KU32 offEnd = 2;
2250 while (IS_SLASH(pwszPath[offEnd]))
2251 offEnd++;
2252
2253 offStartServer = offEnd;
2254 while ( (ch = pwszPath[offEnd]) != '\0'
2255 && !IS_SLASH(ch))
2256 offEnd++;
2257 offEndServer = offEnd;
2258
2259 if (ch != '\0')
2260 { /* likely */ }
2261 else
2262 {
2263 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2264 return NULL;
2265 }
2266
2267 while (IS_SLASH(pwszPath[offEnd]))
2268 offEnd++;
2269 offStartServer = offEnd;
2270 while ( (ch = pwszPath[offEnd]) != '\0'
2271 && !IS_SLASH(ch))
2272 offEnd++;
2273#endif
2274 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2275 return NULL;
2276}
2277
2278
2279/**
2280 * Walks an full path relative to the given directory, ANSI version.
2281 *
2282 * This will create any missing nodes while walking.
2283 *
2284 * The caller will have to do the path hash table insertion of the result.
2285 *
2286 * @returns Pointer to the tree node corresponding to @a pszPath.
2287 * NULL on lookup failure, see @a penmError for details.
2288 * @param pCache The cache.
2289 * @param pParent The directory to start the lookup in.
2290 * @param pszPath The path to walk.
2291 * @param cchPath The length of the path.
2292 * @param penmError Where to return details as to why the lookup
2293 * failed.
2294 * @param ppLastAncestor Where to return the last parent element found
2295 * (referenced) in case of error an path/file not
2296 * found problem. Optional.
2297 */
2298PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath,
2299 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2300{
2301 /*
2302 * Walk loop.
2303 */
2304 KU32 off = 0;
2305 if (ppLastAncestor)
2306 *ppLastAncestor = NULL;
2307 for (;;)
2308 {
2309 PKFSOBJ pChild;
2310
2311 /*
2312 * Find the end of the component, counting trailing slashes.
2313 */
2314 char ch;
2315 KU32 cchSlashes = 0;
2316 KU32 offEnd = off + 1;
2317 while ((ch = pszPath[offEnd]) != '\0')
2318 {
2319 if (!IS_SLASH(ch))
2320 offEnd++;
2321 else
2322 {
2323 do
2324 cchSlashes++;
2325 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2326 break;
2327 }
2328 }
2329
2330 /*
2331 * Do we need to populate or refresh this directory first?
2332 */
2333 if ( pParent->fPopulated
2334 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2335 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
2336 { /* likely */ }
2337 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2338 { /* likely */ }
2339 else
2340 return NULL;
2341
2342 /*
2343 * Search the current node for the name.
2344 *
2345 * If we don't find it, we may insert a missing node depending on
2346 * the cache configuration.
2347 */
2348 pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
2349 if (pChild != NULL)
2350 { /* probably likely */ }
2351 else
2352 {
2353 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2354 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
2355 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
2356 {
2357 if (pChild)
2358 return kFsCacheObjRetainInternal(pChild);
2359 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2360 }
2361 else
2362 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2363 if (ppLastAncestor)
2364 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2365 return NULL;
2366 }
2367
2368 /* Advance off and check if we're done already. */
2369 off = offEnd + cchSlashes;
2370 if ( cchSlashes == 0
2371 || off >= cchPath)
2372 {
2373 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2374 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2375 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2376 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2377 { /* likely */ }
2378 else
2379 return NULL;
2380 return kFsCacheObjRetainInternal(pChild);
2381 }
2382
2383 /*
2384 * Check that it's a directory. If a missing entry, we may have to
2385 * refresh it and re-examin it.
2386 */
2387 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2388 pParent = (PKFSDIR)pChild;
2389 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2390 {
2391 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2392 if (ppLastAncestor)
2393 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2394 return NULL;
2395 }
2396 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2397 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
2398 {
2399 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2400 if (ppLastAncestor)
2401 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2402 return NULL;
2403 }
2404 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2405 pParent = (PKFSDIR)pChild;
2406 else
2407 {
2408 if (ppLastAncestor)
2409 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2410 return NULL;
2411 }
2412 }
2413
2414 return NULL;
2415
2416}
2417
2418
2419/**
2420 * Walks an full path relative to the given directory, UTF-16 version.
2421 *
2422 * This will create any missing nodes while walking.
2423 *
2424 * The caller will have to do the path hash table insertion of the result.
2425 *
2426 * @returns Pointer to the tree node corresponding to @a pszPath.
2427 * NULL on lookup failure, see @a penmError for details.
2428 * @param pCache The cache.
2429 * @param pParent The directory to start the lookup in.
2430 * @param pszPath The path to walk. No dot-dot bits allowed!
2431 * @param cchPath The length of the path.
2432 * @param penmError Where to return details as to why the lookup
2433 * failed.
2434 * @param ppLastAncestor Where to return the last parent element found
2435 * (referenced) in case of error an path/file not
2436 * found problem. Optional.
2437 */
2438PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath,
2439 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2440{
2441 /*
2442 * Walk loop.
2443 */
2444 KU32 off = 0;
2445 if (ppLastAncestor)
2446 *ppLastAncestor = NULL;
2447 for (;;)
2448 {
2449 PKFSOBJ pChild;
2450
2451 /*
2452 * Find the end of the component, counting trailing slashes.
2453 */
2454 wchar_t wc;
2455 KU32 cwcSlashes = 0;
2456 KU32 offEnd = off + 1;
2457 while ((wc = pwszPath[offEnd]) != '\0')
2458 {
2459 if (!IS_SLASH(wc))
2460 offEnd++;
2461 else
2462 {
2463 do
2464 cwcSlashes++;
2465 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2466 break;
2467 }
2468 }
2469
2470 /*
2471 * Do we need to populate or refresh this directory first?
2472 */
2473 if ( pParent->fPopulated
2474 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2475 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
2476 { /* likely */ }
2477 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2478 { /* likely */ }
2479 else
2480 return NULL;
2481
2482 /*
2483 * Search the current node for the name.
2484 *
2485 * If we don't find it, we may insert a missing node depending on
2486 * the cache configuration.
2487 */
2488 pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off);
2489 if (pChild != NULL)
2490 { /* probably likely */ }
2491 else
2492 {
2493 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2494 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
2495 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
2496 {
2497 if (pChild)
2498 return kFsCacheObjRetainInternal(pChild);
2499 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2500 }
2501 else
2502 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2503 if (ppLastAncestor)
2504 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2505 return NULL;
2506 }
2507
2508 /* Advance off and check if we're done already. */
2509 off = offEnd + cwcSlashes;
2510 if ( cwcSlashes == 0
2511 || off >= cwcPath)
2512 {
2513 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2514 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2515 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2516 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2517 { /* likely */ }
2518 else
2519 return NULL;
2520 return kFsCacheObjRetainInternal(pChild);
2521 }
2522
2523 /*
2524 * Check that it's a directory. If a missing entry, we may have to
2525 * refresh it and re-examin it.
2526 */
2527 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2528 pParent = (PKFSDIR)pChild;
2529 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2530 {
2531 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2532 if (ppLastAncestor)
2533 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2534 return NULL;
2535 }
2536 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2537 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
2538 {
2539 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2540 if (ppLastAncestor)
2541 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2542 return NULL;
2543 }
2544 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2545 pParent = (PKFSDIR)pChild;
2546 else
2547 {
2548 if (ppLastAncestor)
2549 *ppLastAncestor = kFsCacheObjRetainInternal(&pParent->Obj);
2550 return NULL;
2551 }
2552 }
2553
2554 return NULL;
2555
2556}
2557
2558/**
2559 * Walk the file system tree for the given absolute path, entering it into the
2560 * hash table.
2561 *
2562 * This will create any missing nodes while walking.
2563 *
2564 * The caller will have to do the path hash table insertion of the result.
2565 *
2566 * @returns Pointer to the tree node corresponding to @a pszPath.
2567 * NULL on lookup failure, see @a penmError for details.
2568 * @param pCache The cache.
2569 * @param pszPath The path to walk. No dot-dot bits allowed!
2570 * @param cchPath The length of the path.
2571 * @param penmError Where to return details as to why the lookup
2572 * failed.
2573 * @param ppLastAncestor Where to return the last parent element found
2574 * (referenced) in case of error an path/file not
2575 * found problem. Optional.
2576 */
2577static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath,
2578 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2579{
2580 PKFSOBJ pRoot;
2581 KU32 cchSlashes;
2582 KU32 offEnd;
2583
2584 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
2585
2586 /*
2587 * The root "directory" needs special handling, so we keep it outside the
2588 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
2589 */
2590 cchSlashes = 0;
2591 if ( pszPath[1] == ':'
2592 && IS_ALPHA(pszPath[0]))
2593 {
2594 /* Drive letter. */
2595 offEnd = 2;
2596 kHlpAssert(IS_SLASH(pszPath[2]));
2597 pRoot = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);
2598 }
2599 else if ( IS_SLASH(pszPath[0])
2600 && IS_SLASH(pszPath[1]) )
2601 pRoot = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);
2602 else
2603 {
2604 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2605 return NULL;
2606 }
2607 if (pRoot)
2608 { /* likely */ }
2609 else
2610 return NULL;
2611
2612 /* Count slashes trailing the root spec. */
2613 if (offEnd < cchPath)
2614 {
2615 kHlpAssert(IS_SLASH(pszPath[offEnd]));
2616 do
2617 cchSlashes++;
2618 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2619 }
2620
2621 /* Done already? */
2622 if (offEnd >= cchPath)
2623 {
2624 if ( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2625 || pRoot->uCacheGen == ( pRoot->bObjType != KFSOBJ_TYPE_MISSING
2626 ? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2627 : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
2628 || kFsCacheRefreshObj(pCache, pRoot, penmError))
2629 return kFsCacheObjRetainInternal(pRoot);
2630 return NULL;
2631 }
2632
2633 /* Check that we've got a valid result and not a cached negative one. */
2634 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
2635 { /* likely */ }
2636 else
2637 {
2638 kHlpAssert(pRoot->bObjType == KFSOBJ_TYPE_MISSING);
2639 kHlpAssert( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2640 || pRoot->uCacheGen == pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]);
2641 return pRoot;
2642 }
2643
2644 /*
2645 * Now that we've found a valid root directory, lookup the
2646 * remainder of the path starting with it.
2647 */
2648 return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pRoot, &pszPath[offEnd + cchSlashes],
2649 cchPath - offEnd - cchSlashes, penmError, ppLastAncestor);
2650}
2651
2652
2653/**
2654 * Walk the file system tree for the given absolute path, UTF-16 version.
2655 *
2656 * This will create any missing nodes while walking.
2657 *
2658 * The caller will have to do the path hash table insertion of the result.
2659 *
2660 * @returns Pointer to the tree node corresponding to @a pszPath.
2661 * NULL on lookup failure, see @a penmError for details.
2662 * @param pCache The cache.
2663 * @param pwszPath The path to walk.
2664 * @param cwcPath The length of the path (in wchar_t's).
2665 * @param penmError Where to return details as to why the lookup
2666 * failed.
2667 * @param ppLastAncestor Where to return the last parent element found
2668 * (referenced) in case of error an path/file not
2669 * found problem. Optional.
2670 */
2671static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath,
2672 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2673{
2674 PKFSDIR pParent = &pCache->RootDir;
2675 PKFSOBJ pRoot;
2676 KU32 off;
2677 KU32 cwcSlashes;
2678 KU32 offEnd;
2679
2680 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath));
2681
2682 /*
2683 * The root "directory" needs special handling, so we keep it outside the
2684 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
2685 */
2686 cwcSlashes = 0;
2687 off = 0;
2688 if ( pwszPath[1] == ':'
2689 && IS_ALPHA(pwszPath[0]))
2690 {
2691 /* Drive letter. */
2692 offEnd = 2;
2693 kHlpAssert(IS_SLASH(pwszPath[2]));
2694 pRoot = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError);
2695 }
2696 else if ( IS_SLASH(pwszPath[0])
2697 && IS_SLASH(pwszPath[1]) )
2698 pRoot = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError);
2699 else
2700 {
2701 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2702 return NULL;
2703 }
2704 if (pRoot)
2705 { /* likely */ }
2706 else
2707 return NULL;
2708
2709 /* Count slashes trailing the root spec. */
2710 if (offEnd < cwcPath)
2711 {
2712 kHlpAssert(IS_SLASH(pwszPath[offEnd]));
2713 do
2714 cwcSlashes++;
2715 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2716 }
2717
2718 /* Done already? */
2719 if (offEnd >= cwcPath)
2720 {
2721 if ( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2722 || pRoot->uCacheGen == (pRoot->bObjType != KFSOBJ_TYPE_MISSING
2723 ? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2724 : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
2725 || kFsCacheRefreshObj(pCache, pRoot, penmError))
2726 return kFsCacheObjRetainInternal(pRoot);
2727 return NULL;
2728 }
2729
2730 /* Check that we've got a valid result and not a cached negative one. */
2731 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
2732 { /* likely */ }
2733 else
2734 {
2735 kHlpAssert(pRoot->bObjType == KFSOBJ_TYPE_MISSING);
2736 kHlpAssert( pRoot->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2737 || pRoot->uCacheGen == pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]);
2738 return pRoot;
2739 }
2740
2741 /*
2742 * Now that we've found a valid root directory, lookup the
2743 * remainder of the path starting with it.
2744 */
2745 return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pRoot, &pwszPath[offEnd + cwcSlashes],
2746 cwcPath - offEnd - cwcSlashes, penmError, ppLastAncestor);
2747}
2748
2749
2750/**
2751 * This deals with paths that are relative and paths that contains '..'
2752 * elements, ANSI version.
2753 *
2754 * @returns Pointer to object corresponding to @a pszPath on success.
2755 * NULL if this isn't a path we care to cache.
2756 *
2757 * @param pCache The cache.
2758 * @param pszPath The path.
2759 * @param cchPath The length of the path.
2760 * @param penmError Where to return details as to why the lookup
2761 * failed.
2762 * @param ppLastAncestor Where to return the last parent element found
2763 * (referenced) in case of error an path/file not
2764 * found problem. Optional.
2765 */
2766static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath,
2767 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2768{
2769 /*
2770 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2771 * ends up calling it anyway.
2772 */
2773 char szFull[KFSCACHE_CFG_MAX_PATH];
2774 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
2775 if ( cchFull >= 3
2776 && cchFull < sizeof(szFull))
2777 {
2778 PKFSOBJ pFsObj;
2779 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath));
2780 pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError, ppLastAncestor);
2781
2782#if 0 /* No need to do this until it's actually queried. */
2783 /* Cache the resulting path. */
2784 if ( pFsObj
2785 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2786 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2787 {
2788 KU32 uHashPath = kFsCacheStrHash(szFull);
2789 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2790 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2791 }
2792#endif
2793 return pFsObj;
2794 }
2795
2796 /* The path is too long! */
2797 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
2798 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2799 return NULL;
2800}
2801
2802
2803/**
2804 * This deals with paths that are relative and paths that contains '..'
2805 * elements, UTF-16 version.
2806 *
2807 * @returns Pointer to object corresponding to @a pszPath on success.
2808 * NULL if this isn't a path we care to cache.
2809 *
2810 * @param pCache The cache.
2811 * @param pwszPath The path.
2812 * @param cwcPath The length of the path (in wchar_t's).
2813 * @param penmError Where to return details as to why the lookup
2814 * failed.
2815 * @param ppLastAncestor Where to return the last parent element found
2816 * (referenced) in case of error an path/file not
2817 * found problem. Optional.
2818 */
2819static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath,
2820 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
2821{
2822 /*
2823 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2824 * ends up calling it anyway.
2825 */
2826 wchar_t wszFull[KFSCACHE_CFG_MAX_PATH];
2827 UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL);
2828 if ( cwcFull >= 3
2829 && cwcFull < KFSCACHE_CFG_MAX_PATH)
2830 {
2831 PKFSOBJ pFsObj;
2832 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
2833 pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError, ppLastAncestor);
2834
2835#if 0 /* No need to do this until it's actually queried. */
2836 /* Cache the resulting path. */
2837 if ( pFsObj
2838 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2839 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2840 {
2841 KU32 uHashPath = kFsCacheStrHash(szFull);
2842 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2843 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2844 }
2845#endif
2846 return pFsObj;
2847 }
2848
2849 /* The path is too long! */
2850 kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull));
2851 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2852 return NULL;
2853}
2854
2855
2856/**
2857 * Refreshes a path hash that has expired, ANSI version.
2858 *
2859 * @returns pHash on success, NULL if removed.
2860 * @param pCache The cache.
2861 * @param pHashEntry The path hash.
2862 * @param idxHashTab The hash table entry.
2863 */
2864static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
2865{
2866 PKFSOBJ pLastAncestor = NULL;
2867 if (!pHashEntry->pFsObj)
2868 {
2869 if (pHashEntry->fAbsolute)
2870 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
2871 &pHashEntry->enmError, &pLastAncestor);
2872 else
2873 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
2874 &pHashEntry->enmError, &pLastAncestor);
2875 }
2876 else
2877 {
2878 KU8 bOldType = pHashEntry->pFsObj->bObjType;
2879 KFSLOOKUPERROR enmError;
2880 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
2881 {
2882 if (pHashEntry->pFsObj->bObjType == bOldType)
2883 { }
2884 else
2885 {
2886 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
2887 if (pHashEntry->fAbsolute)
2888 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
2889 &pHashEntry->enmError, &pLastAncestor);
2890 else
2891 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
2892 &pHashEntry->enmError, &pLastAncestor);
2893 }
2894 }
2895 else
2896 {
2897 fprintf(stderr, "kFsCacheRefreshPathA - refresh failure handling not implemented!\n");
2898 __debugbreak();
2899 /** @todo just remove this entry. */
2900 return NULL;
2901 }
2902 }
2903
2904 if (pLastAncestor && !pHashEntry->pFsObj)
2905 pHashEntry->idxMissingGen = pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN;
2906 pHashEntry->uCacheGen = !pHashEntry->pFsObj
2907 ? pCache->auGenerationsMissing[pHashEntry->idxMissingGen]
2908 : pHashEntry->pFsObj->bObjType == KFSOBJ_TYPE_MISSING
2909 ? pCache->auGenerationsMissing[pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2910 : pCache->auGenerations[ pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
2911 if (pLastAncestor)
2912 kFsCacheObjRelease(pCache, pLastAncestor);
2913 return pHashEntry;
2914}
2915
2916
2917/**
2918 * Refreshes a path hash that has expired, UTF-16 version.
2919 *
2920 * @returns pHash on success, NULL if removed.
2921 * @param pCache The cache.
2922 * @param pHashEntry The path hash.
2923 * @param idxHashTab The hash table entry.
2924 */
2925static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab)
2926{
2927 PKFSOBJ pLastAncestor = NULL;
2928 if (!pHashEntry->pFsObj)
2929 {
2930 if (pHashEntry->fAbsolute)
2931 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
2932 &pHashEntry->enmError, &pLastAncestor);
2933 else
2934 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
2935 &pHashEntry->enmError, &pLastAncestor);
2936 }
2937 else
2938 {
2939 KU8 bOldType = pHashEntry->pFsObj->bObjType;
2940 KFSLOOKUPERROR enmError;
2941 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
2942 {
2943 if (pHashEntry->pFsObj->bObjType == bOldType)
2944 { }
2945 else
2946 {
2947 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
2948 if (pHashEntry->fAbsolute)
2949 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
2950 &pHashEntry->enmError, &pLastAncestor);
2951 else
2952 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
2953 &pHashEntry->enmError, &pLastAncestor);
2954 }
2955 }
2956 else
2957 {
2958 fprintf(stderr, "kFsCacheRefreshPathW - refresh failure handling not implemented!\n");
2959 __debugbreak();
2960 /** @todo just remove this entry. */
2961 return NULL;
2962 }
2963 }
2964 if (pLastAncestor && !pHashEntry->pFsObj)
2965 pHashEntry->idxMissingGen = pLastAncestor->fFlags & KFSOBJ_F_USE_CUSTOM_GEN;
2966 pHashEntry->uCacheGen = !pHashEntry->pFsObj
2967 ? pCache->auGenerationsMissing[pHashEntry->idxMissingGen]
2968 : pHashEntry->pFsObj->bObjType == KFSOBJ_TYPE_MISSING
2969 ? pCache->auGenerationsMissing[pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
2970 : pCache->auGenerations[ pHashEntry->pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN];
2971 if (pLastAncestor)
2972 kFsCacheObjRelease(pCache, pLastAncestor);
2973 return pHashEntry;
2974}
2975
2976
2977/**
2978 * Internal lookup worker that looks up a KFSOBJ for the given ANSI path with
2979 * length and hash.
2980 *
2981 * This will first try the hash table. If not in the hash table, the file
2982 * system cache tree is walked, missing bits filled in and finally a hash table
2983 * entry is created.
2984 *
2985 * Only drive letter paths are cachable. We don't do any UNC paths at this
2986 * point.
2987 *
2988 * @returns Reference to object corresponding to @a pszPath on success, this
2989 * must be released by kFsCacheObjRelease.
2990 * NULL if not a path we care to cache.
2991 * @param pCache The cache.
2992 * @param pchPath The path to lookup.
2993 * @param cchPath The path length.
2994 * @param uHashPath The hash of the path.
2995 * @param penmError Where to return details as to why the lookup
2996 * failed.
2997 */
2998static PKFSOBJ kFsCacheLookupHashedA(PKFSCACHE pCache, const char *pchPath, KU32 cchPath, KU32 uHashPath,
2999 KFSLOOKUPERROR *penmError)
3000{
3001 /*
3002 * Do hash table lookup of the path.
3003 */
3004 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3005 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab];
3006 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3007 if (pHashEntry)
3008 {
3009 do
3010 {
3011 if ( pHashEntry->uHashPath == uHashPath
3012 && pHashEntry->cchPath == cchPath
3013 && kHlpMemComp(pHashEntry->pszPath, pchPath, cchPath) == 0)
3014 {
3015 PKFSOBJ pFsObj;
3016 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3017 || pHashEntry->uCacheGen == ( (pFsObj = pHashEntry->pFsObj) != NULL
3018 ? pFsObj->bObjType != KFSOBJ_TYPE_MISSING
3019 ? pCache->auGenerations[ pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3020 : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3021 : pCache->auGenerationsMissing[pHashEntry->idxMissingGen])
3022 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
3023 {
3024 pCache->cLookups++;
3025 pCache->cPathHashHits++;
3026 KFSCACHE_LOG2(("kFsCacheLookupA(%*.*s) - hit %p\n", cchPath, cchPath, pchPath, pHashEntry->pFsObj));
3027 *penmError = pHashEntry->enmError;
3028 if (pHashEntry->pFsObj)
3029 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3030 return NULL;
3031 }
3032 break;
3033 }
3034 pHashEntry = pHashEntry->pNext;
3035 } while (pHashEntry);
3036 }
3037
3038 /*
3039 * Create an entry for it by walking the file system cache and filling in the blanks.
3040 */
3041 if ( cchPath > 0
3042 && cchPath < KFSCACHE_CFG_MAX_PATH)
3043 {
3044 PKFSOBJ pFsObj;
3045 KBOOL fAbsolute;
3046 PKFSOBJ pLastAncestor = NULL;
3047
3048 /* Is absolute without any '..' bits? */
3049 if ( cchPath >= 3
3050 && ( ( pchPath[1] == ':' /* Drive letter */
3051 && IS_SLASH(pchPath[2])
3052 && IS_ALPHA(pchPath[0]) )
3053 || ( IS_SLASH(pchPath[0]) /* UNC */
3054 && IS_SLASH(pchPath[1]) ) )
3055 && !kFsCacheHasDotDotA(pchPath, cchPath) )
3056 {
3057 pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, penmError, &pLastAncestor);
3058 fAbsolute = K_TRUE;
3059 }
3060 else
3061 {
3062 pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, penmError, &pLastAncestor);
3063 fAbsolute = K_FALSE;
3064 }
3065 if ( pFsObj
3066 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3067 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3068 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3069 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pchPath, cchPath, uHashPath, idxHashTab, fAbsolute,
3070 pLastAncestor ? pLastAncestor->bObjType & KFSOBJ_F_USE_CUSTOM_GEN : 0, *penmError);
3071 if (pLastAncestor)
3072 kFsCacheObjRelease(pCache, pLastAncestor);
3073
3074 pCache->cLookups++;
3075 if (pFsObj)
3076 pCache->cWalkHits++;
3077 return pFsObj;
3078 }
3079
3080 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3081 return NULL;
3082}
3083
3084
3085/**
3086 * Internal lookup worker that looks up a KFSOBJ for the given UTF-16 path with
3087 * length and hash.
3088 *
3089 * This will first try the hash table. If not in the hash table, the file
3090 * system cache tree is walked, missing bits filled in and finally a hash table
3091 * entry is created.
3092 *
3093 * Only drive letter paths are cachable. We don't do any UNC paths at this
3094 * point.
3095 *
3096 * @returns Reference to object corresponding to @a pwcPath on success, this
3097 * must be released by kFsCacheObjRelease.
3098 * NULL if not a path we care to cache.
3099 * @param pCache The cache.
3100 * @param pwcPath The path to lookup.
3101 * @param cwcPath The length of the path (in wchar_t's).
3102 * @param uHashPath The hash of the path.
3103 * @param penmError Where to return details as to why the lookup
3104 * failed.
3105 */
3106static PKFSOBJ kFsCacheLookupHashedW(PKFSCACHE pCache, const wchar_t *pwcPath, KU32 cwcPath, KU32 uHashPath,
3107 KFSLOOKUPERROR *penmError)
3108{
3109 /*
3110 * Do hash table lookup of the path.
3111 */
3112 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3113 PKFSHASHW pHashEntry = pCache->apUtf16Paths[idxHashTab];
3114 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3115 if (pHashEntry)
3116 {
3117 do
3118 {
3119 if ( pHashEntry->uHashPath == uHashPath
3120 && pHashEntry->cwcPath == cwcPath
3121 && kHlpMemComp(pHashEntry->pwszPath, pwcPath, cwcPath) == 0)
3122 {
3123 PKFSOBJ pFsObj;
3124 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3125 || pHashEntry->uCacheGen == ((pFsObj = pHashEntry->pFsObj) != NULL
3126 ? pFsObj->bObjType != KFSOBJ_TYPE_MISSING
3127 ? pCache->auGenerations[ pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3128 : pCache->auGenerationsMissing[pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
3129 : pCache->auGenerationsMissing[pHashEntry->idxMissingGen])
3130 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) )
3131 {
3132 pCache->cLookups++;
3133 pCache->cPathHashHits++;
3134 KFSCACHE_LOG2(("kFsCacheLookupW(%*.*ls) - hit %p\n", cwcPath, cwcPath, pwcPath, pHashEntry->pFsObj));
3135 *penmError = pHashEntry->enmError;
3136 if (pHashEntry->pFsObj)
3137 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3138 return NULL;
3139 }
3140 break;
3141 }
3142 pHashEntry = pHashEntry->pNext;
3143 } while (pHashEntry);
3144 }
3145
3146 /*
3147 * Create an entry for it by walking the file system cache and filling in the blanks.
3148 */
3149 if ( cwcPath > 0
3150 && cwcPath < KFSCACHE_CFG_MAX_PATH)
3151 {
3152 PKFSOBJ pFsObj;
3153 KBOOL fAbsolute;
3154 PKFSOBJ pLastAncestor = NULL;
3155
3156 /* Is absolute without any '..' bits? */
3157 if ( cwcPath >= 3
3158 && ( ( pwcPath[1] == ':' /* Drive letter */
3159 && IS_SLASH(pwcPath[2])
3160 && IS_ALPHA(pwcPath[0]) )
3161 || ( IS_SLASH(pwcPath[0]) /* UNC */
3162 && IS_SLASH(pwcPath[1]) ) )
3163 && !kFsCacheHasDotDotW(pwcPath, cwcPath) )
3164 {
3165 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, penmError, &pLastAncestor);
3166 fAbsolute = K_TRUE;
3167 }
3168 else
3169 {
3170 pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, penmError, &pLastAncestor);
3171 fAbsolute = K_FALSE;
3172 }
3173 if ( pFsObj
3174 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3175 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3176 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3177 kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwcPath, cwcPath, uHashPath, idxHashTab, fAbsolute,
3178 pLastAncestor ? pLastAncestor->bObjType & KFSOBJ_F_USE_CUSTOM_GEN : 0, *penmError);
3179 if (pLastAncestor)
3180 kFsCacheObjRelease(pCache, pLastAncestor);
3181
3182 pCache->cLookups++;
3183 if (pFsObj)
3184 pCache->cWalkHits++;
3185 return pFsObj;
3186 }
3187
3188 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3189 return NULL;
3190}
3191
3192
3193
3194/**
3195 * Looks up a KFSOBJ for the given ANSI path.
3196 *
3197 * This will first try the hash table. If not in the hash table, the file
3198 * system cache tree is walked, missing bits filled in and finally a hash table
3199 * entry is created.
3200 *
3201 * Only drive letter paths are cachable. We don't do any UNC paths at this
3202 * point.
3203 *
3204 * @returns Reference to object corresponding to @a pszPath on success, this
3205 * must be released by kFsCacheObjRelease.
3206 * NULL if not a path we care to cache.
3207 * @param pCache The cache.
3208 * @param pszPath The path to lookup.
3209 * @param penmError Where to return details as to why the lookup
3210 * failed.
3211 */
3212PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3213{
3214 KU32 uHashPath;
3215 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
3216 return kFsCacheLookupHashedA(pCache, pszPath, cchPath, uHashPath, penmError);
3217}
3218
3219
3220/**
3221 * Looks up a KFSOBJ for the given UTF-16 path.
3222 *
3223 * This will first try the hash table. If not in the hash table, the file
3224 * system cache tree is walked, missing bits filled in and finally a hash table
3225 * entry is created.
3226 *
3227 * Only drive letter paths are cachable. We don't do any UNC paths at this
3228 * point.
3229 *
3230 * @returns Reference to object corresponding to @a pwszPath on success, this
3231 * must be released by kFsCacheObjRelease.
3232 * NULL if not a path we care to cache.
3233 * @param pCache The cache.
3234 * @param pwszPath The path to lookup.
3235 * @param penmError Where to return details as to why the lookup
3236 * failed.
3237 */
3238PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3239{
3240 KU32 uHashPath;
3241 KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath);
3242 return kFsCacheLookupHashedW(pCache, pwszPath, cwcPath, uHashPath, penmError);
3243}
3244
3245
3246/**
3247 * Looks up a KFSOBJ for the given ANSI path.
3248 *
3249 * This will first try the hash table. If not in the hash table, the file
3250 * system cache tree is walked, missing bits filled in and finally a hash table
3251 * entry is created.
3252 *
3253 * Only drive letter paths are cachable. We don't do any UNC paths at this
3254 * point.
3255 *
3256 * @returns Reference to object corresponding to @a pchPath on success, this
3257 * must be released by kFsCacheObjRelease.
3258 * NULL if not a path we care to cache.
3259 * @param pCache The cache.
3260 * @param pchPath The path to lookup (does not need to be nul
3261 * terminated).
3262 * @param cchPath The path length.
3263 * @param penmError Where to return details as to why the lookup
3264 * failed.
3265 */
3266PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError)
3267{
3268 return kFsCacheLookupHashedA(pCache, pchPath, (KU32)cchPath, kFsCacheStrHashN(pchPath, cchPath), penmError);
3269}
3270
3271
3272/**
3273 * Looks up a KFSOBJ for the given UTF-16 path.
3274 *
3275 * This will first try the hash table. If not in the hash table, the file
3276 * system cache tree is walked, missing bits filled in and finally a hash table
3277 * entry is created.
3278 *
3279 * Only drive letter paths are cachable. We don't do any UNC paths at this
3280 * point.
3281 *
3282 * @returns Reference to object corresponding to @a pwchPath on success, this
3283 * must be released by kFsCacheObjRelease.
3284 * NULL if not a path we care to cache.
3285 * @param pCache The cache.
3286 * @param pwcPath The path to lookup (does not need to be nul
3287 * terminated).
3288 * @param cwcPath The path length (in wchar_t's).
3289 * @param penmError Where to return details as to why the lookup
3290 * failed.
3291 */
3292PKFSOBJ kFsCacheLookupWithLengthW(PKFSCACHE pCache, const wchar_t *pwcPath, KSIZE cwcPath, KFSLOOKUPERROR *penmError)
3293{
3294 return kFsCacheLookupHashedW(pCache, pwcPath, (KU32)cwcPath, kFsCacheUtf16HashN(pwcPath, cwcPath), penmError);
3295}
3296
3297
3298/**
3299 * Wrapper around kFsCacheLookupA that drops KFSOBJ_TYPE_MISSING and returns
3300 * KFSLOOKUPERROR_NOT_FOUND instead.
3301 *
3302 * @returns Reference to object corresponding to @a pszPath on success, this
3303 * must be released by kFsCacheObjRelease.
3304 * NULL if not a path we care to cache.
3305 * @param pCache The cache.
3306 * @param pszPath The path to lookup.
3307 * @param penmError Where to return details as to why the lookup
3308 * failed.
3309 */
3310PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3311{
3312 PKFSOBJ pObj = kFsCacheLookupA(pCache, pszPath, penmError);
3313 if (pObj)
3314 {
3315 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3316 return pObj;
3317
3318 kFsCacheObjRelease(pCache, pObj);
3319 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3320 }
3321 return NULL;
3322}
3323
3324
3325/**
3326 * Wrapper around kFsCacheLookupW that drops KFSOBJ_TYPE_MISSING and returns
3327 * KFSLOOKUPERROR_NOT_FOUND instead.
3328 *
3329 * @returns Reference to object corresponding to @a pszPath on success, this
3330 * must be released by kFsCacheObjRelease.
3331 * NULL if not a path we care to cache.
3332 * @param pCache The cache.
3333 * @param pwszPath The path to lookup.
3334 * @param penmError Where to return details as to why the lookup
3335 * failed.
3336 */
3337PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3338{
3339 PKFSOBJ pObj = kFsCacheLookupW(pCache, pwszPath, penmError);
3340 if (pObj)
3341 {
3342 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3343 return pObj;
3344
3345 kFsCacheObjRelease(pCache, pObj);
3346 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3347 }
3348 return NULL;
3349}
3350
3351
3352/**
3353 * Destroys a cache object which has a zero reference count.
3354 *
3355 * @returns 0
3356 * @param pCache The cache.
3357 * @param pObj The object.
3358 */
3359KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
3360{
3361 kHlpAssert(pObj->cRefs == 0);
3362 kHlpAssert(pObj->pParent == NULL);
3363 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3364
3365 KFSCACHE_LOG(("Destroying %s/%s, type=%d\n", pObj->pParent ? pObj->pParent->Obj.pszName : "", pObj->pszName, pObj->bObjType));
3366
3367 /*
3368 * Invalidate the structure.
3369 */
3370 pObj->u32Magic = ~KFSOBJ_MAGIC;
3371
3372 /*
3373 * Destroy any user data first.
3374 */
3375 while (pObj->pUserDataHead != NULL)
3376 {
3377 PKFSUSERDATA pUserData = pObj->pUserDataHead;
3378 pObj->pUserDataHead = pUserData->pNext;
3379 if (pUserData->pfnDestructor)
3380 pUserData->pfnDestructor(pCache, pObj, pUserData);
3381 kHlpFree(pUserData);
3382 }
3383
3384 /*
3385 * Do type specific destruction
3386 */
3387 switch (pObj->bObjType)
3388 {
3389 case KFSOBJ_TYPE_MISSING:
3390 /* nothing else to do here */
3391 pCache->cbObjects -= sizeof(KFSDIR);
3392 break;
3393
3394 case KFSOBJ_TYPE_DIR:
3395 {
3396 PKFSDIR pDir = (PKFSDIR)pObj;
3397 KU32 cChildren = pDir->cChildren;
3398 pCache->cbObjects -= sizeof(*pDir)
3399 + K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
3400 + pDir->cHashTab * sizeof(pDir->paHashTab);
3401
3402 pDir->cChildren = 0;
3403 while (cChildren-- > 0)
3404 kFsCacheObjRelease(pCache, pDir->papChildren[cChildren]);
3405 kHlpFree(pDir->papChildren);
3406 pDir->papChildren = NULL;
3407
3408 kHlpFree(pDir->paHashTab);
3409 pDir->paHashTab = NULL;
3410 break;
3411 }
3412
3413 case KFSOBJ_TYPE_FILE:
3414 case KFSOBJ_TYPE_OTHER:
3415 pCache->cbObjects -= sizeof(*pObj);
3416 break;
3417
3418 default:
3419 return 0;
3420 }
3421
3422 /*
3423 * Common bits.
3424 */
3425 pCache->cbObjects -= pObj->cchName + 1;
3426#ifdef KFSCACHE_CFG_UTF16
3427 pCache->cbObjects -= (pObj->cwcName + 1) * sizeof(wchar_t);
3428#endif
3429#ifdef KFSCACHE_CFG_SHORT_NAMES
3430 if (pObj->pszName != pObj->pszShortName)
3431 {
3432 pCache->cbObjects -= pObj->cchShortName + 1;
3433# ifdef KFSCACHE_CFG_UTF16
3434 pCache->cbObjects -= (pObj->cwcShortName + 1) * sizeof(wchar_t);
3435# endif
3436 }
3437#endif
3438 pCache->cObjects--;
3439
3440 kHlpFree(pObj);
3441 return 0;
3442}
3443
3444
3445/**
3446 * Releases a reference to a cache object.
3447 *
3448 * @returns New reference count.
3449 * @param pCache The cache.
3450 * @param pObj The object.
3451 */
3452KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
3453{
3454 if (pObj)
3455 {
3456 KU32 cRefs;
3457 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3458 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3459
3460 cRefs = --pObj->cRefs;
3461 if (cRefs)
3462 return cRefs;
3463 return kFsCacheObjDestroy(pCache, pObj);
3464 }
3465 return 0;
3466}
3467
3468
3469/**
3470 * Retains a reference to a cahce object.
3471 *
3472 * @returns New reference count.
3473 * @param pObj The object.
3474 */
3475KU32 kFsCacheObjRetain(PKFSOBJ pObj)
3476{
3477 KU32 cRefs;
3478 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3479 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3480
3481 cRefs = ++pObj->cRefs;
3482 kHlpAssert(cRefs < 16384);
3483 return cRefs;
3484}
3485
3486
3487/**
3488 * Associates an item of user data with the given object.
3489 *
3490 * If the data needs cleaning up before being free, set the
3491 * PKFSUSERDATA::pfnDestructor member of the returned structure.
3492 *
3493 * @returns Pointer to the user data on success.
3494 * NULL if out of memory or key already in use.
3495 *
3496 * @param pCache The cache.
3497 * @param pObj The object.
3498 * @param uKey The user data key.
3499 * @param cbUserData The size of the user data.
3500 */
3501PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData)
3502{
3503 kHlpAssert(cbUserData >= sizeof(*pNew));
3504 if (kFsCacheObjGetUserData(pCache, pObj, uKey) == NULL)
3505 {
3506 PKFSUSERDATA pNew = (PKFSUSERDATA)kHlpAllocZ(cbUserData);
3507 if (pNew)
3508 {
3509 pNew->uKey = uKey;
3510 pNew->pfnDestructor = NULL;
3511 pNew->pNext = pObj->pUserDataHead;
3512 pObj->pUserDataHead = pNew;
3513 return pNew;
3514 }
3515 }
3516
3517 return NULL;
3518}
3519
3520
3521/**
3522 * Retrieves an item of user data associated with the given object.
3523 *
3524 * @returns Pointer to the associated user data if found, otherwise NULL.
3525 * @param pCache The cache.
3526 * @param pObj The object.
3527 * @param uKey The user data key.
3528 */
3529PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey)
3530{
3531 PKFSUSERDATA pCur;
3532
3533 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3534 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3535
3536 for (pCur = pObj->pUserDataHead; pCur; pCur = pCur->pNext)
3537 if (pCur->uKey == uKey)
3538 return pCur;
3539 return NULL;
3540}
3541
3542
3543/**
3544 * Gets the full path to @a pObj, ANSI version.
3545 *
3546 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3547 * @param pObj The object to get the full path to.
3548 * @param pszPath Where to return the path
3549 * @param cbPath The size of the output buffer.
3550 * @param chSlash The slash to use.
3551 */
3552KBOOL kFsCacheObjGetFullPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
3553{
3554 KSIZE off = pObj->cchParent;
3555 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3556 if (off > 0)
3557 {
3558 KSIZE offEnd = off + pObj->cchName;
3559 if (offEnd < cbPath)
3560 {
3561 PKFSDIR pAncestor;
3562
3563 pszPath[off + pObj->cchName] = '\0';
3564 memcpy(&pszPath[off], pObj->pszName, pObj->cchName);
3565
3566 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3567 {
3568 kHlpAssert(off > 1);
3569 kHlpAssert(pAncestor != NULL);
3570 kHlpAssert(pAncestor->Obj.cchName > 0);
3571 pszPath[--off] = chSlash;
3572 off -= pAncestor->Obj.cchName;
3573 kHlpAssert(pAncestor->Obj.cchParent == off);
3574 memcpy(&pszPath[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3575 }
3576 return K_TRUE;
3577 }
3578 }
3579 else
3580 {
3581 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
3582 off = pObj->cchName;
3583 if (off + fDriveLetter < cbPath)
3584 {
3585 memcpy(pszPath, pObj->pszName, off);
3586 if (fDriveLetter)
3587 pszPath[off++] = chSlash;
3588 pszPath[off] = '\0';
3589 return K_TRUE;
3590 }
3591 }
3592
3593 return K_FALSE;
3594}
3595
3596
3597/**
3598 * Gets the full path to @a pObj, UTF-16 version.
3599 *
3600 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3601 * @param pObj The object to get the full path to.
3602 * @param pszPath Where to return the path
3603 * @param cbPath The size of the output buffer.
3604 * @param wcSlash The slash to use.
3605 */
3606KBOOL kFsCacheObjGetFullPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
3607{
3608 KSIZE off = pObj->cwcParent;
3609 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3610 if (off > 0)
3611 {
3612 KSIZE offEnd = off + pObj->cwcName;
3613 if (offEnd < cwcPath)
3614 {
3615 PKFSDIR pAncestor;
3616
3617 pwszPath[off + pObj->cwcName] = '\0';
3618 memcpy(&pwszPath[off], pObj->pwszName, pObj->cwcName * sizeof(wchar_t));
3619
3620 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3621 {
3622 kHlpAssert(off > 1);
3623 kHlpAssert(pAncestor != NULL);
3624 kHlpAssert(pAncestor->Obj.cwcName > 0);
3625 pwszPath[--off] = wcSlash;
3626 off -= pAncestor->Obj.cwcName;
3627 kHlpAssert(pAncestor->Obj.cwcParent == off);
3628 memcpy(&pwszPath[off], pAncestor->Obj.pwszName, pAncestor->Obj.cwcName * sizeof(wchar_t));
3629 }
3630 return K_TRUE;
3631 }
3632 }
3633 else
3634 {
3635 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
3636 off = pObj->cwcName;
3637 if (off + fDriveLetter < cwcPath)
3638 {
3639 memcpy(pwszPath, pObj->pwszName, off * sizeof(wchar_t));
3640 if (fDriveLetter)
3641 pwszPath[off++] = wcSlash;
3642 pwszPath[off] = '\0';
3643 return K_TRUE;
3644 }
3645 }
3646
3647 return K_FALSE;
3648}
3649
3650
3651#ifdef KFSCACHE_CFG_SHORT_NAMES
3652
3653/**
3654 * Gets the full short path to @a pObj, ANSI version.
3655 *
3656 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3657 * @param pObj The object to get the full path to.
3658 * @param pszPath Where to return the path
3659 * @param cbPath The size of the output buffer.
3660 * @param chSlash The slash to use.
3661 */
3662KBOOL kFsCacheObjGetFullShortPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
3663{
3664 KSIZE off = pObj->cchShortParent;
3665 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3666 if (off > 0)
3667 {
3668 KSIZE offEnd = off + pObj->cchShortName;
3669 if (offEnd < cbPath)
3670 {
3671 PKFSDIR pAncestor;
3672
3673 pszPath[off + pObj->cchShortName] = '\0';
3674 memcpy(&pszPath[off], pObj->pszShortName, pObj->cchShortName);
3675
3676 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3677 {
3678 kHlpAssert(off > 1);
3679 kHlpAssert(pAncestor != NULL);
3680 kHlpAssert(pAncestor->Obj.cchShortName > 0);
3681 pszPath[--off] = chSlash;
3682 off -= pAncestor->Obj.cchShortName;
3683 kHlpAssert(pAncestor->Obj.cchShortParent == off);
3684 memcpy(&pszPath[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
3685 }
3686 return K_TRUE;
3687 }
3688 }
3689 else
3690 {
3691 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
3692 off = pObj->cchShortName;
3693 if (off + fDriveLetter < cbPath)
3694 {
3695 memcpy(pszPath, pObj->pszShortName, off);
3696 if (fDriveLetter)
3697 pszPath[off++] = chSlash;
3698 pszPath[off] = '\0';
3699 return K_TRUE;
3700 }
3701 }
3702
3703 return K_FALSE;
3704}
3705
3706
3707/**
3708 * Gets the full short path to @a pObj, UTF-16 version.
3709 *
3710 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3711 * @param pObj The object to get the full path to.
3712 * @param pszPath Where to return the path
3713 * @param cbPath The size of the output buffer.
3714 * @param wcSlash The slash to use.
3715 */
3716KBOOL kFsCacheObjGetFullShortPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
3717{
3718 KSIZE off = pObj->cwcShortParent;
3719 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3720 if (off > 0)
3721 {
3722 KSIZE offEnd = off + pObj->cwcShortName;
3723 if (offEnd < cwcPath)
3724 {
3725 PKFSDIR pAncestor;
3726
3727 pwszPath[off + pObj->cwcShortName] = '\0';
3728 memcpy(&pwszPath[off], pObj->pwszShortName, pObj->cwcShortName * sizeof(wchar_t));
3729
3730 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3731 {
3732 kHlpAssert(off > 1);
3733 kHlpAssert(pAncestor != NULL);
3734 kHlpAssert(pAncestor->Obj.cwcShortName > 0);
3735 pwszPath[--off] = wcSlash;
3736 off -= pAncestor->Obj.cwcShortName;
3737 kHlpAssert(pAncestor->Obj.cwcShortParent == off);
3738 memcpy(&pwszPath[off], pAncestor->Obj.pwszShortName, pAncestor->Obj.cwcShortName * sizeof(wchar_t));
3739 }
3740 return K_TRUE;
3741 }
3742 }
3743 else
3744 {
3745 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
3746 off = pObj->cwcShortName;
3747 if (off + fDriveLetter < cwcPath)
3748 {
3749 memcpy(pwszPath, pObj->pwszShortName, off * sizeof(wchar_t));
3750 if (fDriveLetter)
3751 pwszPath[off++] = wcSlash;
3752 pwszPath[off] = '\0';
3753 return K_TRUE;
3754 }
3755 }
3756
3757 return K_FALSE;
3758}
3759
3760#endif /* KFSCACHE_CFG_SHORT_NAMES */
3761
3762
3763
3764/**
3765 * Read the specified bits from the files into the given buffer, simple version.
3766 *
3767 * @returns K_TRUE on success (all requested bytes read),
3768 * K_FALSE on any kind of failure.
3769 *
3770 * @param pCache The cache.
3771 * @param pFileObj The file object.
3772 * @param offStart Where to start reading.
3773 * @param pvBuf Where to store what we read.
3774 * @param cbToRead How much to read (exact).
3775 */
3776KBOOL kFsCacheFileSimpleOpenReadClose(PKFSCACHE pCache, PKFSOBJ pFileObj, KU64 offStart, void *pvBuf, KSIZE cbToRead)
3777{
3778 /*
3779 * Open the file relative to the parent directory.
3780 */
3781 MY_NTSTATUS rcNt;
3782 HANDLE hFile;
3783 MY_IO_STATUS_BLOCK Ios;
3784 MY_OBJECT_ATTRIBUTES ObjAttr;
3785 MY_UNICODE_STRING UniStr;
3786
3787 kHlpAssertReturn(pFileObj->bObjType == KFSOBJ_TYPE_FILE, K_FALSE);
3788 kHlpAssert(pFileObj->pParent);
3789 kHlpAssertReturn(pFileObj->pParent->hDir != INVALID_HANDLE_VALUE, K_FALSE);
3790 kHlpAssertReturn(offStart == 0, K_FALSE); /** @todo when needed */
3791
3792 Ios.Information = -1;
3793 Ios.u.Status = -1;
3794
3795 UniStr.Buffer = (wchar_t *)pFileObj->pwszName;
3796 UniStr.Length = (USHORT)(pFileObj->cwcName * sizeof(wchar_t));
3797 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
3798
3799 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFileObj->pParent->hDir, NULL /*pSecAttr*/);
3800
3801 rcNt = g_pfnNtCreateFile(&hFile,
3802 GENERIC_READ | SYNCHRONIZE,
3803 &ObjAttr,
3804 &Ios,
3805 NULL, /*cbFileInitialAlloc */
3806 FILE_ATTRIBUTE_NORMAL,
3807 FILE_SHARE_READ,
3808 FILE_OPEN,
3809 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
3810 NULL, /*pEaBuffer*/
3811 0); /*cbEaBuffer*/
3812 if (MY_NT_SUCCESS(rcNt))
3813 {
3814 LARGE_INTEGER offFile;
3815 offFile.QuadPart = offStart;
3816
3817 Ios.Information = -1;
3818 Ios.u.Status = -1;
3819 rcNt = g_pfnNtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApcComplete*/, NULL /*pvApcCtx*/, &Ios,
3820 pvBuf, (KU32)cbToRead, !offStart ? &offFile : NULL, NULL /*puKey*/);
3821 if (MY_NT_SUCCESS(rcNt))
3822 rcNt = Ios.u.Status;
3823 if (MY_NT_SUCCESS(rcNt))
3824 {
3825 if (Ios.Information == cbToRead)
3826 {
3827 g_pfnNtClose(hFile);
3828 return K_TRUE;
3829 }
3830 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': Information=%p\n", pFileObj->pwszName, Ios.Information));
3831 }
3832 else
3833 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': %#x\n", pFileObj->pwszName, rcNt));
3834 g_pfnNtClose(hFile);
3835 }
3836 else
3837 KFSCACHE_LOG(("Error opening '%ls' for caching: %#x\n", pFileObj->pwszName, rcNt));
3838 return K_FALSE;
3839}
3840
3841
3842/**
3843 * Invalidate all cache entries of missing files.
3844 *
3845 * @param pCache The cache.
3846 */
3847void kFsCacheInvalidateMissing(PKFSCACHE pCache)
3848{
3849 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3850 pCache->auGenerationsMissing[0]++;
3851 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
3852 KFSCACHE_LOG(("Invalidate missing %#x\n", pCache->auGenerationsMissing[0]));
3853}
3854
3855
3856/**
3857 * Invalidate all cache entries (regular, custom & missing).
3858 *
3859 * @param pCache The cache.
3860 */
3861void kFsCacheInvalidateAll(PKFSCACHE pCache)
3862{
3863 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3864
3865 pCache->auGenerationsMissing[0]++;
3866 kHlpAssert(pCache->auGenerationsMissing[0] < KU32_MAX);
3867 pCache->auGenerationsMissing[1]++;
3868 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
3869
3870 pCache->auGenerations[0]++;
3871 kHlpAssert(pCache->auGenerations[0] < KU32_MAX);
3872 pCache->auGenerations[1]++;
3873 kHlpAssert(pCache->auGenerations[1] < KU32_MAX);
3874
3875 KFSCACHE_LOG(("Invalidate all - default: %#x/%#x, custom: %#x/%#x\n",
3876 pCache->auGenerationsMissing[0], pCache->auGenerations[0],
3877 pCache->auGenerationsMissing[1], pCache->auGenerations[1]));
3878}
3879
3880
3881/**
3882 * Invalidate all cache entries with custom generation handling set.
3883 *
3884 * @see kFsCacheSetupCustomRevisionForTree, KFSOBJ_F_USE_CUSTOM_GEN
3885 * @param pCache The cache.
3886 */
3887void kFsCacheInvalidateCustomMissing(PKFSCACHE pCache)
3888{
3889 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3890 pCache->auGenerationsMissing[1]++;
3891 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
3892 KFSCACHE_LOG(("Invalidate missing custom %#x\n", pCache->auGenerationsMissing[1]));
3893}
3894
3895
3896/**
3897 * Invalidate all cache entries with custom generation handling set, both
3898 * missing and regular present entries.
3899 *
3900 * @see kFsCacheSetupCustomRevisionForTree, KFSOBJ_F_USE_CUSTOM_GEN
3901 * @param pCache The cache.
3902 */
3903void kFsCacheInvalidateCustomBoth(PKFSCACHE pCache)
3904{
3905 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3906 pCache->auGenerations[1]++;
3907 kHlpAssert(pCache->auGenerations[1] < KU32_MAX);
3908 pCache->auGenerationsMissing[1]++;
3909 kHlpAssert(pCache->auGenerationsMissing[1] < KU32_MAX);
3910 KFSCACHE_LOG(("Invalidate both custom %#x/%#x\n", pCache->auGenerationsMissing[1], pCache->auGenerations[1]));
3911}
3912
3913
3914
3915/**
3916 * Applies the given flags to all the objects in a tree.
3917 *
3918 * @param pRoot Where to start applying the flag changes.
3919 * @param fAndMask The AND mask.
3920 * @param fOrMask The OR mask.
3921 */
3922static void kFsCacheApplyFlagsToTree(PKFSDIR pRoot, KU32 fAndMask, KU32 fOrMask)
3923{
3924 PKFSOBJ *ppCur = ((PKFSDIR)pRoot)->papChildren;
3925 KU32 cLeft = ((PKFSDIR)pRoot)->cChildren;
3926 while (cLeft-- > 0)
3927 {
3928 PKFSOBJ pCur = *ppCur++;
3929 if (pCur->bObjType != KFSOBJ_TYPE_DIR)
3930 pCur->fFlags = (fAndMask & pCur->fFlags) | fOrMask;
3931 else
3932 kFsCacheApplyFlagsToTree((PKFSDIR)pCur, fAndMask, fOrMask);
3933 }
3934
3935 pRoot->Obj.fFlags = (fAndMask & pRoot->Obj.fFlags) | fOrMask;
3936}
3937
3938
3939/**
3940 * Sets up using custom revisioning for the specified directory tree or file.
3941 *
3942 * There are some restrictions of the current implementation:
3943 * - If the root of the sub-tree is ever deleted from the cache (i.e.
3944 * deleted in real life and reflected in the cache), the setting is lost.
3945 * - It is not automatically applied to the lookup paths caches.
3946 *
3947 * @returns K_TRUE on success, K_FALSE on failure.
3948 * @param pCache The cache.
3949 * @param pRoot The root of the subtree. A non-directory is
3950 * fine, like a missing node.
3951 */
3952KBOOL kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot)
3953{
3954 if (pRoot)
3955 {
3956 if (pRoot->bObjType == KFSOBJ_TYPE_DIR)
3957 kFsCacheApplyFlagsToTree((PKFSDIR)pRoot, KU32_MAX, KFSOBJ_F_USE_CUSTOM_GEN);
3958 else
3959 pRoot->fFlags |= KFSOBJ_F_USE_CUSTOM_GEN;
3960 return K_TRUE;
3961 }
3962 return K_FALSE;
3963}
3964
3965
3966PKFSCACHE kFsCacheCreate(KU32 fFlags)
3967{
3968 PKFSCACHE pCache;
3969 birdResolveImports();
3970
3971 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
3972 if (pCache)
3973 {
3974 /* Dummy root dir entry. */
3975 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC;
3976 pCache->RootDir.Obj.cRefs = 1;
3977 pCache->RootDir.Obj.uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
3978 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR;
3979 pCache->RootDir.Obj.fHaveStats = K_FALSE;
3980 pCache->RootDir.Obj.pParent = NULL;
3981 pCache->RootDir.Obj.pszName = "";
3982 pCache->RootDir.Obj.cchName = 0;
3983 pCache->RootDir.Obj.cchParent = 0;
3984#ifdef KFSCACHE_CFG_UTF16
3985 pCache->RootDir.Obj.cwcName = 0;
3986 pCache->RootDir.Obj.cwcParent = 0;
3987 pCache->RootDir.Obj.pwszName = L"";
3988#endif
3989
3990#ifdef KFSCACHE_CFG_SHORT_NAMES
3991 pCache->RootDir.Obj.pszShortName = NULL;
3992 pCache->RootDir.Obj.cchShortName = 0;
3993 pCache->RootDir.Obj.cchShortParent = 0;
3994# ifdef KFSCACHE_CFG_UTF16
3995 pCache->RootDir.Obj.cwcShortName;
3996 pCache->RootDir.Obj.cwcShortParent;
3997 pCache->RootDir.Obj.pwszShortName;
3998# endif
3999#endif
4000 pCache->RootDir.cChildren = 0;
4001 pCache->RootDir.cChildrenAllocated = 0;
4002 pCache->RootDir.papChildren = NULL;
4003 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
4004 pCache->RootDir.cHashTab = 251;
4005 pCache->RootDir.paHashTab = (PKFSOBJHASH)kHlpAllocZ( pCache->RootDir.cHashTab
4006 * sizeof(pCache->RootDir.paHashTab[0]));
4007 if (pCache->RootDir.paHashTab)
4008 {
4009 /* The cache itself. */
4010 pCache->u32Magic = KFSCACHE_MAGIC;
4011 pCache->fFlags = fFlags;
4012 pCache->auGenerations[0] = KU32_MAX / 4;
4013 pCache->auGenerations[1] = KU32_MAX / 32;
4014 pCache->auGenerationsMissing[0] = KU32_MAX / 256;
4015 pCache->auGenerationsMissing[1] = 1;
4016 pCache->cObjects = 1;
4017 pCache->cbObjects = sizeof(pCache->RootDir) + pCache->RootDir.cHashTab * sizeof(pCache->RootDir.paHashTab[0]);
4018 pCache->cPathHashHits = 0;
4019 pCache->cWalkHits = 0;
4020 pCache->cAnsiPaths = 0;
4021 pCache->cAnsiPathCollisions = 0;
4022 pCache->cbAnsiPaths = 0;
4023#ifdef KFSCACHE_CFG_UTF16
4024 pCache->cUtf16Paths = 0;
4025 pCache->cUtf16PathCollisions = 0;
4026 pCache->cbUtf16Paths = 0;
4027#endif
4028 return pCache;
4029 }
4030
4031 kHlpFree(pCache);
4032 }
4033 return NULL;
4034}
4035
Note: See TracBrowser for help on using the repository browser.

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