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

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

Updates

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