VirtualBox

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

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

fixes

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