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

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

Updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 80.9 KB
Line 
1/* $Id: kFsCache.c 2855 2016-08-31 21:21:22Z 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 pchDot = (const char *)kHlpMemChr(pchDot + 1, '.', &pszPath[cchPath] - pchDot - 1);
497 else
498 {
499 char ch;
500 if ( (ch = pchDot[2]) == '\0'
501 && IS_SLASH(ch))
502 {
503 if (pchDot == pszPath)
504 return K_TRUE;
505 ch = pchDot[-1];
506 if ( IS_SLASH(ch)
507 || ch == ':')
508 return K_TRUE;
509 }
510 pchDot = (const char *)kHlpMemChr(pchDot + 2, '.', &pszPath[cchPath] - pchDot - 2);
511 }
512 }
513
514 return K_FALSE;
515}
516
517
518/**
519 * Looks for '..' in the path.
520 *
521 * @returns K_TRUE if '..' component found, K_FALSE if not.
522 * @param pwszPath The path.
523 * @param cwcPath The length of the path (in wchar_t's).
524 */
525static KBOOL kFsCacheHasDotDotW(const wchar_t *pwszPath, KSIZE cwcPath)
526{
527 const wchar_t *pwcDot = wmemchr(pwszPath, '.', cwcPath);
528 while (pwcDot)
529 {
530 if (pwcDot[1] != '.')
531 pwcDot = wmemchr(pwcDot + 1, '.', &pwszPath[cwcPath] - pwcDot - 1);
532 else
533 {
534 wchar_t wch;
535 if ( (wch = pwcDot[2]) == '\0'
536 && IS_SLASH(wch))
537 {
538 if (pwcDot == pwszPath)
539 return K_TRUE;
540 wch = pwcDot[-1];
541 if ( IS_SLASH(wch)
542 || wch == ':')
543 return K_TRUE;
544 }
545 pwcDot = wmemchr(pwcDot + 2, '.', &pwszPath[cwcPath] - pwcDot - 2);
546 }
547 }
548
549 return K_FALSE;
550}
551
552
553/**
554 * Creates an ANSI hash table entry for the given path.
555 *
556 * @returns The hash table entry or NULL if out of memory.
557 * @param pCache The hash
558 * @param pFsObj The resulting object.
559 * @param pszPath The path.
560 * @param cchPath The length of the path.
561 * @param uHashPath The hash of the path.
562 * @param idxHashTab The hash table index of the path.
563 * @param enmError The lookup error.
564 */
565static PKFSHASHA kFsCacheCreatePathHashTabEntryA(PKFSCACHE pCache, PKFSOBJ pFsObj, const char *pszPath, KU32 cchPath,
566 KU32 uHashPath, KU32 idxHashTab, KFSLOOKUPERROR enmError)
567{
568 PKFSHASHA pHashEntry = (PKFSHASHA)kHlpAlloc(sizeof(*pHashEntry) + cchPath + 1);
569 if (pHashEntry)
570 {
571 pHashEntry->uHashPath = uHashPath;
572 pHashEntry->cchPath = cchPath;
573 pHashEntry->pszPath = (const char *)kHlpMemCopy(pHashEntry + 1, pszPath, cchPath + 1);
574 pHashEntry->pFsObj = pFsObj;
575 pHashEntry->enmError = enmError;
576 if (pFsObj)
577 pHashEntry->uCacheGen = pCache->uGeneration;
578 else if (enmError != KFSLOOKUPERROR_UNSUPPORTED)
579 pHashEntry->uCacheGen = pCache->uGenerationMissing;
580 else
581 pHashEntry->uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
582
583 pHashEntry->pNext = pCache->apAnsiPaths[idxHashTab];
584 pCache->apAnsiPaths[idxHashTab] = pHashEntry;
585
586 pCache->cbAnsiPaths += sizeof(*pHashEntry) + cchPath + 1;
587 pCache->cAnsiPaths++;
588 if (pHashEntry->pNext)
589 pCache->cAnsiPathCollisions++;
590 }
591 return pHashEntry;
592}
593
594
595/**
596 * Creates an UTF-16 hash table entry for the given path.
597 *
598 * @returns The hash table entry or NULL if out of memory.
599 * @param pCache The hash
600 * @param pFsObj The resulting object.
601 * @param pwszPath The path.
602 * @param cwcPath The length of the path (in wchar_t's).
603 * @param uHashPath The hash of the path.
604 * @param idxHashTab The hash table index of the path.
605 * @param enmError The lookup error.
606 */
607static PKFSHASHW kFsCacheCreatePathHashTabEntryW(PKFSCACHE pCache, PKFSOBJ pFsObj, const wchar_t *pwszPath, KU32 cwcPath,
608 KU32 uHashPath, KU32 idxHashTab, KFSLOOKUPERROR enmError)
609{
610 PKFSHASHW pHashEntry = (PKFSHASHW)kHlpAlloc(sizeof(*pHashEntry) + (cwcPath + 1) * sizeof(wchar_t));
611 if (pHashEntry)
612 {
613 pHashEntry->uHashPath = uHashPath;
614 pHashEntry->cwcPath = cwcPath;
615 pHashEntry->pwszPath = (const wchar_t *)kHlpMemCopy(pHashEntry + 1, pwszPath, (cwcPath + 1) * sizeof(wchar_t));
616 pHashEntry->pFsObj = pFsObj;
617 pHashEntry->enmError = enmError;
618 if (pFsObj)
619 pHashEntry->uCacheGen = pCache->uGeneration;
620 else if (enmError != KFSLOOKUPERROR_UNSUPPORTED)
621 pHashEntry->uCacheGen = pCache->uGenerationMissing;
622 else
623 pHashEntry->uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
624
625 pHashEntry->pNext = pCache->apUtf16Paths[idxHashTab];
626 pCache->apUtf16Paths[idxHashTab] = pHashEntry;
627
628 pCache->cbUtf16Paths += sizeof(*pHashEntry) + (cwcPath + 1) * sizeof(wchar_t);
629 pCache->cUtf16Paths++;
630 if (pHashEntry->pNext)
631 pCache->cAnsiPathCollisions++;
632 }
633 return pHashEntry;
634}
635
636
637/**
638 * Links the child in under the parent.
639 *
640 * @returns K_TRUE on success, K_FALSE if out of memory.
641 * @param pParent The parent node.
642 * @param pChild The child node.
643 */
644static KBOOL kFsCacheDirAddChild(PKFSCACHE pCache, PKFSDIR pParent, PKFSOBJ pChild, KFSLOOKUPERROR *penmError)
645{
646 if ((pParent->cChildren % 16) == 0)
647 {
648 void *pvNew = kHlpRealloc(pParent->papChildren, (pParent->cChildren + 16) * sizeof(pParent->papChildren[0]));
649 if (!pvNew)
650 return K_FALSE;
651 pParent->papChildren = (PKFSOBJ *)pvNew;
652 pCache->cbObjects += 16 * sizeof(pParent->papChildren[0]);
653 }
654 pParent->papChildren[pParent->cChildren++] = pChild;
655 return K_TRUE;
656}
657
658
659/**
660 * Creates a new cache object.
661 *
662 * @returns Pointer (with 1 reference) to the new object. The object will not
663 * be linked to the parent directory yet.
664 *
665 * NULL if we're out of memory.
666 *
667 * @param pCache The cache.
668 * @param pParent The parent directory.
669 * @param pszName The ANSI name.
670 * @param cchName The length of the ANSI name.
671 * @param pwszName The UTF-16 name.
672 * @param cwcName The length of the UTF-16 name.
673 * @param pszShortName The ANSI short name, NULL if none.
674 * @param cchShortName The length of the ANSI short name, 0 if none.
675 * @param pwszShortName The UTF-16 short name, NULL if none.
676 * @param cwcShortName The length of the UTF-16 short name, 0 if none.
677 * @param bObjType The objct type.
678 * @param penmError Where to explain failures.
679 */
680PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,
681 char const *pszName, KU16 cchName, wchar_t const *pwszName, KU16 cwcName,
682#ifdef KFSCACHE_CFG_SHORT_NAMES
683 char const *pszShortName, KU16 cchShortName, wchar_t const *pwszShortName, KU16 cwcShortName,
684#endif
685 KU8 bObjType, KFSLOOKUPERROR *penmError)
686{
687 /*
688 * Allocate the object.
689 */
690 KBOOL const fDirish = bObjType != KFSOBJ_TYPE_FILE && bObjType == KFSOBJ_TYPE_OTHER;
691 KSIZE const cbObj = (fDirish ? sizeof(KFSDIR) : sizeof(KFSOBJ))
692 + (cwcName + 1) * sizeof(wchar_t) + cchName + 1
693#ifdef KFSCACHE_CFG_SHORT_NAMES
694 + (cwcShortName > 0 ? (cwcShortName + 1) * sizeof(wchar_t) + cchShortName + 1 : 0)
695#endif
696 ;
697 PKFSOBJ pObj;
698 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
699
700 pObj = (PKFSOBJ)kHlpAlloc(cbObj);
701 if (pObj)
702 {
703 KU8 *pbExtra = (KU8 *)(pObj + 1);
704
705 pCache->cbObjects += cbObj;
706 pCache->cObjects++;
707
708 /*
709 * Initialize the object.
710 */
711 pObj->u32Magic = KFSOBJ_MAGIC;
712 pObj->cRefs = 1;
713 pObj->uCacheGen = bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing;
714 pObj->bObjType = bObjType;
715 pObj->fHaveStats = K_FALSE;
716 pObj->abUnused[0] = K_FALSE;
717 pObj->abUnused[1] = K_FALSE;
718 pObj->fFlags = pParent->Obj.fFlags;
719 pObj->pParent = pParent;
720 pObj->pUserDataHead = NULL;
721
722#ifdef KFSCACHE_CFG_UTF16
723 pObj->cwcParent = pParent->Obj.cwcParent + pParent->Obj.cwcName + !!pParent->Obj.cwcName;
724 pObj->pwszName = (wchar_t *)kHlpMemCopy(pbExtra, pwszName, cwcName * sizeof(wchar_t));
725 pbExtra += cwcName * sizeof(wchar_t);
726 *pbExtra++ = '\0';
727 *pbExtra++ = '\0';
728# ifdef KFSCACHE_CFG_SHORT_NAMES
729 pObj->cwcShortParent = pParent->Obj.cwcShortParent + pParent->Obj.cwcShortName + !!pParent->Obj.cwcShortName;
730 if (cwcShortName)
731 {
732 pObj->pwszShortName = (wchar_t *)kHlpMemCopy(pbExtra, pwszShortName, cwcShortName * sizeof(wchar_t));
733 pbExtra += cwcShortName * sizeof(wchar_t);
734 *pbExtra++ = '\0';
735 *pbExtra++ = '\0';
736 }
737 else
738 {
739 pObj->pwszShortName = pObj->pwszName;
740 pObj->cwcShortName = pObj->cwcName;
741 }
742# endif
743#endif
744 pObj->cchParent = pParent->Obj.cchParent + pParent->Obj.cchName + !!pParent->Obj.cchName;
745 pObj->pszName = (char *)kHlpMemCopy(pbExtra, pszName, cchName);
746 pbExtra += cchName;
747 *pbExtra++ = '\0';
748# ifdef KFSCACHE_CFG_SHORT_NAMES
749 pObj->cchShortParent = pParent->Obj.cchShortParent + pParent->Obj.cchShortName + !!pParent->Obj.cchShortName;
750 if (cchShortName)
751 {
752 pObj->pszShortName = (char *)kHlpMemCopy(pbExtra, pszShortName, cchShortName);
753 pbExtra += cchShortName;
754 *pbExtra++ = '\0';
755 }
756 else
757 {
758 pObj->pszShortName = pObj->pszName;
759 pObj->cchShortName = pObj->cchName;
760 }
761#endif
762 kHlpAssert(pbExtra - (KU8 *)pObj == cbObj);
763
764 /*
765 * Type specific initilization.
766 */
767 if (fDirish)
768 {
769 PKFSDIR pDirObj = (PKFSDIR)pObj;
770 pDirObj->cChildren = 0;
771 pDirObj->papChildren = NULL;
772 pDirObj->cHashTab = 0;
773 pDirObj->paHashTab = NULL;
774 pDirObj->hDir = INVALID_HANDLE_VALUE;
775 pDirObj->uDevNo = pParent->uDevNo;
776 pDirObj->fPopulated = K_FALSE;
777 }
778 }
779 else
780 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
781 return pObj;
782}
783
784
785/**
786 * Creates a new object given wide char names.
787 *
788 * This function just converts the paths and calls kFsCacheCreateObject.
789 *
790 *
791 * @returns Pointer (with 1 reference) to the new object. The object will not
792 * be linked to the parent directory yet.
793 *
794 * NULL if we're out of memory.
795 *
796 * @param pCache The cache.
797 * @param pParent The parent directory.
798 * @param pszName The ANSI name.
799 * @param cchName The length of the ANSI name.
800 * @param pwszName The UTF-16 name.
801 * @param cwcName The length of the UTF-16 name.
802 * @param pwszShortName The UTF-16 short name, NULL if none.
803 * @param cwcShortName The length of the UTF-16 short name, 0 if none.
804 * @param bObjType The objct type.
805 * @param penmError Where to explain failures.
806 */
807PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName,
808#ifdef KFSCACHE_CFG_SHORT_NAMES
809 wchar_t const *pwszShortName, KU32 cwcShortName,
810#endif
811 KU8 bObjType, KFSLOOKUPERROR *penmError)
812{
813 /* Convert names to ANSI first so we know their lengths. */
814 char szName[KFSCACHE_CFG_MAX_ANSI_NAME];
815 int cchName = WideCharToMultiByte(CP_ACP, 0, pwszName, cwcName, szName, sizeof(szName) - 1, NULL, NULL);
816 if (cchName >= 0)
817 {
818#ifdef KFSCACHE_CFG_SHORT_NAMES
819 char szShortName[12*3 + 1];
820 int cchShortName = 0;
821 if ( cwcShortName == 0
822 || (cchShortName = WideCharToMultiByte(CP_ACP, 0, pwszShortName, cwcShortName,
823 szShortName, sizeof(szShortName) - 1, NULL, NULL)) > 0)
824#endif
825 {
826 return kFsCacheCreateObject(pCache, pParent,
827 szName, cchName, pwszName, cwcName,
828#ifdef KFSCACHE_CFG_SHORT_NAMES
829 szShortName, cchShortName, pwszShortName, cwcShortName,
830#endif
831 bObjType, penmError);
832 }
833 }
834 *penmError = KFSLOOKUPERROR_ANSI_CONVERSION_ERROR;
835 return NULL;
836}
837
838
839/**
840 * Creates a missing object.
841 *
842 * This is used for caching negative results.
843 *
844 * @returns Pointer to the newly created object on success (already linked into
845 * pParent). No reference.
846 *
847 * NULL on failure.
848 *
849 * @param pCache The cache.
850 * @param pParent The parent directory.
851 * @param pchName The name.
852 * @param cchName The length of the name.
853 * @param penmError Where to return failure explanations.
854 */
855static PKFSOBJ kFsCacheCreateMissingA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName,
856 KFSLOOKUPERROR *penmError)
857{
858 /*
859 * Just convert the name to UTF-16 and call kFsCacheCreateObject to do the job.
860 */
861 wchar_t wszName[KFSCACHE_CFG_MAX_PATH];
862 int cwcName = MultiByteToWideChar(CP_ACP, 0, pchName, cchName, wszName, KFSCACHE_CFG_MAX_UTF16_NAME - 1);
863 if (cwcName > 0)
864 {
865 /** @todo check that it actually doesn't exists before we add it. We should not
866 * trust the directory enumeration here, or maybe we should?? */
867
868 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, pParent, pchName, cchName, wszName, cwcName,
869#ifdef KFSCACHE_CFG_SHORT_NAMES
870 NULL, 0, NULL, 0,
871#endif
872 KFSOBJ_TYPE_MISSING, penmError);
873 if (pMissing)
874 {
875 KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError);
876 kFsCacheObjRelease(pCache, pMissing);
877 return fRc ? pMissing : NULL;
878 }
879 return NULL;
880 }
881 *penmError = KFSLOOKUPERROR_UTF16_CONVERSION_ERROR;
882 return NULL;
883}
884
885
886/**
887 * Creates a missing object, UTF-16 version.
888 *
889 * This is used for caching negative results.
890 *
891 * @returns Pointer to the newly created object on success (already linked into
892 * pParent). No reference.
893 *
894 * NULL on failure.
895 *
896 * @param pCache The cache.
897 * @param pParent The parent directory.
898 * @param pwcName The name.
899 * @param cwcName The length of the name.
900 * @param penmError Where to return failure explanations.
901 */
902static PKFSOBJ kFsCacheCreateMissingW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName,
903 KFSLOOKUPERROR *penmError)
904{
905 /** @todo check that it actually doesn't exists before we add it. We should not
906 * trust the directory enumeration here, or maybe we should?? */
907 PKFSOBJ pMissing = kFsCacheCreateObjectW(pCache, pParent, pwcName, cwcName,
908#ifdef KFSCACHE_CFG_SHORT_NAMES
909 NULL, 0,
910#endif
911 KFSOBJ_TYPE_MISSING, penmError);
912 if (pMissing)
913 {
914 KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError);
915 kFsCacheObjRelease(pCache, pMissing);
916 return fRc ? pMissing : NULL;
917 }
918 return NULL;
919}
920
921
922/**
923 * Does the initial directory populating or refreshes it if it has been
924 * invalidated.
925 *
926 * This assumes the parent directory is opened.
927 *
928 * @returns K_TRUE on success, K_FALSE on error.
929 * @param pCache The cache.
930 * @param pDir The directory.
931 * @param penmError Where to store K_FALSE explanation.
932 */
933static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
934{
935 KBOOL fRefreshing = K_FALSE;
936 /** @todo will have to make this more flexible wrt information classes since
937 * older windows versions (XP, w2K) might not correctly support the
938 * ones with file ID on all file systems. */
939#ifdef KFSCACHE_CFG_SHORT_NAMES
940 MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdBothDirectoryInformation;
941 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
942#else
943 MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdFullDirectoryInformation;
944 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
945#endif
946 MY_NTSTATUS rcNt;
947 MY_IO_STATUS_BLOCK Ios;
948 union
949 {
950 /* Include the structures for better alignment. */
951 MY_FILE_ID_BOTH_DIR_INFORMATION WithId;
952 MY_FILE_ID_FULL_DIR_INFORMATION NoId;
953 /* Buffer padding. We're using a 56KB buffer here to avoid size troubles with CIFS and such. */
954 KU8 abBuf[56*1024];
955 } uBuf;
956
957 /*
958 * Open the directory.
959 */
960 if (pDir->hDir == INVALID_HANDLE_VALUE)
961 {
962 MY_OBJECT_ATTRIBUTES ObjAttr;
963 MY_UNICODE_STRING UniStr;
964
965 kHlpAssert(!pDir->fPopulated);
966
967 Ios.Information = -1;
968 Ios.u.Status = -1;
969
970 UniStr.Buffer = (wchar_t *)pDir->Obj.pwszName;
971 UniStr.Length = (USHORT)(pDir->Obj.cwcName * sizeof(wchar_t));
972 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
973
974 kHlpAssertStmtReturn(pDir->Obj.pParent, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
975 kHlpAssertStmtReturn(pDir->Obj.pParent->hDir != INVALID_HANDLE_VALUE, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
976 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pDir->Obj.pParent->hDir, NULL /*pSecAttr*/);
977
978 /** @todo FILE_OPEN_REPARSE_POINT? */
979 rcNt = g_pfnNtCreateFile(&pDir->hDir,
980 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
981 &ObjAttr,
982 &Ios,
983 NULL, /*cbFileInitialAlloc */
984 FILE_ATTRIBUTE_NORMAL,
985 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
986 FILE_OPEN,
987 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
988 NULL, /*pEaBuffer*/
989 0); /*cbEaBuffer*/
990 if (MY_NT_SUCCESS(rcNt))
991 { /* likely */ }
992 else
993 {
994 pDir->hDir = INVALID_HANDLE_VALUE;
995 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR;
996 return K_FALSE;
997 }
998 }
999 else if (pDir->fPopulated)
1000 {
1001 /** @todo refreshing directories. */
1002 __debugbreak();
1003 fRefreshing = K_TRUE;
1004 }
1005
1006
1007 /*
1008 * Enumerate the directory content.
1009 */
1010 Ios.Information = -1;
1011 Ios.u.Status = -1;
1012 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1013 NULL, /* hEvent */
1014 NULL, /* pfnApcComplete */
1015 NULL, /* pvApcCompleteCtx */
1016 &Ios,
1017 &uBuf,
1018 sizeof(uBuf),
1019 enmInfoClass,
1020 FALSE, /* fReturnSingleEntry */
1021 NULL, /* Filter / restart pos. */
1022 TRUE); /* fRestartScan */
1023 while (MY_NT_SUCCESS(rcNt))
1024 {
1025 /*
1026 * Process the entries in the buffer.
1027 */
1028 KSIZE offBuf = 0;
1029 for (;;)
1030 {
1031 union
1032 {
1033 KU8 *pb;
1034#ifdef KFSCACHE_CFG_SHORT_NAMES
1035 MY_FILE_ID_BOTH_DIR_INFORMATION *pWithId;
1036 MY_FILE_BOTH_DIR_INFORMATION *pNoId;
1037#else
1038 MY_FILE_ID_FULL_DIR_INFORMATION *pWithId;
1039 MY_FILE_FULL_DIR_INFORMATION *pNoId;
1040#endif
1041 } uPtr;
1042 PKFSOBJ pCur;
1043 KU32 offNext;
1044 KU32 cbMinCur;
1045 wchar_t *pwszFilename;
1046
1047 /* ASSUME only the FileName member differs between the two structures. */
1048 uPtr.pb = &uBuf.abBuf[offBuf];
1049 if (enmInfoClass == enmInfoClassWithId)
1050 {
1051 pwszFilename = &uPtr.pWithId->FileName[0];
1052 cbMinCur = (KU32)((uintptr_t)&uPtr.pWithId->FileName[0] - (uintptr_t)uPtr.pWithId);
1053 cbMinCur += uPtr.pNoId->FileNameLength;
1054 }
1055 else
1056 {
1057 pwszFilename = &uPtr.pNoId->FileName[0];
1058 cbMinCur = (KU32)((uintptr_t)&uPtr.pNoId->FileName[0] - (uintptr_t)uPtr.pNoId);
1059 cbMinCur += uPtr.pNoId->FileNameLength;
1060 }
1061
1062 /*
1063 * Create the entry (not linked yet).
1064 */
1065 pCur = kFsCacheCreateObjectW(pCache, pDir, pwszFilename, uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1066#ifdef KFSCACHE_CFG_SHORT_NAMES
1067 uPtr.pNoId->ShortName, uPtr.pNoId->ShortNameLength / sizeof(wchar_t),
1068#endif
1069 uPtr.pNoId->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR
1070 : uPtr.pNoId->FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)
1071 ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE,
1072 penmError);
1073 if (!pCur)
1074 return K_FALSE;
1075 kHlpAssert(pCur->cRefs == 1);
1076
1077#ifdef KFSCACHE_CFG_SHORT_NAMES
1078 if (enmInfoClass == enmInfoClassWithId)
1079 birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
1080 else
1081 birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
1082#else
1083 if (enmInfoClass == enmInfoClassWithId)
1084 birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
1085 else
1086 birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
1087#endif
1088 pCur->Stats.st_dev = pDir->uDevNo;
1089
1090 /*
1091 * If we're updating we have to check the data.
1092 */
1093 if (fRefreshing)
1094 {
1095 __debugbreak();
1096 }
1097
1098 /*
1099 * If we've still got pCur, add it to the directory.
1100 */
1101 if (pCur)
1102 {
1103 KBOOL fRc = kFsCacheDirAddChild(pCache, pDir, pCur, penmError);
1104 kFsCacheObjRelease(pCache, pCur);
1105 if (fRc)
1106 { /* likely */ }
1107 else
1108 return K_FALSE;
1109 }
1110
1111 /*
1112 * Advance.
1113 */
1114 offNext = uPtr.pNoId->NextEntryOffset;
1115 if ( offNext >= cbMinCur
1116 && offNext < sizeof(uBuf))
1117 offBuf += offNext;
1118 else
1119 break;
1120 }
1121
1122 /*
1123 * Read the next chunk.
1124 */
1125 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1126 NULL, /* hEvent */
1127 NULL, /* pfnApcComplete */
1128 NULL, /* pvApcCompleteCtx */
1129 &Ios,
1130 &uBuf,
1131 sizeof(uBuf),
1132 enmInfoClass,
1133 FALSE, /* fReturnSingleEntry */
1134 NULL, /* Filter / restart pos. */
1135 FALSE); /* fRestartScan */
1136 }
1137
1138 if (rcNt == MY_STATUS_NO_MORE_FILES)
1139 return K_TRUE;
1140 kHlpAssertMsgFailed(("%#x\n", rcNt));
1141 *penmError = KFSLOOKUPERROR_DIR_READ_ERROR;
1142 return K_TRUE;
1143}
1144
1145
1146static KBOOL kFsCacheRefreshMissing(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1147{
1148 return K_TRUE;
1149}
1150
1151
1152static KBOOL kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1153{
1154 return K_TRUE;
1155}
1156
1157
1158static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError)
1159{
1160 return K_FALSE;
1161}
1162
1163
1164
1165/**
1166 * Looks up a drive letter.
1167 *
1168 * Will enter the drive if necessary.
1169 *
1170 * @returns Pointer to the root directory of the drive or an update-to-date
1171 * missing node.
1172 * @param pCache The cache.
1173 * @param chLetter The uppercased drive letter.
1174 * @param penmError Where to return details as to why the lookup
1175 * failed.
1176 */
1177static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError)
1178{
1179 KU32 const uHash = chLetter - 'A';
1180 KU32 cLeft;
1181 PKFSOBJ *ppCur;
1182
1183 MY_UNICODE_STRING NtPath;
1184 wchar_t wszTmp[8];
1185 MY_NTSTATUS rcNt;
1186 char szTmp[4];
1187
1188 /*
1189 * Custom drive letter hashing.
1190 */
1191 if (pCache->RootDir.paHashTab)
1192 {
1193 /** @todo PKFSOBJHASH pHash = */
1194 }
1195
1196 /*
1197 * Special cased lookup.
1198 */
1199 cLeft = pCache->RootDir.cChildren;
1200 ppCur = pCache->RootDir.papChildren;
1201 while (cLeft-- > 0)
1202 {
1203 PKFSOBJ pCur = *ppCur++;
1204 if ( pCur->cchName == 2
1205 && pCur->pszName[0] == chLetter
1206 && pCur->pszName[1] == ':')
1207 {
1208 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
1209 return pCur;
1210 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
1211 if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
1212 return pCur;
1213 return NULL;
1214 }
1215 }
1216
1217 /*
1218 * Need to add it. We always keep the drive letters open for the benefit
1219 * of kFsCachePopuplateOrRefreshDir and others.
1220 */
1221 wszTmp[0] = szTmp[0] = chLetter;
1222 wszTmp[1] = szTmp[1] = ':';
1223 wszTmp[2] = szTmp[2] = '\\';
1224 wszTmp[3] = '.';
1225 wszTmp[4] = '\0';
1226 szTmp[2] = '\0';
1227
1228 NtPath.Buffer = NULL;
1229 NtPath.Length = 0;
1230 NtPath.MaximumLength = 0;
1231 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
1232 {
1233 HANDLE hDir;
1234 rcNt = birdOpenFileUniStr(&NtPath,
1235 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1236 FILE_ATTRIBUTE_NORMAL,
1237 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1238 FILE_OPEN,
1239 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1240 OBJ_CASE_INSENSITIVE,
1241 &hDir);
1242 birdFreeNtPath(&NtPath);
1243 if (MY_NT_SUCCESS(rcNt))
1244 {
1245 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1246#ifdef KFSCACHE_CFG_SHORT_NAMES
1247 NULL, 0, NULL, 0,
1248#endif
1249 KFSOBJ_TYPE_DIR, penmError);
1250 if (pDir)
1251 {
1252 /*
1253 * We need a little bit of extra info for a drive root. These things are typically
1254 * inherited by subdirectories down the tree, so, we do it all here for till that changes.
1255 */
1256 union
1257 {
1258 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
1259 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
1260 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
1261 } uBuf;
1262 MY_IO_STATUS_BLOCK Ios;
1263 KBOOL fRc;
1264
1265 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
1266 pDir->hDir = hDir;
1267
1268 /* Get the device number. */
1269 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
1270 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
1271
1272 /* Get the file system. */
1273 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
1274 Ios.Information = -1;
1275 Ios.u.Status = -1;
1276 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
1277 MyFileFsAttributeInformation);
1278 if (MY_NT_SUCCESS(rcNt))
1279 rcNt = Ios.u.Status;
1280 if (MY_NT_SUCCESS(rcNt))
1281 {
1282 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N'
1283 && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
1284 && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
1285 && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
1286 && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
1287 {
1288 DWORD dwDriveType = GetDriveTypeW(wszTmp);
1289 if ( dwDriveType == DRIVE_FIXED
1290 || dwDriveType == DRIVE_RAMDISK)
1291 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
1292 }
1293 }
1294
1295 /*
1296 * Link the new drive letter into the root dir.
1297 */
1298 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
1299 kFsCacheObjRelease(pCache, &pDir->Obj);
1300 return fRc ? &pDir->Obj : NULL;
1301 }
1302
1303 g_pfnNtClose(hDir);
1304 return NULL;
1305 }
1306
1307 /* Assume it doesn't exist if this happens... This may be a little to
1308 restrictive wrt status code checks. */
1309 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
1310 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
1311 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
1312 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
1313 ("%#x\n", rcNt),
1314 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
1315 NULL);
1316 }
1317 else
1318 {
1319 kHlpAssertFailed();
1320 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
1321 return NULL;
1322 }
1323
1324 /*
1325 * Maybe create a missing entry.
1326 */
1327 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1328 {
1329 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1330#ifdef KFSCACHE_CFG_SHORT_NAMES
1331 NULL, 0, NULL, 0,
1332#endif
1333 KFSOBJ_TYPE_MISSING, penmError);
1334 if (pMissing)
1335 {
1336 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
1337 kFsCacheObjRelease(pCache, pMissing);
1338 return fRc ? pMissing : NULL;
1339 }
1340 }
1341 else
1342 {
1343 /** @todo this isn't necessary correct for a root spec. */
1344 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1345 }
1346 return NULL;
1347}
1348
1349
1350/**
1351 * Look up a child node, ANSI version.
1352 *
1353 * @returns Pointer to the child if found, NULL if not.
1354 * @param pCache The cache.
1355 * @param pParent The parent directory to search.
1356 * @param pchName The child name to search for (not terminated).
1357 * @param cchName The length of the child name.
1358 */
1359static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
1360{
1361 /* Check for '.' first. */
1362 if (cchName != 1 || *pchName != '.')
1363 {
1364 KU32 cLeft;
1365 PKFSOBJ *ppCur;
1366
1367 if (pParent->paHashTab != NULL)
1368 {
1369 /** @todo directory hash table lookup. */
1370 }
1371
1372 /* Linear search. */
1373 cLeft = pParent->cChildren;
1374 ppCur = pParent->papChildren;
1375 while (cLeft-- > 0)
1376 {
1377 PKFSOBJ pCur = *ppCur++;
1378 if ( ( pCur->cchName == cchName
1379 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
1380#ifdef KFSCACHE_CFG_SHORT_NAMES
1381 || ( pCur->cchShortName == cchName
1382 && pCur->pszShortName != pCur->pszName
1383 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
1384#endif
1385 )
1386 return pCur;
1387 }
1388 return NULL;
1389 }
1390 return &pParent->Obj;
1391}
1392
1393
1394/**
1395 * For use when kFsCacheIAreEqualW hit's something non-trivial.
1396 *
1397 * @returns K_TRUE if equal, K_FALSE if different.
1398 * @param pwcName1 The first string.
1399 * @param pwcName2 The second string.
1400 * @param cwcName The length of the two strings (in wchar_t's).
1401 */
1402KBOOL kFsCacheIAreEqualSlowW(const wchar_t *pwcName1, const wchar_t *pwcName2, KU16 cwcName)
1403{
1404 MY_UNICODE_STRING UniStr1 = { cwcName * sizeof(wchar_t), cwcName * sizeof(wchar_t), (wchar_t *)pwcName1 };
1405 MY_UNICODE_STRING UniStr2 = { cwcName * sizeof(wchar_t), cwcName * sizeof(wchar_t), (wchar_t *)pwcName2 };
1406 return g_pfnRtlEqualUnicodeString(&UniStr1, &UniStr2, TRUE /*fCaseInsensitive*/);
1407}
1408
1409
1410/**
1411 * Compares two UTF-16 strings in a case-insensitive fashion.
1412 *
1413 * You would think we should be using _wscnicmp here instead, however it is
1414 * locale dependent and defaults to ASCII upper/lower handling setlocale hasn't
1415 * been called.
1416 *
1417 * @returns K_TRUE if equal, K_FALSE if different.
1418 * @param pwcName1 The first string.
1419 * @param pwcName2 The second string.
1420 * @param cwcName The length of the two strings (in wchar_t's).
1421 */
1422K_INLINE KBOOL kFsCacheIAreEqualW(const wchar_t *pwcName1, const wchar_t *pwcName2, KU32 cwcName)
1423{
1424 while (cwcName > 0)
1425 {
1426 wchar_t wc1 = *pwcName1;
1427 wchar_t wc2 = *pwcName2;
1428 if (wc1 == wc2)
1429 { /* not unlikely */ }
1430 else if ( (KU16)wc1 < (KU16)0xc0 /* U+00C0 is the first upper/lower letter after 'z'. */
1431 && (KU16)wc2 < (KU16)0xc0)
1432 {
1433 /* ASCII upper case. */
1434 if ((KU16)wc1 - (KU16)0x61 < (KU16)26)
1435 wc1 &= ~(wchar_t)0x20;
1436 if ((KU16)wc2 - (KU16)0x61 < (KU16)26)
1437 wc2 &= ~(wchar_t)0x20;
1438 if (wc1 != wc2)
1439 return K_FALSE;
1440 }
1441 else
1442 return kFsCacheIAreEqualSlowW(pwcName1, pwcName2, (KU16)cwcName);
1443
1444 pwcName2++;
1445 pwcName1++;
1446 cwcName--;
1447 }
1448
1449 return K_TRUE;
1450}
1451
1452
1453/**
1454 * Look up a child node, UTF-16 version.
1455 *
1456 * @returns Pointer to the child if found, NULL if not.
1457 * @param pCache The cache.
1458 * @param pParent The parent directory to search.
1459 * @param pwcName The child name to search for (not terminated).
1460 * @param cwcName The length of the child name (in wchar_t's).
1461 */
1462static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
1463{
1464 /* Check for '.' first. */
1465 if (cwcName != 1 || *pwcName != '.')
1466 {
1467 KU32 cLeft;
1468 PKFSOBJ *ppCur;
1469
1470 if (pParent->paHashTab != NULL)
1471 {
1472 /** @todo directory hash table lookup. */
1473 }
1474
1475 /* Linear search. */
1476 cLeft = pParent->cChildren;
1477 ppCur = pParent->papChildren;
1478 while (cLeft-- > 0)
1479 {
1480 PKFSOBJ pCur = *ppCur++;
1481 if ( ( pCur->cwcName == cwcName
1482 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
1483#ifdef KFSCACHE_CFG_SHORT_NAMES
1484 || ( pCur->cwcShortName == cwcName
1485 && pCur->pwszShortName != pCur->pwszName
1486 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
1487#endif
1488 )
1489 return pCur;
1490 }
1491 return NULL;
1492 }
1493 return &pParent->Obj;
1494}
1495
1496
1497/**
1498 * Looks up a UNC share, ANSI version.
1499 *
1500 * We keep both the server and share in the root directory entry. This means we
1501 * have to clean up the entry name before we can insert it.
1502 *
1503 * @returns Pointer to the share root directory or an update-to-date missing
1504 * node.
1505 * @param pCache The cache.
1506 * @param pszPath The path.
1507 * @param poff Where to return the root dire.
1508 * @param penmError Where to return details as to why the lookup
1509 * failed.
1510 */
1511static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
1512{
1513#if 0 /* later */
1514 KU32 offStartServer;
1515 KU32 offEndServer;
1516 KU32 offStartShare;
1517
1518 KU32 offEnd = 2;
1519 while (IS_SLASH(pszPath[offEnd]))
1520 offEnd++;
1521
1522 offStartServer = offEnd;
1523 while ( (ch = pszPath[offEnd]) != '\0'
1524 && !IS_SLASH(ch))
1525 offEnd++;
1526 offEndServer = offEnd;
1527
1528 if (ch != '\0')
1529 { /* likely */ }
1530 else
1531 {
1532 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1533 return NULL;
1534 }
1535
1536 while (IS_SLASH(pszPath[offEnd]))
1537 offEnd++;
1538 offStartServer = offEnd;
1539 while ( (ch = pszPath[offEnd]) != '\0'
1540 && !IS_SLASH(ch))
1541 offEnd++;
1542#endif
1543 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
1544 return NULL;
1545}
1546
1547
1548/**
1549 * Looks up a UNC share, UTF-16 version.
1550 *
1551 * We keep both the server and share in the root directory entry. This means we
1552 * have to clean up the entry name before we can insert it.
1553 *
1554 * @returns Pointer to the share root directory or an update-to-date missing
1555 * node.
1556 * @param pCache The cache.
1557 * @param pwszPath The path.
1558 * @param poff Where to return the root dire.
1559 * @param penmError Where to return details as to why the lookup
1560 * failed.
1561 */
1562static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
1563{
1564#if 0 /* later */
1565 KU32 offStartServer;
1566 KU32 offEndServer;
1567 KU32 offStartShare;
1568
1569 KU32 offEnd = 2;
1570 while (IS_SLASH(pwszPath[offEnd]))
1571 offEnd++;
1572
1573 offStartServer = offEnd;
1574 while ( (ch = pwszPath[offEnd]) != '\0'
1575 && !IS_SLASH(ch))
1576 offEnd++;
1577 offEndServer = offEnd;
1578
1579 if (ch != '\0')
1580 { /* likely */ }
1581 else
1582 {
1583 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1584 return NULL;
1585 }
1586
1587 while (IS_SLASH(pwszPath[offEnd]))
1588 offEnd++;
1589 offStartServer = offEnd;
1590 while ( (ch = pwszPath[offEnd]) != '\0'
1591 && !IS_SLASH(ch))
1592 offEnd++;
1593#endif
1594 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
1595 return NULL;
1596}
1597
1598
1599/**
1600 * Walk the file system tree for the given absolute path, entering it into the
1601 * hash table.
1602 *
1603 * This will create any missing nodes while walking.
1604 *
1605 * The caller will have to do the path hash table insertion of the result.
1606 *
1607 * @returns Pointer to the tree node corresponding to @a pszPath.
1608 * NULL on lookup failure, see @a penmError for details.
1609 * @param pCache The cache.
1610 * @param pszPath The path to walk.
1611 * @param cchPath The length of the path.
1612 * @param penmError Where to return details as to why the lookup
1613 * failed.
1614 */
1615static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
1616{
1617 PKFSDIR pParent = &pCache->RootDir;
1618 PKFSOBJ pChild;
1619 KU32 off;
1620 KU32 cchSlashes;
1621 KU32 offEnd;
1622
1623 KFSCACHE_LOG(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
1624
1625 /*
1626 * The root "directory" needs special handling, so we keep it outside the
1627 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
1628 */
1629 cchSlashes = 0;
1630 off = 0;
1631 if ( pszPath[1] == ':'
1632 && IS_ALPHA(pszPath[0]))
1633 {
1634 /* Drive letter. */
1635 offEnd = 2;
1636 kHlpAssert(IS_SLASH(pszPath[2]));
1637 pChild = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);
1638 }
1639 else if ( IS_SLASH(pszPath[0])
1640 && IS_SLASH(pszPath[1]) )
1641 pChild = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);
1642 else
1643 {
1644 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
1645 return NULL;
1646 }
1647 if (pChild)
1648 { /* likely */ }
1649 else
1650 return NULL;
1651
1652 /* Count slashes trailing the root spec. */
1653 if (offEnd < cchPath)
1654 {
1655 kHlpAssert(IS_SLASH(pszPath[offEnd]));
1656 do
1657 cchSlashes++;
1658 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
1659 }
1660
1661 /* Done already? */
1662 if (offEnd >= cchPath)
1663 {
1664 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1665 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
1666 || kFsCacheRefreshObj(pCache, pChild, penmError))
1667 return kFsCacheObjRetainInternal(pChild);
1668 return NULL;
1669 }
1670
1671 /* Check that we've got a valid result and not a cached negative one. */
1672 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
1673 { /* likely */ }
1674 else
1675 {
1676 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
1677 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
1678 return pChild;
1679 }
1680
1681 /* Next component. */
1682 pParent = (PKFSDIR)pChild;
1683 off = offEnd + cchSlashes;
1684
1685
1686 /*
1687 * Walk loop.
1688 */
1689 for (;;)
1690 {
1691 /*
1692 * Find the end of the component, counting trailing slashes.
1693 */
1694 char ch;
1695 cchSlashes = 0;
1696 offEnd = off + 1;
1697 while ((ch = pszPath[offEnd]) != '\0')
1698 {
1699 if (!IS_SLASH(ch))
1700 offEnd++;
1701 else
1702 {
1703 do
1704 cchSlashes++;
1705 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
1706 break;
1707 }
1708 }
1709
1710 /*
1711 * Do we need to populate or refresh this directory first?
1712 */
1713 if ( pParent->fPopulated
1714 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1715 || pParent->Obj.uCacheGen == pCache->uGeneration) )
1716 { /* likely */ }
1717 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
1718 { /* likely */ }
1719 else
1720 return NULL;
1721
1722 /*
1723 * Search the current node for the name.
1724 *
1725 * If we don't find it, we may insert a missing node depending on
1726 * the cache configuration.
1727 */
1728 pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
1729 if (pChild != NULL)
1730 { /* probably likely */ }
1731 else
1732 {
1733 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1734 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
1735 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
1736 {
1737 if (pChild)
1738 return pChild;
1739 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1740 }
1741 else
1742 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1743 return NULL;
1744 }
1745
1746 /* Advance off and check if we're done already. */
1747 off = offEnd + cchSlashes;
1748 if ( cchSlashes == 0
1749 || off >= cchPath)
1750 {
1751 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
1752 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1753 || pChild->uCacheGen == pCache->uGenerationMissing
1754 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
1755 { /* likely */ }
1756 else
1757 return NULL;
1758 return pChild;
1759 }
1760
1761 /*
1762 * Check that it's a directory. If a missing entry, we may have to
1763 * refresh it and re-examin it.
1764 */
1765 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
1766 pParent = (PKFSDIR)pChild;
1767 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
1768 {
1769 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1770 return NULL;
1771 }
1772 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1773 || pChild->uCacheGen == pCache->uGenerationMissing)
1774 {
1775 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1776 return NULL;
1777 }
1778 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
1779 pParent = (PKFSDIR)pChild;
1780 else
1781 return NULL;
1782 }
1783
1784 return NULL;
1785}
1786
1787
1788/**
1789 * Walk the file system tree for the given absolute path, UTF-16 version.
1790 *
1791 * This will create any missing nodes while walking.
1792 *
1793 * The caller will have to do the path hash table insertion of the result.
1794 *
1795 * @returns Pointer to the tree node corresponding to @a pszPath.
1796 * NULL on lookup failure, see @a penmError for details.
1797 * @param pCache The cache.
1798 * @param pwszPath The path to walk.
1799 * @param cwcPath The length of the path (in wchar_t's).
1800 * @param penmError Where to return details as to why the lookup
1801 * failed.
1802 */
1803static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KFSLOOKUPERROR *penmError)
1804{
1805 PKFSDIR pParent = &pCache->RootDir;
1806 PKFSOBJ pChild;
1807 KU32 off;
1808 KU32 cwcSlashes;
1809 KU32 offEnd;
1810
1811 KFSCACHE_LOG(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath));
1812
1813 /*
1814 * The root "directory" needs special handling, so we keep it outside the
1815 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
1816 */
1817 cwcSlashes = 0;
1818 off = 0;
1819 if ( pwszPath[1] == ':'
1820 && IS_ALPHA(pwszPath[0]))
1821 {
1822 /* Drive letter. */
1823 offEnd = 2;
1824 kHlpAssert(IS_SLASH(pwszPath[2]));
1825 pChild = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError);
1826 }
1827 else if ( IS_SLASH(pwszPath[0])
1828 && IS_SLASH(pwszPath[1]) )
1829 pChild = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError);
1830 else
1831 {
1832 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
1833 return NULL;
1834 }
1835 if (pChild)
1836 { /* likely */ }
1837 else
1838 return NULL;
1839
1840 /* Count slashes trailing the root spec. */
1841 if (offEnd < cwcPath)
1842 {
1843 kHlpAssert(IS_SLASH(pwszPath[offEnd]));
1844 do
1845 cwcSlashes++;
1846 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
1847 }
1848
1849 /* Done already? */
1850 if (offEnd >= cwcPath)
1851 {
1852 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1853 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
1854 || kFsCacheRefreshObj(pCache, pChild, penmError))
1855 return kFsCacheObjRetainInternal(pChild);
1856 return NULL;
1857 }
1858
1859 /* Check that we've got a valid result and not a cached negative one. */
1860 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
1861 { /* likely */ }
1862 else
1863 {
1864 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
1865 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
1866 return pChild;
1867 }
1868
1869 /* Next component. */
1870 pParent = (PKFSDIR)pChild;
1871 off = offEnd + cwcSlashes;
1872
1873
1874 /*
1875 * Walk loop.
1876 */
1877 for (;;)
1878 {
1879 /*
1880 * Find the end of the component, counting trailing slashes.
1881 */
1882 wchar_t wc;
1883 cwcSlashes = 0;
1884 offEnd = off + 1;
1885 while ((wc = pwszPath[offEnd]) != '\0')
1886 {
1887 if (!IS_SLASH(wc))
1888 offEnd++;
1889 else
1890 {
1891 do
1892 cwcSlashes++;
1893 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
1894 break;
1895 }
1896 }
1897
1898 /*
1899 * Do we need to populate or refresh this directory first?
1900 */
1901 if ( pParent->fPopulated
1902 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1903 || pParent->Obj.uCacheGen == pCache->uGeneration) )
1904 { /* likely */ }
1905 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
1906 { /* likely */ }
1907 else
1908 return NULL;
1909
1910 /*
1911 * Search the current node for the name.
1912 *
1913 * If we don't find it, we may insert a missing node depending on
1914 * the cache configuration.
1915 */
1916 pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off);
1917 if (pChild != NULL)
1918 { /* probably likely */ }
1919 else
1920 {
1921 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1922 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
1923 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
1924 {
1925 if (pChild)
1926 return pChild;
1927 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1928 }
1929 else
1930 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1931 return NULL;
1932 }
1933
1934 /* Advance off and check if we're done already. */
1935 off = offEnd + cwcSlashes;
1936 if ( cwcSlashes == 0
1937 || off >= cwcPath)
1938 {
1939 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
1940 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1941 || pChild->uCacheGen == pCache->uGenerationMissing
1942 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
1943 { /* likely */ }
1944 else
1945 return NULL;
1946 return pChild;
1947 }
1948
1949 /*
1950 * Check that it's a directory. If a missing entry, we may have to
1951 * refresh it and re-examin it.
1952 */
1953 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
1954 pParent = (PKFSDIR)pChild;
1955 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
1956 {
1957 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1958 return NULL;
1959 }
1960 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1961 || pChild->uCacheGen == pCache->uGenerationMissing)
1962 {
1963 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1964 return NULL;
1965 }
1966 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
1967 pParent = (PKFSDIR)pChild;
1968 else
1969 return NULL;
1970 }
1971
1972 return NULL;
1973}
1974
1975
1976/**
1977 * This deals with paths that are relative and paths that contains '..'
1978 * elements, ANSI version.
1979 *
1980 * @returns Pointer to object corresponding to @a pszPath on success.
1981 * NULL if this isn't a path we care to cache.
1982 *
1983 * @param pCache The cache.
1984 * @param pszPath The path.
1985 * @param cchPath The length of the path.
1986 * @param penmError Where to return details as to why the lookup
1987 * failed.
1988 */
1989static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
1990{
1991 /*
1992 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
1993 * ends up calling it anyway.
1994 */
1995 char szFull[KFSCACHE_CFG_MAX_PATH];
1996 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
1997 if ( cchFull >= 3
1998 && cchFull < sizeof(szFull))
1999 {
2000 PKFSOBJ pFsObj;
2001 KFSCACHE_LOG(("kFsCacheLookupSlowA(%s)\n", pszPath));
2002 pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError);
2003
2004#if 0 /* No need to do this until it's actually queried. */
2005 /* Cache the resulting path. */
2006 if ( pFsObj
2007 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2008 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2009 {
2010 KU32 uHashPath = kFsCacheStrHash(szFull);
2011 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2012 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2013 }
2014#endif
2015 return pFsObj;
2016 }
2017
2018 /* The path is too long! */
2019 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
2020 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2021 return NULL;
2022}
2023
2024
2025/**
2026 * This deals with paths that are relative and paths that contains '..'
2027 * elements, UTF-16 version.
2028 *
2029 * @returns Pointer to object corresponding to @a pszPath on success.
2030 * NULL if this isn't a path we care to cache.
2031 *
2032 * @param pCache The cache.
2033 * @param pwszPath The path.
2034 * @param cwcPath The length of the path (in wchar_t's).
2035 * @param penmError Where to return details as to why the lookup
2036 * failed.
2037 */
2038static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KFSLOOKUPERROR *penmError)
2039{
2040 /*
2041 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2042 * ends up calling it anyway.
2043 */
2044 wchar_t wszFull[KFSCACHE_CFG_MAX_PATH];
2045 UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL);
2046 if ( cwcFull >= 3
2047 && cwcFull < KFSCACHE_CFG_MAX_PATH)
2048 {
2049 PKFSOBJ pFsObj;
2050 KFSCACHE_LOG(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
2051 pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError);
2052
2053#if 0 /* No need to do this until it's actually queried. */
2054 /* Cache the resulting path. */
2055 if ( pFsObj
2056 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2057 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2058 {
2059 KU32 uHashPath = kFsCacheStrHash(szFull);
2060 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2061 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2062 }
2063#endif
2064 return pFsObj;
2065 }
2066
2067 /* The path is too long! */
2068 kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull));
2069 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2070 return NULL;
2071}
2072
2073
2074/**
2075 * Refreshes a path hash that has expired, ANSI version.
2076 *
2077 * @returns pHash on success, NULL if removed.
2078 * @param pCache The cache.
2079 * @param pHashEntry The path hash.
2080 * @param idxHashTab The hash table entry.
2081 */
2082static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
2083{
2084 /** @todo implement once we've start inserting uCacheGen nodes. */
2085 __debugbreak();
2086 K_NOREF(pCache);
2087 K_NOREF(idxHashTab);
2088 return pHashEntry;
2089}
2090
2091
2092/**
2093 * Refreshes a path hash that has expired, UTF-16 version.
2094 *
2095 * @returns pHash on success, NULL if removed.
2096 * @param pCache The cache.
2097 * @param pHashEntry The path hash.
2098 * @param idxHashTab The hash table entry.
2099 */
2100static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab)
2101{
2102 /** @todo implement once we've start inserting uCacheGen nodes. */
2103 __debugbreak();
2104 K_NOREF(pCache);
2105 K_NOREF(idxHashTab);
2106 return pHashEntry;
2107}
2108
2109
2110/**
2111 * Looks up a KFSOBJ for the given ANSI path.
2112 *
2113 * This will first try the hash table. If not in the hash table, the file
2114 * system cache tree is walked, missing bits filled in and finally a hash table
2115 * entry is created.
2116 *
2117 * Only drive letter paths are cachable. We don't do any UNC paths at this
2118 * point.
2119 *
2120 * @returns Reference to object corresponding to @a pszPath on success, this
2121 * must be released by kwFsCacheRelease.
2122 * NULL if not a path we care to cache.
2123 * @param pCache The cache.
2124 * @param pszPath The path to lookup.
2125 * @param penmError Where to return details as to why the lookup
2126 * failed.
2127 */
2128PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
2129{
2130 /*
2131 * Do hash table lookup of the path.
2132 */
2133 KU32 uHashPath;
2134 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
2135 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
2136 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab];
2137 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2138 if (pHashEntry)
2139 {
2140 do
2141 {
2142 if ( pHashEntry->uHashPath == uHashPath
2143 && pHashEntry->cchPath == cchPath
2144 && kHlpMemComp(pHashEntry->pszPath, pszPath, cchPath) == 0)
2145 {
2146 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2147 || pHashEntry->uCacheGen == pCache->uGeneration
2148 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
2149 {
2150 pCache->cLookups++;
2151 pCache->cPathHashHits++;
2152 KFSCACHE_LOG(("kFsCacheLookupA(%s) - hit %p\n", pszPath, pHashEntry->pFsObj));
2153 *penmError = pHashEntry->enmError;
2154 if (pHashEntry->pFsObj)
2155 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
2156 return NULL;
2157 }
2158 break;
2159 }
2160 pHashEntry = pHashEntry->pNext;
2161 } while (pHashEntry);
2162 }
2163
2164 /*
2165 * Create an entry for it by walking the file system cache and filling in the blanks.
2166 */
2167 if ( cchPath > 0
2168 && cchPath < KFSCACHE_CFG_MAX_PATH)
2169 {
2170 PKFSOBJ pFsObj;
2171
2172 /* Is absolute without any '..' bits? */
2173 if ( cchPath >= 3
2174 && ( ( pszPath[1] == ':' /* Drive letter */
2175 && IS_SLASH(pszPath[2])
2176 && IS_ALPHA(pszPath[0]) )
2177 || ( IS_SLASH(pszPath[0]) /* UNC */
2178 && IS_SLASH(pszPath[1]) ) )
2179 && !kFsCacheHasDotDotA(pszPath, cchPath) )
2180 pFsObj = kFsCacheLookupAbsoluteA(pCache, pszPath, cchPath, penmError);
2181 else
2182 pFsObj = kFsCacheLookupSlowA(pCache, pszPath, cchPath, penmError);
2183 if ( pFsObj
2184 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2185 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
2186 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
2187 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath, idxHashTab, *penmError);
2188
2189 pCache->cLookups++;
2190 if (pFsObj)
2191 pCache->cWalkHits++;
2192 return pFsObj;
2193 }
2194
2195 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2196 return NULL;
2197}
2198
2199
2200/**
2201 * Looks up a KFSOBJ for the given UTF-16 path.
2202 *
2203 * This will first try the hash table. If not in the hash table, the file
2204 * system cache tree is walked, missing bits filled in and finally a hash table
2205 * entry is created.
2206 *
2207 * Only drive letter paths are cachable. We don't do any UNC paths at this
2208 * point.
2209 *
2210 * @returns Reference to object corresponding to @a pszPath on success, this
2211 * must be released by kwFsCacheRelease.
2212 * NULL if not a path we care to cache.
2213 * @param pCache The cache.
2214 * @param pwszPath The path to lookup.
2215 * @param penmError Where to return details as to why the lookup
2216 * failed.
2217 */
2218PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
2219{
2220 /*
2221 * Do hash table lookup of the path.
2222 */
2223 KU32 uHashPath;
2224 KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath);
2225 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
2226 PKFSHASHW pHashEntry = pCache->apUtf16Paths[idxHashTab];
2227 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2228 if (pHashEntry)
2229 {
2230 do
2231 {
2232 if ( pHashEntry->uHashPath == uHashPath
2233 && pHashEntry->cwcPath == cwcPath
2234 && kHlpMemComp(pHashEntry->pwszPath, pwszPath, cwcPath) == 0)
2235 {
2236 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2237 || pHashEntry->uCacheGen == pCache->uGeneration
2238 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) )
2239 {
2240 pCache->cLookups++;
2241 pCache->cPathHashHits++;
2242 KFSCACHE_LOG(("kFsCacheLookupW(%ls) - hit %p\n", pwszPath, pHashEntry->pFsObj));
2243 *penmError = pHashEntry->enmError;
2244 if (pHashEntry->pFsObj)
2245 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
2246 return NULL;
2247 }
2248 break;
2249 }
2250 pHashEntry = pHashEntry->pNext;
2251 } while (pHashEntry);
2252 }
2253
2254 /*
2255 * Create an entry for it by walking the file system cache and filling in the blanks.
2256 */
2257 if ( cwcPath > 0
2258 && cwcPath < KFSCACHE_CFG_MAX_PATH)
2259 {
2260 PKFSOBJ pFsObj;
2261
2262 /* Is absolute without any '..' bits? */
2263 if ( cwcPath >= 3
2264 && ( ( pwszPath[1] == ':' /* Drive letter */
2265 && IS_SLASH(pwszPath[2])
2266 && IS_ALPHA(pwszPath[0]) )
2267 || ( IS_SLASH(pwszPath[0]) /* UNC */
2268 && IS_SLASH(pwszPath[1]) ) )
2269 && !kFsCacheHasDotDotW(pwszPath, cwcPath) )
2270 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwszPath, cwcPath, penmError);
2271 else
2272 pFsObj = kFsCacheLookupSlowW(pCache, pwszPath, cwcPath, penmError);
2273 if ( pFsObj
2274 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2275 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
2276 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
2277 kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwszPath, cwcPath, uHashPath, idxHashTab, *penmError);
2278
2279 pCache->cLookups++;
2280 if (pFsObj)
2281 pCache->cWalkHits++;
2282 return pFsObj;
2283 }
2284
2285 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2286 return NULL;
2287}
2288
2289
2290/**
2291 * Destroys a cache object which has a zero reference count.
2292 *
2293 * @returns 0
2294 * @param pCache The cache.
2295 * @param pObj The object.
2296 */
2297KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
2298{
2299 kHlpAssert(pObj->cRefs == 0);
2300 kHlpAssert(pObj->pParent == NULL);
2301 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2302
2303 /*
2304 * Invalidate the structure.
2305 */
2306 pObj->u32Magic = ~KFSOBJ_MAGIC;
2307
2308 /*
2309 * Destroy any user data first.
2310 */
2311 while (pObj->pUserDataHead != NULL)
2312 {
2313 PKFSUSERDATA pUserData = pObj->pUserDataHead;
2314 pObj->pUserDataHead = pUserData->pNext;
2315 pUserData->pfnDestructor(pCache, pObj, pUserData);
2316 kHlpFree(pUserData);
2317 }
2318
2319 /*
2320 * Do type specific destruction
2321 */
2322 switch (pObj->bObjType)
2323 {
2324 case KFSOBJ_TYPE_MISSING:
2325 /* nothing else to do here */
2326 pCache->cbObjects -= sizeof(KFSDIR);
2327 break;
2328
2329 case KFSOBJ_TYPE_DIR:
2330 {
2331 PKFSDIR pDir = (PKFSDIR)pObj;
2332 KU32 cChildren = pDir->cChildren;
2333 pCache->cbObjects -= sizeof(*pDir)
2334 + K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
2335 + pDir->cHashTab * sizeof(pDir->paHashTab);
2336
2337 pDir->cChildren = 0;
2338 while (cChildren-- > 0)
2339 kFsCacheObjRelease(pCache, pDir->papChildren[cChildren]);
2340 kHlpFree(pDir->papChildren);
2341 pDir->papChildren = NULL;
2342
2343 kHlpFree(pDir->paHashTab);
2344 pDir->paHashTab = NULL;
2345 break;
2346 }
2347
2348 case KFSOBJ_TYPE_FILE:
2349 case KFSOBJ_TYPE_OTHER:
2350 pCache->cbObjects -= sizeof(*pObj);
2351 break;
2352
2353 default:
2354 return 0;
2355 }
2356
2357 /*
2358 * Common bits.
2359 */
2360 pCache->cbObjects -= pObj->cchName + 1;
2361#ifdef KFSCACHE_CFG_UTF16
2362 pCache->cbObjects -= (pObj->cwcName + 1) * sizeof(wchar_t);
2363#endif
2364#ifdef KFSCACHE_CFG_SHORT_NAMES
2365 if (pObj->pszName != pObj->pszShortName)
2366 {
2367 pCache->cbObjects -= pObj->cchShortName + 1;
2368# ifdef KFSCACHE_CFG_UTF16
2369 pCache->cbObjects -= (pObj->cwcShortName + 1) * sizeof(wchar_t);
2370# endif
2371 }
2372#endif
2373 pCache->cObjects--;
2374
2375 kHlpFree(pObj);
2376 return 0;
2377}
2378
2379
2380/**
2381 * Releases a reference to a cache object.
2382 *
2383 * @returns New reference count.
2384 * @param pCache The cache.
2385 * @param pObj The object.
2386 */
2387KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
2388{
2389 KU32 cRefs;
2390 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2391 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2392
2393 cRefs = --pObj->cRefs;
2394 if (cRefs)
2395 return cRefs;
2396 return kFsCacheObjDestroy(pCache, pObj);
2397}
2398
2399
2400/**
2401 * Retains a reference to a cahce object.
2402 *
2403 * @returns New reference count.
2404 * @param pObj The object.
2405 */
2406KU32 kFsCacheObjRetain(PKFSOBJ pObj)
2407{
2408 KU32 cRefs;
2409 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2410 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
2411
2412 cRefs = ++pObj->cRefs;
2413 kHlpAssert(cRefs < 16384);
2414 return cRefs;
2415}
2416
2417
2418
2419PKFSCACHE kFsCacheCreate(KU32 fFlags)
2420{
2421 PKFSCACHE pCache;
2422 birdResolveImports();
2423
2424 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
2425 if (pCache)
2426 {
2427 /* Dummy root dir entry. */
2428 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC;
2429 pCache->RootDir.Obj.cRefs = 1;
2430 pCache->RootDir.Obj.uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
2431 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR;
2432 pCache->RootDir.Obj.fHaveStats = K_FALSE;
2433 pCache->RootDir.Obj.pParent = NULL;
2434 pCache->RootDir.Obj.pszName = "";
2435 pCache->RootDir.Obj.cchName = 0;
2436 pCache->RootDir.Obj.cchParent = 0;
2437#ifdef KFSCACHE_CFG_UTF16
2438 pCache->RootDir.Obj.cwcName = 0;
2439 pCache->RootDir.Obj.cwcParent = 0;
2440 pCache->RootDir.Obj.pwszName = L"";
2441#endif
2442
2443#ifdef KFSCACHE_CFG_SHORT_NAMES
2444 pCache->RootDir.Obj.pszShortName = NULL;
2445 pCache->RootDir.Obj.cchShortName = 0;
2446 pCache->RootDir.Obj.cchShortParent = 0;
2447# ifdef KFSCACHE_CFG_UTF16
2448 pCache->RootDir.Obj.cwcShortName;
2449 pCache->RootDir.Obj.cwcShortParent;
2450 pCache->RootDir.Obj.pwszShortName;
2451# endif
2452#endif
2453 pCache->RootDir.cChildren = 0;
2454 pCache->RootDir.papChildren = NULL;
2455 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
2456 pCache->RootDir.cHashTab = 251;
2457 pCache->RootDir.paHashTab = (PKFSOBJHASH)kHlpAllocZ( pCache->RootDir.cHashTab
2458 * sizeof(pCache->RootDir.paHashTab[0]));
2459 if (pCache->RootDir.paHashTab)
2460 {
2461 /* The cache itself. */
2462 pCache->u32Magic = KFSCACHE_MAGIC;
2463 pCache->fFlags = fFlags;
2464 pCache->uGeneration = 1;
2465 pCache->cObjects = 1;
2466 pCache->cbObjects = sizeof(pCache->RootDir) + pCache->RootDir.cHashTab * sizeof(pCache->RootDir.paHashTab[0]);
2467 pCache->cPathHashHits = 0;
2468 pCache->cWalkHits = 0;
2469 pCache->cAnsiPaths = 0;
2470 pCache->cAnsiPathCollisions = 0;
2471 pCache->cbAnsiPaths = 0;
2472#ifdef KFSCACHE_CFG_UTF16
2473 pCache->cUtf16Paths = 0;
2474 pCache->cUtf16PathCollisions = 0;
2475 pCache->cbUtf16Paths = 0;
2476#endif
2477 return pCache;
2478 }
2479
2480 kHlpFree(pCache);
2481 }
2482 return NULL;
2483}
2484
Note: See TracBrowser for help on using the repository browser.