source: trunk/src/lib/nt/ntdircache.c@ 2852

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

updates

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