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

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

updates

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