VirtualBox

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

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

updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 102.0 KB
Line 
1/* $Id: kFsCache.c 2859 2016-09-01 16:34:31Z bird $ */
2/** @file
3 * ntdircache.c - NT directory content cache.
4 */
5
6/*
7 * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#include <k/kHlp.h>
36
37#include "nthlp.h"
38#include "ntstat.h"
39
40#include <stdio.h>
41#include <mbstring.h>
42#include <wchar.h>
43//#include <intrin.h>
44//#include <setjmp.h>
45//#include <ctype.h>
46
47
48//#include <Windows.h>
49//#include <winternl.h>
50
51#include "kFsCache.h"
52
53
54
55
56
57/**
58 * Retains a reference to a cache object, internal version.
59 *
60 * @returns pObj
61 * @param pObj The object.
62 */
63K_INLINE PKFSOBJ kFsCacheObjRetainInternal(PKFSOBJ pObj)
64{
65 KU32 cRefs = ++pObj->cRefs;
66 kHlpAssert(cRefs < 16384);
67 K_NOREF(cRefs);
68 return pObj;
69}
70
71
72#ifndef NDEBUG
73
74/**
75 * Debug printing.
76 * @param pszFormat Debug format string.
77 * @param ... Format argument.
78 */
79void kFsCacheDbgPrintfV(const char *pszFormat, va_list va)
80{
81 if (1)
82 {
83 DWORD const dwSavedErr = GetLastError();
84
85 fprintf(stderr, "debug: ");
86 vfprintf(stderr, pszFormat, va);
87
88 SetLastError(dwSavedErr);
89 }
90}
91
92
93/**
94 * Debug printing.
95 * @param pszFormat Debug format string.
96 * @param ... Format argument.
97 */
98void kFsCacheDbgPrintf(const char *pszFormat, ...)
99{
100 if (1)
101 {
102 va_list va;
103 va_start(va, pszFormat);
104 kFsCacheDbgPrintfV(pszFormat, va);
105 va_end(va);
106 }
107}
108
109#endif /* !NDEBUG */
110
111
112
113/**
114 * Hashes a string.
115 *
116 * @returns 32-bit string hash.
117 * @param pszString String to hash.
118 */
119static KU32 kFsCacheStrHash(const char *pszString)
120{
121 /* This algorithm was created for sdbm (a public-domain reimplementation of
122 ndbm) database library. it was found to do well in scrambling bits,
123 causing better distribution of the keys and fewer splits. it also happens
124 to be a good general hashing function with good distribution. the actual
125 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
126 is the faster version used in gawk. [there is even a faster, duff-device
127 version] the magic constant 65599 was picked out of thin air while
128 experimenting with different constants, and turns out to be a prime.
129 this is one of the algorithms used in berkeley db (see sleepycat) and
130 elsewhere. */
131 KU32 uHash = 0;
132 KU32 uChar;
133 while ((uChar = (unsigned char)*pszString++) != 0)
134 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
135 return uHash;
136}
137
138
139/**
140 * Hashes a string.
141 *
142 * @returns The string length.
143 * @param pszString String to hash.
144 * @param puHash Where to return the 32-bit string hash.
145 */
146static KSIZE kFsCacheStrHashEx(const char *pszString, KU32 *puHash)
147{
148 const char * const pszStart = pszString;
149 KU32 uHash = 0;
150 KU32 uChar;
151 while ((uChar = (unsigned char)*pszString) != 0)
152 {
153 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
154 pszString++;
155 }
156 *puHash = uHash;
157 return pszString - pszStart;
158}
159
160
161/**
162 * Hashes a string.
163 *
164 * @returns The string length in wchar_t units.
165 * @param pwszString String to hash.
166 * @param puHash Where to return the 32-bit string hash.
167 */
168static KSIZE kFsCacheUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
169{
170 const wchar_t * const pwszStart = pwszString;
171 KU32 uHash = 0;
172 KU32 uChar;
173 while ((uChar = *pwszString) != 0)
174 {
175 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
176 pwszString++;
177 }
178 *puHash = uHash;
179 return pwszString - pwszStart;
180}
181
182#if 0
183
184/**
185 * Converts the given string to unicode.
186 *
187 * @returns Length of the resulting string in wchar_t's.
188 * @param pszSrc The source string.
189 * @param pwszDst The destination buffer.
190 * @param cwcDst The size of the destination buffer in wchar_t's.
191 */
192static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
193{
194 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
195 KSIZE offDst = 0;
196 while (offDst < cwcDst)
197 {
198 char ch = *pszSrc++;
199 pwszDst[offDst++] = ch;
200 if (!ch)
201 return offDst - 1;
202 kHlpAssert((unsigned)ch < 127);
203 }
204
205 pwszDst[offDst - 1] = '\0';
206 return offDst;
207}
208
209
210/**
211 * Converts the given UTF-16 to a normal string.
212 *
213 * @returns Length of the resulting string.
214 * @param pwszSrc The source UTF-16 string.
215 * @param pszDst The destination buffer.
216 * @param cbDst The size of the destination buffer in bytes.
217 */
218static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
219{
220 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
221 KSIZE offDst = 0;
222 while (offDst < cbDst)
223 {
224 wchar_t wc = *pwszSrc++;
225 pszDst[offDst++] = (char)wc;
226 if (!wc)
227 return offDst - 1;
228 kHlpAssert((unsigned)wc < 127);
229 }
230
231 pszDst[offDst - 1] = '\0';
232 return offDst;
233}
234
235
236
237/** UTF-16 string length. */
238static KSIZE kwUtf16Len(wchar_t const *pwsz)
239{
240 KSIZE cwc = 0;
241 while (*pwsz != '\0')
242 cwc++, pwsz++;
243 return cwc;
244}
245
246/**
247 * Copy out the UTF-16 string following the convension of GetModuleFileName
248 */
249static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
250{
251 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
252 if (cwcSrc + 1 <= cwcDst)
253 {
254 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
255 return (DWORD)cwcSrc;
256 }
257 if (cwcDst > 0)
258 {
259 KSIZE cwcDstTmp = cwcDst - 1;
260 pwszDst[cwcDstTmp] = '\0';
261 if (cwcDstTmp > 0)
262 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
263 }
264 SetLastError(ERROR_INSUFFICIENT_BUFFER);
265 return (DWORD)cwcDst;
266}
267
268
269/**
270 * Copy out the ANSI string following the convension of GetModuleFileName
271 */
272static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
273{
274 KSIZE cchSrc = kHlpStrLen(pszSrc);
275 if (cchSrc + 1 <= cbDst)
276 {
277 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
278 return (DWORD)cchSrc;
279 }
280 if (cbDst > 0)
281 {
282 KSIZE cbDstTmp = cbDst - 1;
283 pszDst[cbDstTmp] = '\0';
284 if (cbDstTmp > 0)
285 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
286 }
287 SetLastError(ERROR_INSUFFICIENT_BUFFER);
288 return (DWORD)cbDst;
289}
290
291
292/**
293 * Normalizes the path so we get a consistent hash.
294 *
295 * @returns status code.
296 * @param pszPath The path.
297 * @param pszNormPath The output buffer.
298 * @param cbNormPath The size of the output buffer.
299 */
300static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
301{
302 char *pchSlash;
303 KSIZE cchNormPath;
304
305 /*
306 * We hash these to speed stuff up (nt_fullpath isn't cheap and we're
307 * gonna have many repeat queries and assume nobody do case changes to
308 * anything essential while kmk is running).
309 */
310 KU32 uHashPath;
311 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
312 KU32 const idxHashTab = uHashPath % K_ELEMENTS(g_apFsNormalizedPathsA);
313 PKFSNORMHASHA pHashEntry = g_apFsNormalizedPathsA[idxHashTab];
314 if (pHashEntry)
315 {
316 do
317 {
318 if ( pHashEntry->uHashPath == uHashPath
319 && pHashEntry->cchPath == cchPath
320 && kHlpMemComp(pHashEntry->pszPath, pszPath, cchPath) == 0)
321 {
322 if (cbNormPath > pHashEntry->cchNormPath)
323 {
324 KFSCACHE_LOG(("kwPathNormalize(%s) - hit\n", pszPath));
325 kHlpMemCopy(pszNormPath, pHashEntry->szNormPath, pHashEntry->cchNormPath + 1);
326 return 0;
327 }
328 return KERR_BUFFER_OVERFLOW;
329 }
330 pHashEntry = pHashEntry->pNext;
331 } while (pHashEntry);
332 }
333
334 /*
335 * Do it the slow way.
336 */
337 nt_fullpath(pszPath, pszNormPath, cbNormPath);
338 /** @todo nt_fullpath overflow handling?!?!? */
339
340 pchSlash = kHlpStrChr(pszNormPath, '/');
341 while (pchSlash)
342 {
343 *pchSlash = '\\';
344 pchSlash = kHlpStrChr(pchSlash + 1, '/');
345 }
346
347 /*
348 * Create a new hash table entry (ignore failures).
349 */
350 cchNormPath = kHlpStrLen(pszNormPath);
351 if (cchNormPath < KU16_MAX && cchPath < KU16_MAX)
352 {
353 pHashEntry = (PKFSNORMHASHA)kHlpAlloc(sizeof(*pHashEntry) + cchNormPath + 1 + cchPath + 1);
354 if (pHashEntry)
355 {
356 pHashEntry->cchNormPath = (KU16)cchNormPath;
357 pHashEntry->cchPath = (KU16)cchPath;
358 pHashEntry->uHashPath = uHashPath;
359 pHashEntry->pszPath = (char *)kHlpMemCopy(&pHashEntry->szNormPath[cchNormPath + 1], pszPath, cchPath + 1);
360 kHlpMemCopy(pHashEntry->szNormPath, pszNormPath, cchNormPath + 1);
361
362 pHashEntry->pNext = g_apFsNormalizedPathsA[idxHashTab];
363 g_apFsNormalizedPathsA[idxHashTab] = pHashEntry;
364 }
365 }
366
367 return 0;
368}
369
370
371/**
372 * Get the pointer to the filename part of the path.
373 *
374 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
375 * @returns Pointer to the terminator char if no filename.
376 * @param pszPath The path to parse.
377 */
378static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
379{
380 const wchar_t *pwszLast = NULL;
381 for (;;)
382 {
383 wchar_t wc = *pwszPath;
384#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
385 if (wc == '/' || wc == '\\' || wc == ':')
386 {
387 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
388 /* nothing */;
389 pwszLast = pwszPath;
390 }
391#else
392 if (wc == '/')
393 {
394 while ((wc = *++pszFilename) == '/')
395 /* betsuni */;
396 pwszLast = pwszPath;
397 }
398#endif
399 if (!wc)
400 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
401 pwszPath++;
402 }
403}
404
405
406/**
407 * Check if the path leads to a regular file (that exists).
408 *
409 * @returns K_TRUE / K_FALSE
410 * @param pszPath Path to the file to check out.
411 */
412static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
413{
414 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
415 KSIZE cchPath = kHlpStrLen(pszPath);
416 if ( cchPath > 3
417 && pszPath[cchPath - 4] == '.'
418 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
419 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
420 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
421 {
422 PKFSOBJ pFsObj = kFsCacheLookupA(pszPath);
423 if (pFsObj)
424 {
425 if (!(pFsObj->fAttribs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE))) /* also checks invalid */
426 return K_TRUE;
427 }
428 }
429 else
430 {
431 BirdStat_T Stat;
432 int rc = birdStatFollowLink(pszPath, &Stat);
433 if (rc == 0)
434 {
435 if (S_ISREG(Stat.st_mode))
436 return K_TRUE;
437 }
438 }
439 return K_FALSE;
440}
441
442
443
444
445
446
447/**
448 * Helper for getting the extension of a UTF-16 path.
449 *
450 * @returns Pointer to the extension or the terminator.
451 * @param pwszPath The path.
452 * @param pcwcExt Where to return the length of the extension.
453 */
454static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
455{
456 wchar_t const *pwszName = pwszPath;
457 wchar_t const *pwszExt = NULL;
458 for (;;)
459 {
460 wchar_t const wc = *pwszPath++;
461 if (wc == '.')
462 pwszExt = pwszPath;
463 else if (wc == '/' || wc == '\\' || wc == ':')
464 {
465 pwszName = pwszPath;
466 pwszExt = NULL;
467 }
468 else if (wc == '\0')
469 {
470 if (pwszExt)
471 {
472 *pcwcExt = pwszPath - pwszExt - 1;
473 return pwszExt;
474 }
475 *pcwcExt = 0;
476 return pwszPath - 1;
477 }
478 }
479}
480#endif
481
482
483/**
484 * Looks for '..' in the path.
485 *
486 * @returns K_TRUE if '..' component found, K_FALSE if not.
487 * @param pszPath The path.
488 * @param cchPath The length of the path.
489 */
490static KBOOL kFsCacheHasDotDotA(const char *pszPath, KSIZE cchPath)
491{
492 const char *pchDot = (const char *)kHlpMemChr(pszPath, '.', cchPath);
493 while (pchDot)
494 {
495 if (pchDot[1] != '.')
496 {
497 pchDot++;
498 pchDot = (const char *)kHlpMemChr(pchDot, '.', &pszPath[cchPath] - pchDot);
499 }
500 else
501 {
502 char ch;
503 if ( (ch = pchDot[2]) != '\0'
504 && IS_SLASH(ch))
505 {
506 if (pchDot == pszPath)
507 return K_TRUE;
508 ch = pchDot[-1];
509 if ( IS_SLASH(ch)
510 || ch == ':')
511 return K_TRUE;
512 }
513 pchDot = (const char *)kHlpMemChr(pchDot + 2, '.', &pszPath[cchPath] - pchDot - 2);
514 }
515 }
516
517 return K_FALSE;
518}
519
520
521/**
522 * Looks for '..' in the path.
523 *
524 * @returns K_TRUE if '..' component found, K_FALSE if not.
525 * @param pwszPath The path.
526 * @param cwcPath The length of the path (in wchar_t's).
527 */
528static KBOOL kFsCacheHasDotDotW(const wchar_t *pwszPath, KSIZE cwcPath)
529{
530 const wchar_t *pwcDot = wmemchr(pwszPath, '.', cwcPath);
531 while (pwcDot)
532 {
533 if (pwcDot[1] != '.')
534 {
535 pwcDot++;
536 pwcDot = wmemchr(pwcDot, '.', &pwszPath[cwcPath] - pwcDot);
537 }
538 else
539 {
540 wchar_t wch;
541 if ( (wch = pwcDot[2]) != '\0'
542 && IS_SLASH(wch))
543 {
544 if (pwcDot == pwszPath)
545 return K_TRUE;
546 wch = pwcDot[-1];
547 if ( IS_SLASH(wch)
548 || wch == ':')
549 return K_TRUE;
550 }
551 pwcDot = wmemchr(pwcDot + 2, '.', &pwszPath[cwcPath] - pwcDot - 2);
552 }
553 }
554
555 return K_FALSE;
556}
557
558
559/**
560 * Creates an ANSI hash table entry for the given path.
561 *
562 * @returns The hash table entry or NULL if out of memory.
563 * @param pCache The hash
564 * @param pFsObj The resulting object.
565 * @param pszPath The path.
566 * @param cchPath The length of the path.
567 * @param uHashPath The hash of the path.
568 * @param fAbsolute Whether it can be refreshed using an absolute
569 * lookup or requires the slow treatment.
570 * @param idxHashTab The hash table index of the path.
571 * @param enmError The lookup error.
572 */
573static PKFSHASHA kFsCacheCreatePathHashTabEntryA(PKFSCACHE pCache, PKFSOBJ pFsObj, const char *pszPath, KU32 cchPath,
574 KU32 uHashPath, KU32 idxHashTab, BOOL fAbsolute, KFSLOOKUPERROR enmError)
575{
576 PKFSHASHA pHashEntry = (PKFSHASHA)kHlpAlloc(sizeof(*pHashEntry) + cchPath + 1);
577 if (pHashEntry)
578 {
579 pHashEntry->uHashPath = uHashPath;
580 pHashEntry->cchPath = (KU16)cchPath;
581 pHashEntry->fAbsolute = fAbsolute;
582 pHashEntry->pFsObj = pFsObj;
583 pHashEntry->enmError = enmError;
584 pHashEntry->pszPath = (const char *)kHlpMemCopy(pHashEntry + 1, pszPath, cchPath + 1);
585 if (pFsObj)
586 pHashEntry->uCacheGen = pCache->uGeneration;
587 else if (enmError != KFSLOOKUPERROR_UNSUPPORTED)
588 pHashEntry->uCacheGen = pCache->uGenerationMissing;
589 else
590 pHashEntry->uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
591
592 pHashEntry->pNext = pCache->apAnsiPaths[idxHashTab];
593 pCache->apAnsiPaths[idxHashTab] = pHashEntry;
594
595 pCache->cbAnsiPaths += sizeof(*pHashEntry) + cchPath + 1;
596 pCache->cAnsiPaths++;
597 if (pHashEntry->pNext)
598 pCache->cAnsiPathCollisions++;
599 }
600 return pHashEntry;
601}
602
603
604/**
605 * Creates an UTF-16 hash table entry for the given path.
606 *
607 * @returns The hash table entry or NULL if out of memory.
608 * @param pCache The hash
609 * @param pFsObj The resulting object.
610 * @param pwszPath The path.
611 * @param cwcPath The length of the path (in wchar_t's).
612 * @param uHashPath The hash of the path.
613 * @param fAbsolute Whether it can be refreshed using an absolute
614 * lookup or requires the slow treatment.
615 * @param idxHashTab The hash table index of the path.
616 * @param enmError The lookup error.
617 */
618static PKFSHASHW kFsCacheCreatePathHashTabEntryW(PKFSCACHE pCache, PKFSOBJ pFsObj, const wchar_t *pwszPath, KU32 cwcPath,
619 KU32 uHashPath, KU32 idxHashTab, BOOL fAbsolute, KFSLOOKUPERROR enmError)
620{
621 PKFSHASHW pHashEntry = (PKFSHASHW)kHlpAlloc(sizeof(*pHashEntry) + (cwcPath + 1) * sizeof(wchar_t));
622 if (pHashEntry)
623 {
624 pHashEntry->uHashPath = uHashPath;
625 pHashEntry->cwcPath = cwcPath;
626 pHashEntry->fAbsolute = fAbsolute;
627 pHashEntry->pFsObj = pFsObj;
628 pHashEntry->enmError = enmError;
629 pHashEntry->pwszPath = (const wchar_t *)kHlpMemCopy(pHashEntry + 1, pwszPath, (cwcPath + 1) * sizeof(wchar_t));
630 if (pFsObj)
631 pHashEntry->uCacheGen = pCache->uGeneration;
632 else if (enmError != KFSLOOKUPERROR_UNSUPPORTED)
633 pHashEntry->uCacheGen = pCache->uGenerationMissing;
634 else
635 pHashEntry->uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
636
637 pHashEntry->pNext = pCache->apUtf16Paths[idxHashTab];
638 pCache->apUtf16Paths[idxHashTab] = pHashEntry;
639
640 pCache->cbUtf16Paths += sizeof(*pHashEntry) + (cwcPath + 1) * sizeof(wchar_t);
641 pCache->cUtf16Paths++;
642 if (pHashEntry->pNext)
643 pCache->cAnsiPathCollisions++;
644 }
645 return pHashEntry;
646}
647
648
649/**
650 * Links the child in under the parent.
651 *
652 * @returns K_TRUE on success, K_FALSE if out of memory.
653 * @param pParent The parent node.
654 * @param pChild The child node.
655 */
656static KBOOL kFsCacheDirAddChild(PKFSCACHE pCache, PKFSDIR pParent, PKFSOBJ pChild, KFSLOOKUPERROR *penmError)
657{
658 if ((pParent->cChildren % 16) == 0)
659 {
660 void *pvNew = kHlpRealloc(pParent->papChildren, (pParent->cChildren + 16) * sizeof(pParent->papChildren[0]));
661 if (!pvNew)
662 return K_FALSE;
663 pParent->papChildren = (PKFSOBJ *)pvNew;
664 pCache->cbObjects += 16 * sizeof(pParent->papChildren[0]);
665 }
666 pParent->papChildren[pParent->cChildren++] = kFsCacheObjRetainInternal(pChild);
667 return K_TRUE;
668}
669
670
671/**
672 * Creates a new cache object.
673 *
674 * @returns Pointer (with 1 reference) to the new object. The object will not
675 * be linked to the parent directory yet.
676 *
677 * NULL if we're out of memory.
678 *
679 * @param pCache The cache.
680 * @param pParent The parent directory.
681 * @param pszName The ANSI name.
682 * @param cchName The length of the ANSI name.
683 * @param pwszName The UTF-16 name.
684 * @param cwcName The length of the UTF-16 name.
685 * @param pszShortName The ANSI short name, NULL if none.
686 * @param cchShortName The length of the ANSI short name, 0 if none.
687 * @param pwszShortName The UTF-16 short name, NULL if none.
688 * @param cwcShortName The length of the UTF-16 short name, 0 if none.
689 * @param bObjType The objct type.
690 * @param penmError Where to explain failures.
691 */
692PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,
693 char const *pszName, KU16 cchName, wchar_t const *pwszName, KU16 cwcName,
694#ifdef KFSCACHE_CFG_SHORT_NAMES
695 char const *pszShortName, KU16 cchShortName, wchar_t const *pwszShortName, KU16 cwcShortName,
696#endif
697 KU8 bObjType, KFSLOOKUPERROR *penmError)
698{
699 /*
700 * Allocate the object.
701 */
702 KBOOL const fDirish = bObjType != KFSOBJ_TYPE_FILE && bObjType != KFSOBJ_TYPE_OTHER;
703 KSIZE const cbObj = fDirish ? sizeof(KFSDIR) : sizeof(KFSOBJ);
704 KSIZE const cbNames = (cwcName + 1) * sizeof(wchar_t) + cchName + 1
705#ifdef KFSCACHE_CFG_SHORT_NAMES
706 + (cwcShortName > 0 ? (cwcShortName + 1) * sizeof(wchar_t) + cchShortName + 1 : 0)
707#endif
708 ;
709 PKFSOBJ pObj;
710 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
711
712 pObj = (PKFSOBJ)kHlpAlloc(cbObj + cbNames);
713 if (pObj)
714 {
715 KU8 *pbExtra = (KU8 *)pObj + cbObj;
716
717 pCache->cbObjects += cbObj + cbNames;
718 pCache->cObjects++;
719
720 /*
721 * Initialize the object.
722 */
723 pObj->u32Magic = KFSOBJ_MAGIC;
724 pObj->cRefs = 1;
725 pObj->uCacheGen = bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing;
726 pObj->bObjType = bObjType;
727 pObj->fHaveStats = K_FALSE;
728 pObj->abUnused[0] = K_FALSE;
729 pObj->abUnused[1] = K_FALSE;
730 pObj->fFlags = pParent->Obj.fFlags;
731 pObj->pParent = pParent;
732 pObj->pUserDataHead = NULL;
733
734#ifdef KFSCACHE_CFG_UTF16
735 pObj->cwcParent = pParent->Obj.cwcParent + pParent->Obj.cwcName + !!pParent->Obj.cwcName;
736 pObj->pwszName = (wchar_t *)kHlpMemCopy(pbExtra, pwszName, cwcName * sizeof(wchar_t));
737 pObj->cwcName = cwcName;
738 pbExtra += cwcName * sizeof(wchar_t);
739 *pbExtra++ = '\0';
740 *pbExtra++ = '\0';
741# ifdef KFSCACHE_CFG_SHORT_NAMES
742 pObj->cwcShortParent = pParent->Obj.cwcShortParent + pParent->Obj.cwcShortName + !!pParent->Obj.cwcShortName;
743 if (cwcShortName)
744 {
745 pObj->pwszShortName = (wchar_t *)kHlpMemCopy(pbExtra, pwszShortName, cwcShortName * sizeof(wchar_t));
746 pObj->cwcShortName = cwcShortName;
747 pbExtra += cwcShortName * sizeof(wchar_t);
748 *pbExtra++ = '\0';
749 *pbExtra++ = '\0';
750 }
751 else
752 {
753 pObj->pwszShortName = pObj->pwszName;
754 pObj->cwcShortName = cwcName;
755 }
756# endif
757#endif
758 pObj->cchParent = pParent->Obj.cchParent + pParent->Obj.cchName + !!pParent->Obj.cchName;
759 pObj->pszName = (char *)kHlpMemCopy(pbExtra, pszName, cchName);
760 pObj->cchName = cchName;
761 pbExtra += cchName;
762 *pbExtra++ = '\0';
763# ifdef KFSCACHE_CFG_SHORT_NAMES
764 pObj->cchShortParent = pParent->Obj.cchShortParent + pParent->Obj.cchShortName + !!pParent->Obj.cchShortName;
765 if (cchShortName)
766 {
767 pObj->pszShortName = (char *)kHlpMemCopy(pbExtra, pszShortName, cchShortName);
768 pObj->cchShortName = cchShortName;
769 pbExtra += cchShortName;
770 *pbExtra++ = '\0';
771 }
772 else
773 {
774 pObj->pszShortName = pObj->pszName;
775 pObj->cchShortName = cchName;
776 }
777#endif
778 kHlpAssert(pbExtra - (KU8 *)pObj == cbObj);
779
780 /*
781 * Type specific initilization.
782 */
783 if (fDirish)
784 {
785 PKFSDIR pDirObj = (PKFSDIR)pObj;
786 pDirObj->cChildren = 0;
787 pDirObj->papChildren = NULL;
788 pDirObj->cHashTab = 0;
789 pDirObj->paHashTab = NULL;
790 pDirObj->hDir = INVALID_HANDLE_VALUE;
791 pDirObj->uDevNo = pParent->uDevNo;
792 pDirObj->iLastWrite = 0;
793 pDirObj->fPopulated = K_FALSE;
794 }
795 }
796 else
797 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
798 return pObj;
799}
800
801
802/**
803 * Creates a new object given wide char names.
804 *
805 * This function just converts the paths and calls kFsCacheCreateObject.
806 *
807 *
808 * @returns Pointer (with 1 reference) to the new object. The object will not
809 * be linked to the parent directory yet.
810 *
811 * NULL if we're out of memory.
812 *
813 * @param pCache The cache.
814 * @param pParent The parent directory.
815 * @param pszName The ANSI name.
816 * @param cchName The length of the ANSI name.
817 * @param pwszName The UTF-16 name.
818 * @param cwcName The length of the UTF-16 name.
819 * @param pwszShortName The UTF-16 short name, NULL if none.
820 * @param cwcShortName The length of the UTF-16 short name, 0 if none.
821 * @param bObjType The objct type.
822 * @param penmError Where to explain failures.
823 */
824PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName,
825#ifdef KFSCACHE_CFG_SHORT_NAMES
826 wchar_t const *pwszShortName, KU32 cwcShortName,
827#endif
828 KU8 bObjType, KFSLOOKUPERROR *penmError)
829{
830 /* Convert names to ANSI first so we know their lengths. */
831 char szName[KFSCACHE_CFG_MAX_ANSI_NAME];
832 int cchName = WideCharToMultiByte(CP_ACP, 0, pwszName, cwcName, szName, sizeof(szName) - 1, NULL, NULL);
833 if (cchName >= 0)
834 {
835#ifdef KFSCACHE_CFG_SHORT_NAMES
836 char szShortName[12*3 + 1];
837 int cchShortName = 0;
838 if ( cwcShortName == 0
839 || (cchShortName = WideCharToMultiByte(CP_ACP, 0, pwszShortName, cwcShortName,
840 szShortName, sizeof(szShortName) - 1, NULL, NULL)) > 0)
841#endif
842 {
843 return kFsCacheCreateObject(pCache, pParent,
844 szName, cchName, pwszName, cwcName,
845#ifdef KFSCACHE_CFG_SHORT_NAMES
846 szShortName, cchShortName, pwszShortName, cwcShortName,
847#endif
848 bObjType, penmError);
849 }
850 }
851 *penmError = KFSLOOKUPERROR_ANSI_CONVERSION_ERROR;
852 return NULL;
853}
854
855
856/**
857 * Creates a missing object.
858 *
859 * This is used for caching negative results.
860 *
861 * @returns Pointer to the newly created object on success (already linked into
862 * pParent). No reference.
863 *
864 * NULL on failure.
865 *
866 * @param pCache The cache.
867 * @param pParent The parent directory.
868 * @param pchName The name.
869 * @param cchName The length of the name.
870 * @param penmError Where to return failure explanations.
871 */
872static PKFSOBJ kFsCacheCreateMissingA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName,
873 KFSLOOKUPERROR *penmError)
874{
875 /*
876 * Just convert the name to UTF-16 and call kFsCacheCreateObject to do the job.
877 */
878 wchar_t wszName[KFSCACHE_CFG_MAX_PATH];
879 int cwcName = MultiByteToWideChar(CP_ACP, 0, pchName, cchName, wszName, KFSCACHE_CFG_MAX_UTF16_NAME - 1);
880 if (cwcName > 0)
881 {
882 /** @todo check that it actually doesn't exists before we add it. We should not
883 * trust the directory enumeration here, or maybe we should?? */
884
885 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, pParent, pchName, cchName, wszName, cwcName,
886#ifdef KFSCACHE_CFG_SHORT_NAMES
887 NULL, 0, NULL, 0,
888#endif
889 KFSOBJ_TYPE_MISSING, penmError);
890 if (pMissing)
891 {
892 KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError);
893 kFsCacheObjRelease(pCache, pMissing);
894 return fRc ? pMissing : NULL;
895 }
896 return NULL;
897 }
898 *penmError = KFSLOOKUPERROR_UTF16_CONVERSION_ERROR;
899 return NULL;
900}
901
902
903/**
904 * Creates a missing object, UTF-16 version.
905 *
906 * This is used for caching negative results.
907 *
908 * @returns Pointer to the newly created object on success (already linked into
909 * pParent). No reference.
910 *
911 * NULL on failure.
912 *
913 * @param pCache The cache.
914 * @param pParent The parent directory.
915 * @param pwcName The name.
916 * @param cwcName The length of the name.
917 * @param penmError Where to return failure explanations.
918 */
919static PKFSOBJ kFsCacheCreateMissingW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName,
920 KFSLOOKUPERROR *penmError)
921{
922 /** @todo check that it actually doesn't exists before we add it. We should not
923 * trust the directory enumeration here, or maybe we should?? */
924 PKFSOBJ pMissing = kFsCacheCreateObjectW(pCache, pParent, pwcName, cwcName,
925#ifdef KFSCACHE_CFG_SHORT_NAMES
926 NULL, 0,
927#endif
928 KFSOBJ_TYPE_MISSING, penmError);
929 if (pMissing)
930 {
931 KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError);
932 kFsCacheObjRelease(pCache, pMissing);
933 return fRc ? pMissing : NULL;
934 }
935 return NULL;
936}
937
938
939/**
940 * Does the initial directory populating or refreshes it if it has been
941 * invalidated.
942 *
943 * This assumes the parent directory is opened.
944 *
945 * @returns K_TRUE on success, K_FALSE on error.
946 * @param pCache The cache.
947 * @param pDir The directory.
948 * @param penmError Where to store K_FALSE explanation.
949 */
950static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
951{
952 KBOOL fRefreshing = K_FALSE;
953 /** @todo will have to make this more flexible wrt information classes since
954 * older windows versions (XP, w2K) might not correctly support the
955 * ones with file ID on all file systems. */
956#ifdef KFSCACHE_CFG_SHORT_NAMES
957 MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdBothDirectoryInformation;
958 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
959#else
960 MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdFullDirectoryInformation;
961 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
962#endif
963 MY_NTSTATUS rcNt;
964 MY_IO_STATUS_BLOCK Ios;
965 union
966 {
967 /* Include the structures for better alignment. */
968 MY_FILE_ID_BOTH_DIR_INFORMATION WithId;
969 MY_FILE_ID_FULL_DIR_INFORMATION NoId;
970 /* Buffer padding. We're using a 56KB buffer here to avoid size troubles with CIFS and such. */
971 KU8 abBuf[56*1024];
972 } uBuf;
973
974 /*
975 * Open the directory.
976 */
977 if (pDir->hDir == INVALID_HANDLE_VALUE)
978 {
979 MY_OBJECT_ATTRIBUTES ObjAttr;
980 MY_UNICODE_STRING UniStr;
981
982 kHlpAssert(!pDir->fPopulated);
983
984 Ios.Information = -1;
985 Ios.u.Status = -1;
986
987 UniStr.Buffer = (wchar_t *)pDir->Obj.pwszName;
988 UniStr.Length = (USHORT)(pDir->Obj.cwcName * sizeof(wchar_t));
989 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
990
991 kHlpAssertStmtReturn(pDir->Obj.pParent, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
992 kHlpAssertStmtReturn(pDir->Obj.pParent->hDir != INVALID_HANDLE_VALUE, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
993 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pDir->Obj.pParent->hDir, NULL /*pSecAttr*/);
994
995 /** @todo FILE_OPEN_REPARSE_POINT? */
996 rcNt = g_pfnNtCreateFile(&pDir->hDir,
997 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
998 &ObjAttr,
999 &Ios,
1000 NULL, /*cbFileInitialAlloc */
1001 FILE_ATTRIBUTE_NORMAL,
1002 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1003 FILE_OPEN,
1004 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1005 NULL, /*pEaBuffer*/
1006 0); /*cbEaBuffer*/
1007 if (MY_NT_SUCCESS(rcNt))
1008 { /* likely */ }
1009 else
1010 {
1011 pDir->hDir = INVALID_HANDLE_VALUE;
1012 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR;
1013 return K_FALSE;
1014 }
1015 }
1016 else if (pDir->fPopulated)
1017 {
1018 /** @todo refreshing directories. */
1019 __debugbreak();
1020 fRefreshing = K_TRUE;
1021 }
1022
1023
1024 /*
1025 * Enumerate the directory content.
1026 */
1027 Ios.Information = -1;
1028 Ios.u.Status = -1;
1029 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1030 NULL, /* hEvent */
1031 NULL, /* pfnApcComplete */
1032 NULL, /* pvApcCompleteCtx */
1033 &Ios,
1034 &uBuf,
1035 sizeof(uBuf),
1036 enmInfoClass,
1037 FALSE, /* fReturnSingleEntry */
1038 NULL, /* Filter / restart pos. */
1039 TRUE); /* fRestartScan */
1040 while (MY_NT_SUCCESS(rcNt))
1041 {
1042 /*
1043 * Process the entries in the buffer.
1044 */
1045 KSIZE offBuf = 0;
1046 for (;;)
1047 {
1048 union
1049 {
1050 KU8 *pb;
1051#ifdef KFSCACHE_CFG_SHORT_NAMES
1052 MY_FILE_ID_BOTH_DIR_INFORMATION *pWithId;
1053 MY_FILE_BOTH_DIR_INFORMATION *pNoId;
1054#else
1055 MY_FILE_ID_FULL_DIR_INFORMATION *pWithId;
1056 MY_FILE_FULL_DIR_INFORMATION *pNoId;
1057#endif
1058 } uPtr;
1059 PKFSOBJ pCur;
1060 KU32 offNext;
1061 KU32 cbMinCur;
1062 wchar_t *pwszFilename;
1063
1064 /* ASSUME only the FileName member differs between the two structures. */
1065 uPtr.pb = &uBuf.abBuf[offBuf];
1066 if (enmInfoClass == enmInfoClassWithId)
1067 {
1068 pwszFilename = &uPtr.pWithId->FileName[0];
1069 cbMinCur = (KU32)((uintptr_t)&uPtr.pWithId->FileName[0] - (uintptr_t)uPtr.pWithId);
1070 cbMinCur += uPtr.pNoId->FileNameLength;
1071 }
1072 else
1073 {
1074 pwszFilename = &uPtr.pNoId->FileName[0];
1075 cbMinCur = (KU32)((uintptr_t)&uPtr.pNoId->FileName[0] - (uintptr_t)uPtr.pNoId);
1076 cbMinCur += uPtr.pNoId->FileNameLength;
1077 }
1078
1079 /* We need to skip the '.' and '..' entries. */
1080 if ( *pwszFilename != '.'
1081 || uPtr.pNoId->FileNameLength > 4
1082 || !( uPtr.pNoId->FileNameLength == 2
1083 || ( uPtr.pNoId->FileNameLength == 4
1084 && pwszFilename[1] == '.') )
1085 )
1086 {
1087 /*
1088 * Create the entry (not linked yet).
1089 */
1090 pCur = kFsCacheCreateObjectW(pCache, pDir, pwszFilename, uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1091#ifdef KFSCACHE_CFG_SHORT_NAMES
1092 uPtr.pNoId->ShortName, uPtr.pNoId->ShortNameLength / sizeof(wchar_t),
1093#endif
1094 uPtr.pNoId->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR
1095 : uPtr.pNoId->FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)
1096 ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE,
1097 penmError);
1098 if (!pCur)
1099 return K_FALSE;
1100 kHlpAssert(pCur->cRefs == 1);
1101
1102#ifdef KFSCACHE_CFG_SHORT_NAMES
1103 if (enmInfoClass == enmInfoClassWithId)
1104 birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
1105 else
1106 birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
1107#else
1108 if (enmInfoClass == enmInfoClassWithId)
1109 birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
1110 else
1111 birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
1112#endif
1113 pCur->Stats.st_dev = pDir->uDevNo;
1114 pCur->fHaveStats = K_TRUE;
1115
1116 /*
1117 * If we're updating we have to check the data.
1118 */
1119 if (fRefreshing)
1120 {
1121 __debugbreak();
1122 }
1123
1124 /*
1125 * If we've still got pCur, add it to the directory.
1126 */
1127 if (pCur)
1128 {
1129 KBOOL fRc = kFsCacheDirAddChild(pCache, pDir, pCur, penmError);
1130 kFsCacheObjRelease(pCache, pCur);
1131 if (fRc)
1132 { /* likely */ }
1133 else
1134 return K_FALSE;
1135 }
1136 }
1137 /*
1138 * When seeing '.' we update the directory info.
1139 */
1140 else if (uPtr.pNoId->FileNameLength == 2)
1141 {
1142 pDir->iLastWrite = uPtr.pNoId->LastWriteTime.QuadPart;
1143#ifdef KFSCACHE_CFG_SHORT_NAMES
1144 if (enmInfoClass == enmInfoClassWithId)
1145 birdStatFillFromFileIdBothDirInfo(&pDir->Obj.Stats, uPtr.pWithId, pDir->Obj.pszName);
1146 else
1147 birdStatFillFromFileBothDirInfo(&pDir->Obj.Stats, uPtr.pNoId, pDir->Obj.pszName);
1148#else
1149 if (enmInfoClass == enmInfoClassWithId)
1150 birdStatFillFromFileIdFullDirInfo(&pDir->Obj.Stats, uPtr.pWithId, pDir->Obj.pszName);
1151 else
1152 birdStatFillFromFileFullDirInfo(&pDir->Obj.Stats, uPtr.pNoId, pDir->Obj.pszName);
1153#endif
1154 }
1155
1156 /*
1157 * Advance.
1158 */
1159 offNext = uPtr.pNoId->NextEntryOffset;
1160 if ( offNext >= cbMinCur
1161 && offNext < sizeof(uBuf))
1162 offBuf += offNext;
1163 else
1164 break;
1165 }
1166
1167 /*
1168 * Read the next chunk.
1169 */
1170 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1171 NULL, /* hEvent */
1172 NULL, /* pfnApcComplete */
1173 NULL, /* pvApcCompleteCtx */
1174 &Ios,
1175 &uBuf,
1176 sizeof(uBuf),
1177 enmInfoClass,
1178 FALSE, /* fReturnSingleEntry */
1179 NULL, /* Filter / restart pos. */
1180 FALSE); /* fRestartScan */
1181 }
1182
1183 if (rcNt == MY_STATUS_NO_MORE_FILES)
1184 {
1185 /*
1186 * Mark the directory as fully populated and up to date.
1187 */
1188 pDir->fPopulated = K_TRUE;
1189 if (pDir->Obj.uCacheGen != KFSOBJ_CACHE_GEN_IGNORE)
1190 pDir->Obj.uCacheGen = pCache->uGeneration;
1191 return K_TRUE;
1192 }
1193
1194 kHlpAssertMsgFailed(("%#x\n", rcNt));
1195 *penmError = KFSLOOKUPERROR_DIR_READ_ERROR;
1196 return K_TRUE;
1197}
1198
1199
1200/**
1201 * Does the initial directory populating or refreshes it if it has been
1202 * invalidated.
1203 *
1204 * This assumes the parent directory is opened.
1205 *
1206 * @returns K_TRUE on success, K_FALSE on error.
1207 * @param pCache The cache.
1208 * @param pDir The directory.
1209 * @param penmError Where to store K_FALSE explanation. Optional.
1210 */
1211KBOOL kFsCacheDirEnsurePopuplated(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
1212{
1213 KFSLOOKUPERROR enmIgnored;
1214 if ( pDir->fPopulated
1215 && ( pDir->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1216 || pDir->Obj.uCacheGen == pCache->uGeneration) )
1217 return K_TRUE;
1218 return kFsCachePopuplateOrRefreshDir(pCache, pDir, penmError ? penmError : &enmIgnored);
1219}
1220
1221
1222/**
1223 * Checks whether the modified timestamp differs on this directory.
1224 *
1225 * @returns K_TRUE if possibly modified, K_FALSE if definitely not modified.
1226 * @param pDir The directory..
1227 */
1228static KBOOL kFsCacheDirIsModified(PKFSDIR pDir)
1229{
1230 if ( pDir->hDir != INVALID_HANDLE_VALUE
1231 && (pDir->Obj.fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
1232 {
1233 MY_IO_STATUS_BLOCK Ios;
1234 MY_FILE_BASIC_INFORMATION BasicInfo;
1235 MY_NTSTATUS rcNt;
1236
1237 Ios.Information = -1;
1238 Ios.u.Status = -1;
1239
1240 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
1241 if (MY_NT_SUCCESS(rcNt))
1242 return BasicInfo.LastWriteTime.QuadPart != pDir->iLastWrite;
1243 }
1244
1245 return K_TRUE;
1246}
1247
1248
1249static KBOOL kFsCacheRefreshMissing(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1250{
1251 /*
1252 * If we can, we start by checking whether the parent directory
1253 * has been modified. If it has, we need to check if this entry
1254 * was added or not, most likely it wasn't added.
1255 */
1256 if (!kFsCacheDirIsModified(pMissing->pParent))
1257 pMissing->uCacheGen = pCache->uGenerationMissing;
1258 else
1259 {
1260 MY_UNICODE_STRING UniStr;
1261 MY_OBJECT_ATTRIBUTES ObjAttr;
1262 MY_FILE_BASIC_INFORMATION BasicInfo;
1263 MY_NTSTATUS rcNt;
1264
1265 UniStr.Buffer = (wchar_t *)pMissing->pwszName;
1266 UniStr.Length = (USHORT)(pMissing->cwcName * sizeof(wchar_t));
1267 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1268
1269 kHlpAssert(pMissing->pParent->hDir != INVALID_HANDLE_VALUE);
1270 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pMissing->pParent->hDir, NULL /*pSecAttr*/);
1271
1272 rcNt = g_pfnNtQueryAttributesFile(&ObjAttr, &BasicInfo);
1273 if (!MY_NT_SUCCESS(rcNt))
1274 {
1275 /*
1276 * Probably more likely that a missing node stays missing.
1277 */
1278 pMissing->uCacheGen = pCache->uGenerationMissing;
1279 }
1280 else
1281 {
1282 /*
1283 * We must metamorphose this node. This is tedious business
1284 * because we need to check the file name casing. We might
1285 * just as well update the parent directory...
1286 */
1287 /** @todo */
1288 __debugbreak();
1289 }
1290 }
1291
1292 return K_TRUE;
1293}
1294
1295
1296static KBOOL kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1297{
1298 if (kFsCacheRefreshMissing(pCache, pMissing, penmError))
1299 {
1300 if ( pMissing->bObjType == KFSOBJ_TYPE_DIR
1301 || pMissing->bObjType == KFSOBJ_TYPE_MISSING)
1302 return K_TRUE;
1303 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1304 }
1305
1306 return K_FALSE;
1307}
1308
1309
1310static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError)
1311{
1312 if (pObj->bObjType == KFSOBJ_TYPE_MISSING)
1313 return kFsCacheRefreshMissing(pCache, pObj, penmError);
1314
1315 __debugbreak();
1316 return K_FALSE;
1317}
1318
1319
1320
1321/**
1322 * Looks up a drive letter.
1323 *
1324 * Will enter the drive if necessary.
1325 *
1326 * @returns Pointer to the root directory of the drive or an update-to-date
1327 * missing node.
1328 * @param pCache The cache.
1329 * @param chLetter The uppercased drive letter.
1330 * @param penmError Where to return details as to why the lookup
1331 * failed.
1332 */
1333static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError)
1334{
1335 KU32 const uHash = chLetter - 'A';
1336 KU32 cLeft;
1337 PKFSOBJ *ppCur;
1338
1339 MY_UNICODE_STRING NtPath;
1340 wchar_t wszTmp[8];
1341 char szTmp[4];
1342
1343 /*
1344 * Custom drive letter hashing.
1345 */
1346 if (pCache->RootDir.paHashTab)
1347 {
1348 /** @todo PKFSOBJHASH pHash = */
1349 }
1350
1351 /*
1352 * Special cased lookup.
1353 */
1354 cLeft = pCache->RootDir.cChildren;
1355 ppCur = pCache->RootDir.papChildren;
1356 while (cLeft-- > 0)
1357 {
1358 PKFSOBJ pCur = *ppCur++;
1359 if ( pCur->cchName == 2
1360 && pCur->pszName[0] == chLetter
1361 && pCur->pszName[1] == ':')
1362 {
1363 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
1364 return pCur;
1365 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
1366 if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
1367 return pCur;
1368 return NULL;
1369 }
1370 }
1371
1372 /*
1373 * Need to add it. We always keep the drive letters open for the benefit
1374 * of kFsCachePopuplateOrRefreshDir and others.
1375 */
1376 wszTmp[0] = szTmp[0] = chLetter;
1377 wszTmp[1] = szTmp[1] = ':';
1378 wszTmp[2] = szTmp[2] = '\\';
1379 wszTmp[3] = '.';
1380 wszTmp[4] = '\0';
1381 szTmp[2] = '\0';
1382
1383 NtPath.Buffer = NULL;
1384 NtPath.Length = 0;
1385 NtPath.MaximumLength = 0;
1386 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
1387 {
1388 HANDLE hDir;
1389 MY_NTSTATUS rcNt;
1390 rcNt = birdOpenFileUniStr(&NtPath,
1391 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1392 FILE_ATTRIBUTE_NORMAL,
1393 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1394 FILE_OPEN,
1395 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1396 OBJ_CASE_INSENSITIVE,
1397 &hDir);
1398 birdFreeNtPath(&NtPath);
1399 if (MY_NT_SUCCESS(rcNt))
1400 {
1401 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1402#ifdef KFSCACHE_CFG_SHORT_NAMES
1403 NULL, 0, NULL, 0,
1404#endif
1405 KFSOBJ_TYPE_DIR, penmError);
1406 if (pDir)
1407 {
1408 /*
1409 * We need a little bit of extra info for a drive root. These things are typically
1410 * inherited by subdirectories down the tree, so, we do it all here for till that changes.
1411 */
1412 union
1413 {
1414 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
1415 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
1416 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
1417 } uBuf;
1418 MY_IO_STATUS_BLOCK Ios;
1419 KBOOL fRc;
1420
1421 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
1422 pDir->hDir = hDir;
1423
1424 if (birdStatHandle(hDir, &pDir->Obj.Stats, pDir->Obj.pszName) == 0)
1425 {
1426 pDir->Obj.fHaveStats = K_TRUE;
1427 pDir->uDevNo = pDir->Obj.Stats.st_dev;
1428 }
1429 else
1430 {
1431 /* Just in case. */
1432 pDir->Obj.fHaveStats = K_FALSE;
1433 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
1434 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
1435 }
1436
1437 /* Get the file system. */
1438 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
1439 Ios.Information = -1;
1440 Ios.u.Status = -1;
1441 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
1442 MyFileFsAttributeInformation);
1443 if (MY_NT_SUCCESS(rcNt))
1444 rcNt = Ios.u.Status;
1445 if (MY_NT_SUCCESS(rcNt))
1446 {
1447 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N'
1448 && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
1449 && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
1450 && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
1451 && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
1452 {
1453 DWORD dwDriveType = GetDriveTypeW(wszTmp);
1454 if ( dwDriveType == DRIVE_FIXED
1455 || dwDriveType == DRIVE_RAMDISK)
1456 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
1457 }
1458 }
1459
1460 /*
1461 * Link the new drive letter into the root dir.
1462 */
1463 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
1464 kFsCacheObjRelease(pCache, &pDir->Obj);
1465 return fRc ? &pDir->Obj : NULL;
1466 }
1467
1468 g_pfnNtClose(hDir);
1469 return NULL;
1470 }
1471
1472 /* Assume it doesn't exist if this happens... This may be a little to
1473 restrictive wrt status code checks. */
1474 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
1475 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
1476 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
1477 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
1478 ("%#x\n", rcNt),
1479 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
1480 NULL);
1481 }
1482 else
1483 {
1484 kHlpAssertFailed();
1485 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
1486 return NULL;
1487 }
1488
1489 /*
1490 * Maybe create a missing entry.
1491 */
1492 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1493 {
1494 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1495#ifdef KFSCACHE_CFG_SHORT_NAMES
1496 NULL, 0, NULL, 0,
1497#endif
1498 KFSOBJ_TYPE_MISSING, penmError);
1499 if (pMissing)
1500 {
1501 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
1502 kFsCacheObjRelease(pCache, pMissing);
1503 return fRc ? pMissing : NULL;
1504 }
1505 }
1506 else
1507 {
1508 /** @todo this isn't necessary correct for a root spec. */
1509 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1510 }
1511 return NULL;
1512}
1513
1514
1515/**
1516 * Look up a child node, ANSI version.
1517 *
1518 * @returns Pointer to the child if found, NULL if not.
1519 * @param pCache The cache.
1520 * @param pParent The parent directory to search.
1521 * @param pchName The child name to search for (not terminated).
1522 * @param cchName The length of the child name.
1523 */
1524static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
1525{
1526 /* Check for '.' first. */
1527 if (cchName != 1 || *pchName != '.')
1528 {
1529 KU32 cLeft;
1530 PKFSOBJ *ppCur;
1531
1532 if (pParent->paHashTab != NULL)
1533 {
1534 /** @todo directory hash table lookup. */
1535 }
1536
1537 /* Linear search. */
1538 cLeft = pParent->cChildren;
1539 ppCur = pParent->papChildren;
1540 while (cLeft-- > 0)
1541 {
1542 PKFSOBJ pCur = *ppCur++;
1543 if ( ( pCur->cchName == cchName
1544 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
1545#ifdef KFSCACHE_CFG_SHORT_NAMES
1546 || ( pCur->cchShortName == cchName
1547 && pCur->pszShortName != pCur->pszName
1548 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
1549#endif
1550 )
1551 return pCur;
1552 }
1553 return NULL;
1554 }
1555 return &pParent->Obj;
1556}
1557
1558
1559/**
1560 * For use when kFsCacheIAreEqualW hit's something non-trivial.
1561 *
1562 * @returns K_TRUE if equal, K_FALSE if different.
1563 * @param pwcName1 The first string.
1564 * @param pwcName2 The second string.
1565 * @param cwcName The length of the two strings (in wchar_t's).
1566 */
1567KBOOL kFsCacheIAreEqualSlowW(const wchar_t *pwcName1, const wchar_t *pwcName2, KU16 cwcName)
1568{
1569 MY_UNICODE_STRING UniStr1 = { cwcName * sizeof(wchar_t), cwcName * sizeof(wchar_t), (wchar_t *)pwcName1 };
1570 MY_UNICODE_STRING UniStr2 = { cwcName * sizeof(wchar_t), cwcName * sizeof(wchar_t), (wchar_t *)pwcName2 };
1571 return g_pfnRtlEqualUnicodeString(&UniStr1, &UniStr2, TRUE /*fCaseInsensitive*/);
1572}
1573
1574
1575/**
1576 * Compares two UTF-16 strings in a case-insensitive fashion.
1577 *
1578 * You would think we should be using _wscnicmp here instead, however it is
1579 * locale dependent and defaults to ASCII upper/lower handling setlocale hasn't
1580 * been called.
1581 *
1582 * @returns K_TRUE if equal, K_FALSE if different.
1583 * @param pwcName1 The first string.
1584 * @param pwcName2 The second string.
1585 * @param cwcName The length of the two strings (in wchar_t's).
1586 */
1587K_INLINE KBOOL kFsCacheIAreEqualW(const wchar_t *pwcName1, const wchar_t *pwcName2, KU32 cwcName)
1588{
1589 while (cwcName > 0)
1590 {
1591 wchar_t wc1 = *pwcName1;
1592 wchar_t wc2 = *pwcName2;
1593 if (wc1 == wc2)
1594 { /* not unlikely */ }
1595 else if ( (KU16)wc1 < (KU16)0xc0 /* U+00C0 is the first upper/lower letter after 'z'. */
1596 && (KU16)wc2 < (KU16)0xc0)
1597 {
1598 /* ASCII upper case. */
1599 if ((KU16)wc1 - (KU16)0x61 < (KU16)26)
1600 wc1 &= ~(wchar_t)0x20;
1601 if ((KU16)wc2 - (KU16)0x61 < (KU16)26)
1602 wc2 &= ~(wchar_t)0x20;
1603 if (wc1 != wc2)
1604 return K_FALSE;
1605 }
1606 else
1607 return kFsCacheIAreEqualSlowW(pwcName1, pwcName2, (KU16)cwcName);
1608
1609 pwcName2++;
1610 pwcName1++;
1611 cwcName--;
1612 }
1613
1614 return K_TRUE;
1615}
1616
1617
1618/**
1619 * Look up a child node, UTF-16 version.
1620 *
1621 * @returns Pointer to the child if found, NULL if not.
1622 * @param pCache The cache.
1623 * @param pParent The parent directory to search.
1624 * @param pwcName The child name to search for (not terminated).
1625 * @param cwcName The length of the child name (in wchar_t's).
1626 */
1627static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
1628{
1629 /* Check for '.' first. */
1630 if (cwcName != 1 || *pwcName != '.')
1631 {
1632 KU32 cLeft;
1633 PKFSOBJ *ppCur;
1634
1635 if (pParent->paHashTab != NULL)
1636 {
1637 /** @todo directory hash table lookup. */
1638 }
1639
1640 /* Linear search. */
1641 cLeft = pParent->cChildren;
1642 ppCur = pParent->papChildren;
1643 while (cLeft-- > 0)
1644 {
1645 PKFSOBJ pCur = *ppCur++;
1646 if ( ( pCur->cwcName == cwcName
1647 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
1648#ifdef KFSCACHE_CFG_SHORT_NAMES
1649 || ( pCur->cwcShortName == cwcName
1650 && pCur->pwszShortName != pCur->pwszName
1651 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
1652#endif
1653 )
1654 return pCur;
1655 }
1656 return NULL;
1657 }
1658 return &pParent->Obj;
1659}
1660
1661
1662/**
1663 * Looks up a UNC share, ANSI version.
1664 *
1665 * We keep both the server and share in the root directory entry. This means we
1666 * have to clean up the entry name before we can insert it.
1667 *
1668 * @returns Pointer to the share root directory or an update-to-date missing
1669 * node.
1670 * @param pCache The cache.
1671 * @param pszPath The path.
1672 * @param poff Where to return the root dire.
1673 * @param penmError Where to return details as to why the lookup
1674 * failed.
1675 */
1676static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
1677{
1678#if 0 /* later */
1679 KU32 offStartServer;
1680 KU32 offEndServer;
1681 KU32 offStartShare;
1682
1683 KU32 offEnd = 2;
1684 while (IS_SLASH(pszPath[offEnd]))
1685 offEnd++;
1686
1687 offStartServer = offEnd;
1688 while ( (ch = pszPath[offEnd]) != '\0'
1689 && !IS_SLASH(ch))
1690 offEnd++;
1691 offEndServer = offEnd;
1692
1693 if (ch != '\0')
1694 { /* likely */ }
1695 else
1696 {
1697 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1698 return NULL;
1699 }
1700
1701 while (IS_SLASH(pszPath[offEnd]))
1702 offEnd++;
1703 offStartServer = offEnd;
1704 while ( (ch = pszPath[offEnd]) != '\0'
1705 && !IS_SLASH(ch))
1706 offEnd++;
1707#endif
1708 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
1709 return NULL;
1710}
1711
1712
1713/**
1714 * Looks up a UNC share, UTF-16 version.
1715 *
1716 * We keep both the server and share in the root directory entry. This means we
1717 * have to clean up the entry name before we can insert it.
1718 *
1719 * @returns Pointer to the share root directory or an update-to-date missing
1720 * node.
1721 * @param pCache The cache.
1722 * @param pwszPath The path.
1723 * @param poff Where to return the root dire.
1724 * @param penmError Where to return details as to why the lookup
1725 * failed.
1726 */
1727static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
1728{
1729#if 0 /* later */
1730 KU32 offStartServer;
1731 KU32 offEndServer;
1732 KU32 offStartShare;
1733
1734 KU32 offEnd = 2;
1735 while (IS_SLASH(pwszPath[offEnd]))
1736 offEnd++;
1737
1738 offStartServer = offEnd;
1739 while ( (ch = pwszPath[offEnd]) != '\0'
1740 && !IS_SLASH(ch))
1741 offEnd++;
1742 offEndServer = offEnd;
1743
1744 if (ch != '\0')
1745 { /* likely */ }
1746 else
1747 {
1748 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1749 return NULL;
1750 }
1751
1752 while (IS_SLASH(pwszPath[offEnd]))
1753 offEnd++;
1754 offStartServer = offEnd;
1755 while ( (ch = pwszPath[offEnd]) != '\0'
1756 && !IS_SLASH(ch))
1757 offEnd++;
1758#endif
1759 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
1760 return NULL;
1761}
1762
1763
1764/**
1765 * Walks an full path relative to the given directory, ANSI version.
1766 *
1767 * This will create any missing nodes while walking.
1768 *
1769 * The caller will have to do the path hash table insertion of the result.
1770 *
1771 * @returns Pointer to the tree node corresponding to @a pszPath.
1772 * NULL on lookup failure, see @a penmError for details.
1773 * @param pCache The cache.
1774 * @param pParent The directory to start the lookup in.
1775 * @param pszPath The path to walk.
1776 * @param cchPath The length of the path.
1777 * @param penmError Where to return details as to why the lookup
1778 * failed.
1779 */
1780PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath,
1781 KFSLOOKUPERROR *penmError)
1782{
1783 /*
1784 * Walk loop.
1785 */
1786 KU32 off = 0;
1787 for (;;)
1788 {
1789 PKFSOBJ pChild;
1790
1791 /*
1792 * Find the end of the component, counting trailing slashes.
1793 */
1794 char ch;
1795 KU32 cchSlashes = 0;
1796 KU32 offEnd = off + 1;
1797 while ((ch = pszPath[offEnd]) != '\0')
1798 {
1799 if (!IS_SLASH(ch))
1800 offEnd++;
1801 else
1802 {
1803 do
1804 cchSlashes++;
1805 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
1806 break;
1807 }
1808 }
1809
1810 /*
1811 * Do we need to populate or refresh this directory first?
1812 */
1813 if ( pParent->fPopulated
1814 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1815 || pParent->Obj.uCacheGen == pCache->uGeneration) )
1816 { /* likely */ }
1817 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
1818 { /* likely */ }
1819 else
1820 return NULL;
1821
1822 /*
1823 * Search the current node for the name.
1824 *
1825 * If we don't find it, we may insert a missing node depending on
1826 * the cache configuration.
1827 */
1828 pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
1829 if (pChild != NULL)
1830 { /* probably likely */ }
1831 else
1832 {
1833 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1834 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
1835 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
1836 {
1837 if (pChild)
1838 return kFsCacheObjRetainInternal(pChild);
1839 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1840 }
1841 else
1842 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1843 return NULL;
1844 }
1845
1846 /* Advance off and check if we're done already. */
1847 off = offEnd + cchSlashes;
1848 if ( cchSlashes == 0
1849 || off >= cchPath)
1850 {
1851 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
1852 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1853 || pChild->uCacheGen == pCache->uGenerationMissing
1854 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
1855 { /* likely */ }
1856 else
1857 return NULL;
1858 return kFsCacheObjRetainInternal(pChild);
1859 }
1860
1861 /*
1862 * Check that it's a directory. If a missing entry, we may have to
1863 * refresh it and re-examin it.
1864 */
1865 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
1866 pParent = (PKFSDIR)pChild;
1867 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
1868 {
1869 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1870 return NULL;
1871 }
1872 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1873 || pChild->uCacheGen == pCache->uGenerationMissing)
1874 {
1875 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1876 return NULL;
1877 }
1878 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
1879 pParent = (PKFSDIR)pChild;
1880 else
1881 return NULL;
1882 }
1883
1884 return NULL;
1885
1886}
1887
1888
1889/**
1890 * Walks an full path relative to the given directory, UTF-16 version.
1891 *
1892 * This will create any missing nodes while walking.
1893 *
1894 * The caller will have to do the path hash table insertion of the result.
1895 *
1896 * @returns Pointer to the tree node corresponding to @a pszPath.
1897 * NULL on lookup failure, see @a penmError for details.
1898 * @param pCache The cache.
1899 * @param pParent The directory to start the lookup in.
1900 * @param pszPath The path to walk. No dot-dot bits allowed!
1901 * @param cchPath The length of the path.
1902 * @param penmError Where to return details as to why the lookup
1903 * failed.
1904 */
1905PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath,
1906 KFSLOOKUPERROR *penmError)
1907{
1908 /*
1909 * Walk loop.
1910 */
1911 KU32 off = 0;
1912 for (;;)
1913 {
1914 PKFSOBJ pChild;
1915
1916 /*
1917 * Find the end of the component, counting trailing slashes.
1918 */
1919 wchar_t wc;
1920 KU32 cwcSlashes = 0;
1921 KU32 offEnd = off + 1;
1922 while ((wc = pwszPath[offEnd]) != '\0')
1923 {
1924 if (!IS_SLASH(wc))
1925 offEnd++;
1926 else
1927 {
1928 do
1929 cwcSlashes++;
1930 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
1931 break;
1932 }
1933 }
1934
1935 /*
1936 * Do we need to populate or refresh this directory first?
1937 */
1938 if ( pParent->fPopulated
1939 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1940 || pParent->Obj.uCacheGen == pCache->uGeneration) )
1941 { /* likely */ }
1942 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
1943 { /* likely */ }
1944 else
1945 return NULL;
1946
1947 /*
1948 * Search the current node for the name.
1949 *
1950 * If we don't find it, we may insert a missing node depending on
1951 * the cache configuration.
1952 */
1953 pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off);
1954 if (pChild != NULL)
1955 { /* probably likely */ }
1956 else
1957 {
1958 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1959 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
1960 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
1961 {
1962 if (pChild)
1963 return kFsCacheObjRetainInternal(pChild);
1964 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1965 }
1966 else
1967 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1968 return NULL;
1969 }
1970
1971 /* Advance off and check if we're done already. */
1972 off = offEnd + cwcSlashes;
1973 if ( cwcSlashes == 0
1974 || off >= cwcPath)
1975 {
1976 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
1977 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1978 || pChild->uCacheGen == pCache->uGenerationMissing
1979 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
1980 { /* likely */ }
1981 else
1982 return NULL;
1983 return kFsCacheObjRetainInternal(pChild);
1984 }
1985
1986 /*
1987 * Check that it's a directory. If a missing entry, we may have to
1988 * refresh it and re-examin it.
1989 */
1990 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
1991 pParent = (PKFSDIR)pChild;
1992 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
1993 {
1994 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1995 return NULL;
1996 }
1997 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1998 || pChild->uCacheGen == pCache->uGenerationMissing)
1999 {
2000 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2001 return NULL;
2002 }
2003 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2004 pParent = (PKFSDIR)pChild;
2005 else
2006 return NULL;
2007 }
2008
2009 return NULL;
2010
2011}
2012
2013/**
2014 * Walk the file system tree for the given absolute path, entering it into the
2015 * hash table.
2016 *
2017 * This will create any missing nodes while walking.
2018 *
2019 * The caller will have to do the path hash table insertion of the result.
2020 *
2021 * @returns Pointer to the tree node corresponding to @a pszPath.
2022 * NULL on lookup failure, see @a penmError for details.
2023 * @param pCache The cache.
2024 * @param pszPath The path to walk. No dot-dot bits allowed!
2025 * @param cchPath The length of the path.
2026 * @param penmError Where to return details as to why the lookup
2027 * failed.
2028 */
2029static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
2030{
2031 PKFSOBJ pChild;
2032 KU32 cchSlashes;
2033 KU32 offEnd;
2034
2035 KFSCACHE_LOG(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
2036
2037 /*
2038 * The root "directory" needs special handling, so we keep it outside the
2039 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
2040 */
2041 cchSlashes = 0;
2042 if ( pszPath[1] == ':'
2043 && IS_ALPHA(pszPath[0]))
2044 {
2045 /* Drive letter. */
2046 offEnd = 2;
2047 kHlpAssert(IS_SLASH(pszPath[2]));
2048 pChild = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);
2049 }
2050 else if ( IS_SLASH(pszPath[0])
2051 && IS_SLASH(pszPath[1]) )
2052 pChild = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);
2053 else
2054 {
2055 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2056 return NULL;
2057 }
2058 if (pChild)
2059 { /* likely */ }
2060 else
2061 return NULL;
2062
2063 /* Count slashes trailing the root spec. */
2064 if (offEnd < cchPath)
2065 {
2066 kHlpAssert(IS_SLASH(pszPath[offEnd]));
2067 do
2068 cchSlashes++;
2069 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2070 }
2071
2072 /* Done already? */
2073 if (offEnd >= cchPath)
2074 {
2075 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2076 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
2077 || kFsCacheRefreshObj(pCache, pChild, penmError))
2078 return kFsCacheObjRetainInternal(pChild);
2079 return NULL;
2080 }
2081
2082 /* Check that we've got a valid result and not a cached negative one. */
2083 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2084 { /* likely */ }
2085 else
2086 {
2087 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
2088 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
2089 return pChild;
2090 }
2091
2092 /*
2093 * Now that we've found a valid root directory, lookup the
2094 * remainder of the path starting with it.
2095 */
2096 return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pChild, &pszPath[offEnd + cchSlashes],
2097 cchPath - offEnd - cchSlashes, penmError);
2098}
2099
2100
2101/**
2102 * Walk the file system tree for the given absolute path, UTF-16 version.
2103 *
2104 * This will create any missing nodes while walking.
2105 *
2106 * The caller will have to do the path hash table insertion of the result.
2107 *
2108 * @returns Pointer to the tree node corresponding to @a pszPath.
2109 * NULL on lookup failure, see @a penmError for details.
2110 * @param pCache The cache.
2111 * @param pwszPath The path to walk.
2112 * @param cwcPath The length of the path (in wchar_t's).
2113 * @param penmError Where to return details as to why the lookup
2114 * failed.
2115 */
2116static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KFSLOOKUPERROR *penmError)
2117{
2118 PKFSDIR pParent = &pCache->RootDir;
2119 PKFSOBJ pChild;
2120 KU32 off;
2121 KU32 cwcSlashes;
2122 KU32 offEnd;
2123
2124 KFSCACHE_LOG(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath));
2125
2126 /*
2127 * The root "directory" needs special handling, so we keep it outside the
2128 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
2129 */
2130 cwcSlashes = 0;
2131 off = 0;
2132 if ( pwszPath[1] == ':'
2133 && IS_ALPHA(pwszPath[0]))
2134 {
2135 /* Drive letter. */
2136 offEnd = 2;
2137 kHlpAssert(IS_SLASH(pwszPath[2]));
2138 pChild = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError);
2139 }
2140 else if ( IS_SLASH(pwszPath[0])
2141 && IS_SLASH(pwszPath[1]) )
2142 pChild = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError);
2143 else
2144 {
2145 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2146 return NULL;
2147 }
2148 if (pChild)
2149 { /* likely */ }
2150 else
2151 return NULL;
2152
2153 /* Count slashes trailing the root spec. */
2154 if (offEnd < cwcPath)
2155 {
2156 kHlpAssert(IS_SLASH(pwszPath[offEnd]));
2157 do
2158 cwcSlashes++;
2159 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2160 }
2161
2162 /* Done already? */
2163 if (offEnd >= cwcPath)
2164 {
2165 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2166 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
2167 || kFsCacheRefreshObj(pCache, pChild, penmError))
2168 return kFsCacheObjRetainInternal(pChild);
2169 return NULL;
2170 }
2171
2172 /* Check that we've got a valid result and not a cached negative one. */
2173 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2174 { /* likely */ }
2175 else
2176 {
2177 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
2178 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
2179 return pChild;
2180 }
2181
2182 /*
2183 * Now that we've found a valid root directory, lookup the
2184 * remainder of the path starting with it.
2185 */
2186 return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pChild, &pwszPath[offEnd + cwcSlashes],
2187 cwcPath - offEnd - cwcSlashes, penmError);
2188}
2189
2190
2191/**
2192 * This deals with paths that are relative and paths that contains '..'
2193 * elements, ANSI version.
2194 *
2195 * @returns Pointer to object corresponding to @a pszPath on success.
2196 * NULL if this isn't a path we care to cache.
2197 *
2198 * @param pCache The cache.
2199 * @param pszPath The path.
2200 * @param cchPath The length of the path.
2201 * @param penmError Where to return details as to why the lookup
2202 * failed.
2203 */
2204static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
2205{
2206 /*
2207 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2208 * ends up calling it anyway.
2209 */
2210 char szFull[KFSCACHE_CFG_MAX_PATH];
2211 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
2212 if ( cchFull >= 3
2213 && cchFull < sizeof(szFull))
2214 {
2215 PKFSOBJ pFsObj;
2216 KFSCACHE_LOG(("kFsCacheLookupSlowA(%s)\n", pszPath));
2217 pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError);
2218
2219#if 0 /* No need to do this until it's actually queried. */
2220 /* Cache the resulting path. */
2221 if ( pFsObj
2222 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2223 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2224 {
2225 KU32 uHashPath = kFsCacheStrHash(szFull);
2226 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2227 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2228 }
2229#endif
2230 return pFsObj;
2231 }
2232
2233 /* The path is too long! */
2234 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
2235 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2236 return NULL;
2237}
2238
2239
2240/**
2241 * This deals with paths that are relative and paths that contains '..'
2242 * elements, UTF-16 version.
2243 *
2244 * @returns Pointer to object corresponding to @a pszPath on success.
2245 * NULL if this isn't a path we care to cache.
2246 *
2247 * @param pCache The cache.
2248 * @param pwszPath The path.
2249 * @param cwcPath The length of the path (in wchar_t's).
2250 * @param penmError Where to return details as to why the lookup
2251 * failed.
2252 */
2253static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KFSLOOKUPERROR *penmError)
2254{
2255 /*
2256 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2257 * ends up calling it anyway.
2258 */
2259 wchar_t wszFull[KFSCACHE_CFG_MAX_PATH];
2260 UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL);
2261 if ( cwcFull >= 3
2262 && cwcFull < KFSCACHE_CFG_MAX_PATH)
2263 {
2264 PKFSOBJ pFsObj;
2265 KFSCACHE_LOG(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
2266 pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError);
2267
2268#if 0 /* No need to do this until it's actually queried. */
2269 /* Cache the resulting path. */
2270 if ( pFsObj
2271 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2272 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2273 {
2274 KU32 uHashPath = kFsCacheStrHash(szFull);
2275 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2276 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2277 }
2278#endif
2279 return pFsObj;
2280 }
2281
2282 /* The path is too long! */
2283 kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull));
2284 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2285 return NULL;
2286}
2287
2288
2289/**
2290 * Refreshes a path hash that has expired, ANSI version.
2291 *
2292 * @returns pHash on success, NULL if removed.
2293 * @param pCache The cache.
2294 * @param pHashEntry The path hash.
2295 * @param idxHashTab The hash table entry.
2296 */
2297static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
2298{
2299 if (!pHashEntry->pFsObj)
2300 {
2301 if (pHashEntry->fAbsolute)
2302 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, &pHashEntry->enmError);
2303 else
2304 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, &pHashEntry->enmError);
2305 }
2306 else
2307 {
2308 KFSLOOKUPERROR enmError;
2309 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
2310 {
2311 }
2312 __debugbreak(); /** @todo implement once we've start inserting uCacheGen nodes. */
2313 K_NOREF(pCache);
2314 K_NOREF(idxHashTab);
2315 }
2316 pHashEntry->uCacheGen = pCache->uGenerationMissing;
2317 return pHashEntry;
2318}
2319
2320
2321/**
2322 * Refreshes a path hash that has expired, UTF-16 version.
2323 *
2324 * @returns pHash on success, NULL if removed.
2325 * @param pCache The cache.
2326 * @param pHashEntry The path hash.
2327 * @param idxHashTab The hash table entry.
2328 */
2329static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab)
2330{
2331 if (!pHashEntry->pFsObj)
2332 {
2333 if (pHashEntry->fAbsolute)
2334 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, &pHashEntry->enmError);
2335 else
2336 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, &pHashEntry->enmError);
2337 }
2338 else
2339 {
2340 KFSLOOKUPERROR enmError;
2341 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
2342 {
2343 }
2344 __debugbreak(); /** @todo implement once we've start inserting uCacheGen nodes. */
2345 K_NOREF(pCache);
2346 K_NOREF(idxHashTab);
2347 }
2348 return pHashEntry;
2349}
2350
2351
2352/**
2353 * Looks up a KFSOBJ for the given ANSI path.
2354 *
2355 * This will first try the hash table. If not in the hash table, the file
2356 * system cache tree is walked, missing bits filled in and finally a hash table
2357 * entry is created.
2358 *
2359 * Only drive letter paths are cachable. We don't do any UNC paths at this
2360 * point.
2361 *
2362 * @returns Reference to object corresponding to @a pszPath on success, this
2363 * must be released by kFsCacheObjRelease.
2364 * NULL if not a path we care to cache.
2365 * @param pCache The cache.
2366 * @param pszPath The path to lookup.
2367 * @param penmError Where to return details as to why the lookup
2368 * failed.
2369 */
2370PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
2371{
2372 /*
2373 * Do hash table lookup of the path.
2374 */
2375 KU32 uHashPath;
2376 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
2377 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
2378 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab];
2379 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2380 if (pHashEntry)
2381 {
2382 do
2383 {
2384 if ( pHashEntry->uHashPath == uHashPath
2385 && pHashEntry->cchPath == cchPath
2386 && kHlpMemComp(pHashEntry->pszPath, pszPath, cchPath) == 0)
2387 {
2388 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2389 || pHashEntry->uCacheGen == (pHashEntry->pFsObj ? pCache->uGeneration : pCache->uGenerationMissing)
2390 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
2391 {
2392 pCache->cLookups++;
2393 pCache->cPathHashHits++;
2394 KFSCACHE_LOG(("kFsCacheLookupA(%s) - hit %p\n", pszPath, pHashEntry->pFsObj));
2395 *penmError = pHashEntry->enmError;
2396 if (pHashEntry->pFsObj)
2397 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
2398 return NULL;
2399 }
2400 break;
2401 }
2402 pHashEntry = pHashEntry->pNext;
2403 } while (pHashEntry);
2404 }
2405
2406 /*
2407 * Create an entry for it by walking the file system cache and filling in the blanks.
2408 */
2409 if ( cchPath > 0
2410 && cchPath < KFSCACHE_CFG_MAX_PATH)
2411 {
2412 PKFSOBJ pFsObj;
2413 KBOOL fAbsolute;
2414
2415 /* Is absolute without any '..' bits? */
2416 if ( cchPath >= 3
2417 && ( ( pszPath[1] == ':' /* Drive letter */
2418 && IS_SLASH(pszPath[2])
2419 && IS_ALPHA(pszPath[0]) )
2420 || ( IS_SLASH(pszPath[0]) /* UNC */
2421 && IS_SLASH(pszPath[1]) ) )
2422 && !kFsCacheHasDotDotA(pszPath, cchPath) )
2423 {
2424 pFsObj = kFsCacheLookupAbsoluteA(pCache, pszPath, cchPath, penmError);
2425 fAbsolute = K_TRUE;
2426 }
2427 else
2428 {
2429 pFsObj = kFsCacheLookupSlowA(pCache, pszPath, cchPath, penmError);
2430 fAbsolute = K_FALSE;
2431 }
2432 if ( pFsObj
2433 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2434 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
2435 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
2436 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath, idxHashTab, fAbsolute, *penmError);
2437
2438 pCache->cLookups++;
2439 if (pFsObj)
2440 pCache->cWalkHits++;
2441 return pFsObj;
2442 }
2443
2444 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2445 return NULL;
2446}
2447
2448
2449/**
2450 * Looks up a KFSOBJ for the given UTF-16 path.
2451 *
2452 * This will first try the hash table. If not in the hash table, the file
2453 * system cache tree is walked, missing bits filled in and finally a hash table
2454 * entry is created.
2455 *
2456 * Only drive letter paths are cachable. We don't do any UNC paths at this
2457 * point.
2458 *
2459 * @returns Reference to object corresponding to @a pszPath on success, this
2460 * must be released by kFsCacheObjRelease.
2461 * NULL if not a path we care to cache.
2462 * @param pCache The cache.
2463 * @param pwszPath The path to lookup.
2464 * @param penmError Where to return details as to why the lookup
2465 * failed.
2466 */
2467PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
2468{
2469 /*
2470 * Do hash table lookup of the path.
2471 */
2472 KU32 uHashPath;
2473 KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath);
2474 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
2475 PKFSHASHW pHashEntry = pCache->apUtf16Paths[idxHashTab];
2476 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2477 if (pHashEntry)
2478 {
2479 do
2480 {
2481 if ( pHashEntry->uHashPath == uHashPath
2482 && pHashEntry->cwcPath == cwcPath
2483 && kHlpMemComp(pHashEntry->pwszPath, pwszPath, cwcPath) == 0)
2484 {
2485 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2486 || pHashEntry->uCacheGen == (pHashEntry->pFsObj ? pCache->uGeneration : pCache->uGenerationMissing)
2487 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) )
2488 {
2489 pCache->cLookups++;
2490 pCache->cPathHashHits++;
2491 KFSCACHE_LOG(("kFsCacheLookupW(%ls) - hit %p\n", pwszPath, pHashEntry->pFsObj));
2492 *penmError = pHashEntry->enmError;
2493 if (pHashEntry->pFsObj)
2494 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
2495 return NULL;
2496 }
2497 break;
2498 }
2499 pHashEntry = pHashEntry->pNext;
2500 } while (pHashEntry);
2501 }
2502
2503 /*
2504 * Create an entry for it by walking the file system cache and filling in the blanks.
2505 */
2506 if ( cwcPath > 0
2507 && cwcPath < KFSCACHE_CFG_MAX_PATH)
2508 {
2509 PKFSOBJ pFsObj;
2510 KBOOL fAbsolute;
2511
2512 /* Is absolute without any '..' bits? */
2513 if ( cwcPath >= 3
2514 && ( ( pwszPath[1] == ':' /* Drive letter */
2515 && IS_SLASH(pwszPath[2])
2516 && IS_ALPHA(pwszPath[0]) )
2517 || ( IS_SLASH(pwszPath[0]) /* UNC */
2518 && IS_SLASH(pwszPath[1]) ) )
2519 && !kFsCacheHasDotDotW(pwszPath, cwcPath) )
2520 {
2521 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwszPath, cwcPath, penmError);
2522 fAbsolute = K_TRUE;
2523 }
2524 else
2525 {
2526 pFsObj = kFsCacheLookupSlowW(pCache, pwszPath, cwcPath, penmError);
2527 fAbsolute = K_FALSE;
2528 }
2529 if ( pFsObj
2530 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2531 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
2532 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
2533 kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwszPath, cwcPath, uHashPath, idxHashTab, fAbsolute, *penmError);
2534
2535 pCache->cLookups++;
2536 if (pFsObj)
2537 pCache->cWalkHits++;
2538 return pFsObj;
2539 }
2540
2541 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2542 return NULL;
2543}
2544
2545
2546/**
2547 * Wrapper around kFsCacheLookupA that drops KFSOBJ_TYPE_MISSING and returns
2548 * KFSLOOKUPERROR_NOT_FOUND instead.
2549 *
2550 * @returns Reference to object corresponding to @a pszPath on success, this
2551 * must be released by kFsCacheObjRelease.
2552 * NULL if not a path we care to cache.
2553 * @param pCache The cache.
2554 * @param pszPath The path to lookup.
2555 * @param penmError Where to return details as to why the lookup
2556 * failed.
2557 */
2558PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
2559{
2560 PKFSOBJ pObj = kFsCacheLookupA(pCache, pszPath, penmError);
2561 if (pObj)
2562 {
2563 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
2564 return pObj;
2565
2566 kFsCacheObjRelease(pCache, pObj);
2567 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2568 }
2569 return NULL;
2570}
2571
2572
2573/**
2574 * Wrapper around kFsCacheLookupW that drops KFSOBJ_TYPE_MISSING and returns
2575 * KFSLOOKUPERROR_NOT_FOUND instead.
2576 *
2577 * @returns Reference to object corresponding to @a pszPath on success, this
2578 * must be released by kFsCacheObjRelease.
2579 * NULL if not a path we care to cache.
2580 * @param pCache The cache.
2581 * @param pwszPath The path to lookup.
2582 * @param penmError Where to return details as to why the lookup
2583 * failed.
2584 */
2585PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
2586{
2587 PKFSOBJ pObj = kFsCacheLookupW(pCache, pwszPath, penmError);
2588 if (pObj)
2589 {
2590 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
2591 return pObj;
2592
2593 kFsCacheObjRelease(pCache, pObj);
2594 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2595 }
2596 return NULL;
2597}
2598
2599
2600/**
2601 * Destroys a cache object which has a zero reference count.
2602 *
2603 * @returns 0
2604 * @param pCache The cache.
2605 * @param pObj The object.
2606 */
2607KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
2608{
2609 kHlpAssert(pObj->cRefs == 0);
2610 kHlpAssert(pObj->pParent == NULL);
2611 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2612
2613 /*
2614 * Invalidate the structure.
2615 */
2616 pObj->u32Magic = ~KFSOBJ_MAGIC;
2617
2618 /*
2619 * Destroy any user data first.
2620 */
2621 while (pObj->pUserDataHead != NULL)
2622 {
2623 PKFSUSERDATA pUserData = pObj->pUserDataHead;
2624 pObj->pUserDataHead = pUserData->pNext;
2625 if (pUserData->pfnDestructor)
2626 pUserData->pfnDestructor(pCache, pObj, pUserData);
2627 kHlpFree(pUserData);
2628 }
2629
2630 /*
2631 * Do type specific destruction
2632 */
2633 switch (pObj->bObjType)
2634 {
2635 case KFSOBJ_TYPE_MISSING:
2636 /* nothing else to do here */
2637 pCache->cbObjects -= sizeof(KFSDIR);
2638 break;
2639
2640 case KFSOBJ_TYPE_DIR:
2641 {
2642 PKFSDIR pDir = (PKFSDIR)pObj;
2643 KU32 cChildren = pDir->cChildren;
2644 pCache->cbObjects -= sizeof(*pDir)
2645 + K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
2646 + pDir->cHashTab * sizeof(pDir->paHashTab);
2647
2648 pDir->cChildren = 0;
2649 while (cChildren-- > 0)
2650 kFsCacheObjRelease(pCache, pDir->papChildren[cChildren]);
2651 kHlpFree(pDir->papChildren);
2652 pDir->papChildren = NULL;
2653
2654 kHlpFree(pDir->paHashTab);
2655 pDir->paHashTab = NULL;
2656 break;
2657 }
2658
2659 case KFSOBJ_TYPE_FILE:
2660 case KFSOBJ_TYPE_OTHER:
2661 pCache->cbObjects -= sizeof(*pObj);
2662 break;
2663
2664 default:
2665 return 0;
2666 }
2667
2668 /*
2669 * Common bits.
2670 */
2671 pCache->cbObjects -= pObj->cchName + 1;
2672#ifdef KFSCACHE_CFG_UTF16
2673 pCache->cbObjects -= (pObj->cwcName + 1) * sizeof(wchar_t);
2674#endif
2675#ifdef KFSCACHE_CFG_SHORT_NAMES
2676 if (pObj->pszName != pObj->pszShortName)
2677 {
2678 pCache->cbObjects -= pObj->cchShortName + 1;
2679# ifdef KFSCACHE_CFG_UTF16
2680 pCache->cbObjects -= (pObj->cwcShortName + 1) * sizeof(wchar_t);
2681# endif
2682 }
2683#endif
2684 pCache->cObjects--;
2685
2686 kHlpFree(pObj);
2687 return 0;
2688}
2689
2690
2691/**
2692 * Releases a reference to a cache object.
2693 *
2694 * @returns New reference count.
2695 * @param pCache The cache.
2696 * @param pObj The object.
2697 */
2698KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
2699{
2700 if (pObj)
2701 {
2702 KU32 cRefs;
2703 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2704 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2705
2706 cRefs = --pObj->cRefs;
2707 if (cRefs)
2708 return cRefs;
2709 return kFsCacheObjDestroy(pCache, pObj);
2710 }
2711 return 0;
2712}
2713
2714
2715/**
2716 * Retains a reference to a cahce object.
2717 *
2718 * @returns New reference count.
2719 * @param pObj The object.
2720 */
2721KU32 kFsCacheObjRetain(PKFSOBJ pObj)
2722{
2723 KU32 cRefs;
2724 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2725 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2726
2727 cRefs = ++pObj->cRefs;
2728 kHlpAssert(cRefs < 16384);
2729 return cRefs;
2730}
2731
2732
2733/**
2734 * Associates an item of user data with the given object.
2735 *
2736 * If the data needs cleaning up before being free, set the
2737 * PKFSUSERDATA::pfnDestructor member of the returned structure.
2738 *
2739 * @returns Pointer to the user data on success.
2740 * NULL if out of memory or key already in use.
2741 *
2742 * @param pCache The cache.
2743 * @param pObj The object.
2744 * @param uKey The user data key.
2745 * @param cbUserData The size of the user data.
2746 */
2747PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData)
2748{
2749 kHlpAssert(cbUserData >= sizeof(*pNew));
2750 if (kFsCacheObjGetUserData(pCache, pObj, uKey) == NULL)
2751 {
2752 PKFSUSERDATA pNew = (PKFSUSERDATA)kHlpAllocZ(cbUserData);
2753 if (pNew)
2754 {
2755 pNew->uKey = uKey;
2756 pNew->pfnDestructor = NULL;
2757 pNew->pNext = pObj->pUserDataHead;
2758 pObj->pUserDataHead = pNew;
2759 return pNew;
2760 }
2761 }
2762
2763 return NULL;
2764}
2765
2766
2767/**
2768 * Retrieves an item of user data associated with the given object.
2769 *
2770 * @returns Pointer to the associated user data if found, otherwise NULL.
2771 * @param pCache The cache.
2772 * @param pObj The object.
2773 * @param uKey The user data key.
2774 */
2775PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey)
2776{
2777 PKFSUSERDATA pCur;
2778
2779 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2780 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2781
2782 for (pCur = pObj->pUserDataHead; pCur; pCur = pCur->pNext)
2783 if (pCur->uKey == uKey)
2784 return pCur;
2785 return NULL;
2786}
2787
2788
2789/**
2790 * Gets the full path to @a pObj, ANSI version.
2791 *
2792 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
2793 * @param pObj The object to get the full path to.
2794 * @param pszPath Where to return the path
2795 * @param cbPath The size of the output buffer.
2796 * @param chSlash The slash to use.
2797 */
2798KBOOL kFsCacheObjGetFullPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
2799{
2800 KSIZE off = pObj->cchParent;
2801 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2802 if (off > 0)
2803 {
2804 KSIZE offEnd = off + pObj->cchName;
2805 if (offEnd < cbPath)
2806 {
2807 PKFSDIR pAncestor;
2808
2809 pszPath[off + pObj->cchName] = '\0';
2810 memcpy(&pszPath[off], pObj->pszName, pObj->cchName);
2811
2812 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
2813 {
2814 kHlpAssert(off > 1);
2815 kHlpAssert(pAncestor != NULL);
2816 kHlpAssert(pAncestor->Obj.cchName > 0);
2817 pszPath[--off] = chSlash;
2818 off -= pAncestor->Obj.cchName;
2819 kHlpAssert(pAncestor->Obj.cchParent == off);
2820 memcpy(&pszPath[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
2821 }
2822 return K_TRUE;
2823 }
2824 }
2825 else
2826 {
2827 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
2828 off = pObj->cchName;
2829 if (off + fDriveLetter < cbPath)
2830 {
2831 memcpy(pszPath, pObj->pszName, off);
2832 if (fDriveLetter)
2833 pszPath[off++] = chSlash;
2834 pszPath[off] = '\0';
2835 return K_TRUE;
2836 }
2837 }
2838
2839 return K_FALSE;
2840}
2841
2842
2843/**
2844 * Gets the full path to @a pObj, UTF-16 version.
2845 *
2846 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
2847 * @param pObj The object to get the full path to.
2848 * @param pszPath Where to return the path
2849 * @param cbPath The size of the output buffer.
2850 * @param wcSlash The slash to use.
2851 */
2852KBOOL kFsCacheObjGetFullPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
2853{
2854 KSIZE off = pObj->cwcParent;
2855 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2856 if (off > 0)
2857 {
2858 KSIZE offEnd = off + pObj->cwcName;
2859 if (offEnd < cwcPath)
2860 {
2861 PKFSDIR pAncestor;
2862
2863 pwszPath[off + pObj->cwcName] = '\0';
2864 memcpy(&pwszPath[off], pObj->pwszName, pObj->cwcName * sizeof(wchar_t));
2865
2866 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
2867 {
2868 kHlpAssert(off > 1);
2869 kHlpAssert(pAncestor != NULL);
2870 kHlpAssert(pAncestor->Obj.cwcName > 0);
2871 pwszPath[--off] = wcSlash;
2872 off -= pAncestor->Obj.cwcName;
2873 kHlpAssert(pAncestor->Obj.cwcParent == off);
2874 memcpy(&pwszPath[off], pAncestor->Obj.pwszName, pAncestor->Obj.cwcName * sizeof(wchar_t));
2875 }
2876 return K_TRUE;
2877 }
2878 }
2879 else
2880 {
2881 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
2882 off = pObj->cwcName;
2883 if (off + fDriveLetter < cwcPath)
2884 {
2885 memcpy(pwszPath, pObj->pwszName, off * sizeof(wchar_t));
2886 if (fDriveLetter)
2887 pwszPath[off++] = wcSlash;
2888 pwszPath[off] = '\0';
2889 return K_TRUE;
2890 }
2891 }
2892
2893 return K_FALSE;
2894}
2895
2896
2897#ifdef KFSCACHE_CFG_SHORT_NAMES
2898
2899/**
2900 * Gets the full short path to @a pObj, ANSI version.
2901 *
2902 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
2903 * @param pObj The object to get the full path to.
2904 * @param pszPath Where to return the path
2905 * @param cbPath The size of the output buffer.
2906 * @param chSlash The slash to use.
2907 */
2908KBOOL kFsCacheObjGetFullShortPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
2909{
2910 KSIZE off = pObj->cchShortParent;
2911 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2912 if (off > 0)
2913 {
2914 KSIZE offEnd = off + pObj->cchShortName;
2915 if (offEnd < cbPath)
2916 {
2917 PKFSDIR pAncestor;
2918
2919 pszPath[off + pObj->cchShortName] = '\0';
2920 memcpy(&pszPath[off], pObj->pszShortName, pObj->cchShortName);
2921
2922 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
2923 {
2924 kHlpAssert(off > 1);
2925 kHlpAssert(pAncestor != NULL);
2926 kHlpAssert(pAncestor->Obj.cchShortName > 0);
2927 pszPath[--off] = chSlash;
2928 off -= pAncestor->Obj.cchShortName;
2929 kHlpAssert(pAncestor->Obj.cchShortParent == off);
2930 memcpy(&pszPath[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
2931 }
2932 return K_TRUE;
2933 }
2934 }
2935 else
2936 {
2937 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
2938 off = pObj->cchShortName;
2939 if (off + fDriveLetter < cbPath)
2940 {
2941 memcpy(pszPath, pObj->pszShortName, off);
2942 if (fDriveLetter)
2943 pszPath[off++] = chSlash;
2944 pszPath[off] = '\0';
2945 return K_TRUE;
2946 }
2947 }
2948
2949 return K_FALSE;
2950}
2951
2952
2953/**
2954 * Gets the full short path to @a pObj, UTF-16 version.
2955 *
2956 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
2957 * @param pObj The object to get the full path to.
2958 * @param pszPath Where to return the path
2959 * @param cbPath The size of the output buffer.
2960 * @param wcSlash The slash to use.
2961 */
2962KBOOL kFsCacheObjGetFullShortPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
2963{
2964 KSIZE off = pObj->cwcShortParent;
2965 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2966 if (off > 0)
2967 {
2968 KSIZE offEnd = off + pObj->cwcShortName;
2969 if (offEnd < cwcPath)
2970 {
2971 PKFSDIR pAncestor;
2972
2973 pwszPath[off + pObj->cwcShortName] = '\0';
2974 memcpy(&pwszPath[off], pObj->pwszShortName, pObj->cwcShortName * sizeof(wchar_t));
2975
2976 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
2977 {
2978 kHlpAssert(off > 1);
2979 kHlpAssert(pAncestor != NULL);
2980 kHlpAssert(pAncestor->Obj.cwcShortName > 0);
2981 pwszPath[--off] = wcSlash;
2982 off -= pAncestor->Obj.cwcShortName;
2983 kHlpAssert(pAncestor->Obj.cwcShortParent == off);
2984 memcpy(&pwszPath[off], pAncestor->Obj.pwszShortName, pAncestor->Obj.cwcShortName * sizeof(wchar_t));
2985 }
2986 return K_TRUE;
2987 }
2988 }
2989 else
2990 {
2991 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
2992 off = pObj->cwcShortName;
2993 if (off + fDriveLetter < cwcPath)
2994 {
2995 memcpy(pwszPath, pObj->pwszShortName, off * sizeof(wchar_t));
2996 if (fDriveLetter)
2997 pwszPath[off++] = wcSlash;
2998 pwszPath[off] = '\0';
2999 return K_TRUE;
3000 }
3001 }
3002
3003 return K_FALSE;
3004}
3005
3006#endif /* KFSCACHE_CFG_SHORT_NAMES */
3007
3008
3009/**
3010 * Invalidate all cache entries of missing files.
3011 *
3012 * @param pCache The cache.
3013 */
3014void kFsCacheInvalidateMissing(PKFSCACHE pCache)
3015{
3016 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3017 pCache->uGenerationMissing++;
3018 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
3019}
3020
3021
3022/**
3023 * Invalidate all cache entries of missing files.
3024 *
3025 * @param pCache The cache.
3026 */
3027void kFsCacheInvalidateAll(PKFSCACHE pCache)
3028{
3029 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3030 pCache->uGenerationMissing++;
3031 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
3032 pCache->uGeneration++;
3033 kHlpAssert(pCache->uGeneration < KU32_MAX);
3034}
3035
3036
3037
3038PKFSCACHE kFsCacheCreate(KU32 fFlags)
3039{
3040 PKFSCACHE pCache;
3041 birdResolveImports();
3042
3043 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
3044 if (pCache)
3045 {
3046 /* Dummy root dir entry. */
3047 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC;
3048 pCache->RootDir.Obj.cRefs = 1;
3049 pCache->RootDir.Obj.uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
3050 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR;
3051 pCache->RootDir.Obj.fHaveStats = K_FALSE;
3052 pCache->RootDir.Obj.pParent = NULL;
3053 pCache->RootDir.Obj.pszName = "";
3054 pCache->RootDir.Obj.cchName = 0;
3055 pCache->RootDir.Obj.cchParent = 0;
3056#ifdef KFSCACHE_CFG_UTF16
3057 pCache->RootDir.Obj.cwcName = 0;
3058 pCache->RootDir.Obj.cwcParent = 0;
3059 pCache->RootDir.Obj.pwszName = L"";
3060#endif
3061
3062#ifdef KFSCACHE_CFG_SHORT_NAMES
3063 pCache->RootDir.Obj.pszShortName = NULL;
3064 pCache->RootDir.Obj.cchShortName = 0;
3065 pCache->RootDir.Obj.cchShortParent = 0;
3066# ifdef KFSCACHE_CFG_UTF16
3067 pCache->RootDir.Obj.cwcShortName;
3068 pCache->RootDir.Obj.cwcShortParent;
3069 pCache->RootDir.Obj.pwszShortName;
3070# endif
3071#endif
3072 pCache->RootDir.cChildren = 0;
3073 pCache->RootDir.papChildren = NULL;
3074 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
3075 pCache->RootDir.cHashTab = 251;
3076 pCache->RootDir.paHashTab = (PKFSOBJHASH)kHlpAllocZ( pCache->RootDir.cHashTab
3077 * sizeof(pCache->RootDir.paHashTab[0]));
3078 if (pCache->RootDir.paHashTab)
3079 {
3080 /* The cache itself. */
3081 pCache->u32Magic = KFSCACHE_MAGIC;
3082 pCache->fFlags = fFlags;
3083 pCache->uGeneration = KU32_MAX / 2;
3084 pCache->uGenerationMissing = 1;
3085 pCache->cObjects = 1;
3086 pCache->cbObjects = sizeof(pCache->RootDir) + pCache->RootDir.cHashTab * sizeof(pCache->RootDir.paHashTab[0]);
3087 pCache->cPathHashHits = 0;
3088 pCache->cWalkHits = 0;
3089 pCache->cAnsiPaths = 0;
3090 pCache->cAnsiPathCollisions = 0;
3091 pCache->cbAnsiPaths = 0;
3092#ifdef KFSCACHE_CFG_UTF16
3093 pCache->cUtf16Paths = 0;
3094 pCache->cUtf16PathCollisions = 0;
3095 pCache->cbUtf16Paths = 0;
3096#endif
3097 return pCache;
3098 }
3099
3100 kHlpFree(pCache);
3101 }
3102 return NULL;
3103}
3104
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