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

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

updates

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