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

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

updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 70.7 KB
Line 
1/* $Id: ntdircache.c 2851 2016-08-31 17:30:52Z 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 <intrin.h>
42//#include <setjmp.h>
43//#include <ctype.h>
44
45
46//#include <Windows.h>
47//#include <winternl.h>
48
49
50
51/*********************************************************************************************************************************
52* Defined Constants And Macros *
53*********************************************************************************************************************************/
54/** @def KFSCACHE_CFG_UTF16
55 * Whether to compile in the UTF-16 names support. */
56#define KFSCACHE_CFG_UTF16 1
57/** @def KFSCACHE_CFG_SHORT_NAMES
58 * Whether to compile in the short name support. */
59#define KFSCACHE_CFG_SHORT_NAMES 1
60/** @def KFSCACHE_CFG_PATH_HASH_TAB_SIZE
61 * Size of the path hash table. */
62#define KFSCACHE_CFG_PATH_HASH_TAB_SIZE 16381
63/** The max length paths we consider. */
64#define KFSCACHE_CFG_MAX_PATH 1024
65/** The max ANSI name length. */
66#define KFSCACHE_CFG_MAX_ANSI_NAME (256*3 + 16)
67/** The max UTF-16 name length. */
68#define KFSCACHE_CFG_MAX_UTF16_NAME (256*2 + 16)
69
70
71
72/** Special KFSOBJ::uCacheGen number indicating that it does not apply. */
73#define KFSWOBJ_CACHE_GEN_IGNORE KU32_MAX
74
75/** @def KW_LOG
76 * Generic logging.
77 * @param a Argument list for kFsCacheDbgPrintf */
78#ifndef NDEBUG
79# define KFSCACHE_LOG(a) kFsCacheDbgPrintf a
80#else
81# define KFSCACHE_LOG(a) do { } while (0)
82#endif
83
84
85/** @name KFSOBJ_TYPE_XXX - KFSOBJ::bObjType
86 * @{ */
87/** Directory, type KFSDIR. */
88#define KFSOBJ_TYPE_DIR KU8_C(0x01)
89/** Regular file - type KFSOBJ. */
90#define KFSOBJ_TYPE_FILE KU8_C(0x02)
91/** Other file - type KFSOBJ. */
92#define KFSOBJ_TYPE_OTHER KU8_C(0x03)
93/** Caching of a negative result - type KFSOBJ.
94 * @remarks We will allocate enough space for the largest cache node, so this
95 * can metamorph into any other object should it actually turn up. */
96#define KFSOBJ_TYPE_MISSING KU8_C(0x04)
97///** Invalidated entry flag. */
98//#define KFSOBJ_TYPE_F_INVALID KU8_C(0x20)
99/** @} */
100
101/** @name KFSOBJ_F_XXX - KFSOBJ::fFlags
102 * @{ */
103/** Whether the file system update the modified timestamp of directories
104 * when something is removed from it or added to it.
105 * @remarks They say NTFS is the only windows filesystem doing this. */
106#define KFSOBJ_F_WORKING_DIR_MTIME KU32_C(0x00000001)
107/** NTFS file system volume. */
108#define KFSOBJ_F_NTFS KU32_C(0x80000000)
109/** @} */
110
111
112#define IS_ALPHA(ch) ( ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= 'a' && (ch) <= 'z') )
113#define IS_SLASH(ch) ((ch) == '\\' || (ch) == '/')
114
115
116/*********************************************************************************************************************************
117* Structures and Typedefs *
118*********************************************************************************************************************************/
119/** Pointer to a core object. */
120typedef struct KFSOBJ *PKFSOBJ;
121/** Pointer to a directory object. */
122typedef struct KFSDIR *PKFSDIR;
123/** Pointer to a directory hash table entry. */
124typedef struct KFSOBJHASH *PKFSOBJHASH;
125
126
127/**
128 * Directory hash table entry.
129 *
130 * There can be two of these per directory entry when the short name differs
131 * from the long name.
132 */
133typedef struct KFSOBJHASH
134{
135 /** Pointer to the next entry with the same hash. */
136 PKFSOBJHASH pNext;
137 /** Pointer to the object. */
138 PKFSOBJ pObj;
139} KFSOBJHASH;
140
141
142/**
143 * Base cache node.
144 */
145typedef struct KFSOBJ
146{
147 /** Magic value (KFSOBJ_MAGIC). */
148 KU32 u32Magic;
149 /** Number of references. */
150 KU32 volatile cRefs;
151 /** The cache generation, see KFSWOBJ_CACHE_GEN_IGNORE. */
152 KU32 uCacheGen;
153 /** The object type, KFSOBJ_TYPE_XXX. */
154 KU8 bObjType;
155 /** Set if the Stats member is valid, clear if not. */
156 KBOOL fHaveStats;
157 /** Unused flags. */
158 KBOOL abUnused[2];
159 /** Flags, KFSOBJ_F_XXX. */
160 KU32 fFlags;
161
162 /** Pointer to the parent (directory).
163 * This is only NULL for a root. */
164 PKFSDIR pParent;
165
166 /** The directory name. (Allocated after the structure.) */
167 const char *pszName;
168 /** The length of pszName. */
169 KU16 cchName;
170 /** The length of the parent path (up to where pszName starts).
171 * @note This is valuable when constructing an absolute path to this node by
172 * means of the parent pointer (no need for recursion). */
173 KU16 cchParent;
174#ifdef KFSCACHE_CFG_UTF16
175 /** The length of pwszName (in wchar_t's). */
176 KU16 cwcName;
177 /** The length of the parent UTF-16 path (in wchar_t's).
178 * @note This is valuable when constructing an absolute path to this node by
179 * means of the parent pointer (no need for recursion). */
180 KU16 cwcParent;
181 /** The UTF-16 object name. (Allocated after the structure.) */
182 const wchar_t *pwszName;
183#endif
184
185#ifdef KFSCACHE_CFG_SHORT_NAMES
186 /** The short object name. (Allocated after the structure, could be same
187 * as pszName.) */
188 const char *pszShortName;
189 /** The length of pszShortName. */
190 KU16 cchShortName;
191 /** The length of the short parent path (up to where pszShortName starts). */
192 KU16 cchShortParent;
193# ifdef KFSCACHE_CFG_UTF16
194 /** The length of pwszShortName (in wchar_t's). */
195 KU16 cwcShortName;
196 /** The length of the short parent UTF-16 path (in wchar_t's). */
197 KU16 cwcShortParent;
198 /** The UTF-16 short object name. (Allocated after the structure, possibly
199 * same as pwszName.) */
200 const wchar_t *pwszShortName;
201# endif
202#endif
203
204 /** Stats - only valid when fHaveStats is set. */
205 BirdStat_T Stats;
206} KFSOBJ;
207
208/** The magic for a KFSOBJ structure (Thelonious Sphere Monk). */
209#define KFSOBJ_MAGIC KU32_C(0x19171010)
210
211
212/**
213 * Directory node in the cache.
214 */
215typedef struct KFSDIR
216{
217 /** The core object information. */
218 KFSOBJ Obj;
219
220 /** Child objects. */
221 PKFSOBJ *papChildren;
222 /** The number of child objects. */
223 KU32 cChildren;
224
225 /** The size of the hash table.
226 * @remarks The hash table is optional and only used when there are a lot of
227 * entries in the directory. */
228 KU32 cHashTab;
229 /** Pointer to the hash table.
230 * @todo this isn't quite there yet, structure wise. sigh. */
231 PKFSOBJHASH paHashTab;
232
233 /** Handle to the directory (we generally keep it open). */
234 HANDLE hDir;
235 /** The device number we queried/inherited when opening it. */
236 KU64 uDevNo;
237
238 /** Set if populated. */
239 KBOOL fPopulated;
240} KFSDIR;
241
242
243/**
244 * Lookup errors.
245 */
246typedef enum KFSLOOKUPERROR
247{
248 /** Lookup was a success. */
249 KFSLOOKUPERROR_SUCCESS = 0,
250 /** A path component was not found. */
251 KFSLOOKUPERROR_PATH_COMP_NOT_FOUND,
252 /** A path component is not a directory. */
253 KFSLOOKUPERROR_PATH_COMP_NOT_DIR,
254 /** The final path entry is not a directory (trailing slash). */
255 KFSLOOKUPERROR_NOT_DIR,
256 /** Not found. */
257 KFSLOOKUPERROR_NOT_FOUND,
258 /** The path is too long. */
259 KFSLOOKUPERROR_PATH_TOO_LONG,
260 /** Unsupported path type. */
261 KFSLOOKUPERROR_UNSUPPORTED,
262 /** We're out of memory. */
263 KFSLOOKUPERROR_OUT_OF_MEMORY,
264
265 /** Error opening directory. */
266 KFSLOOKUPERROR_DIR_OPEN_ERROR,
267 /** Error reading directory. */
268 KFSLOOKUPERROR_DIR_READ_ERROR,
269 /** UTF-16 to ANSI conversion error. */
270 KFSLOOKUPERROR_ANSI_CONVERSION_ERROR,
271 /** ANSI to UTF-16 conversion error. */
272 KFSLOOKUPERROR_UTF16_CONVERSION_ERROR,
273 /** Internal error. */
274 KFSLOOKUPERROR_INTERNAL_ERROR
275} KFSLOOKUPERROR;
276
277
278/** Pointer to an ANSI path hash table entry. */
279typedef struct KFSHASHA *PKFSHASHA;
280/**
281 * ANSI file system path hash table entry.
282 * The path hash table allows us to skip parsing and walking a path.
283 */
284typedef struct KFSHASHA
285{
286 /** Next entry with the same hash table slot. */
287 PKFSHASHA pNext;
288 /** Path hash value. */
289 KU32 uHashPath;
290 /** The path length. */
291 KU32 cchPath;
292 /** The cache generation ID. */
293 KU32 uCacheGen;
294 /** The lookup error (when pFsObj is NULL). */
295 KFSLOOKUPERROR enmError;
296 /** The path. (Allocated after the structure.) */
297 const char *pszPath;
298 /** Pointer to the matching FS object.
299 * This is NULL for negative path entries? */
300 PKFSOBJ pFsObj;
301} KFSHASHA;
302
303
304#ifdef KFSCACHE_CFG_UTF16
305/** Pointer to an UTF-16 path hash table entry. */
306typedef struct KFSHASHW *PKFSHASHW;
307/**
308 * UTF-16 file system path hash table entry. The path hash table allows us
309 * to skip parsing and walking a path.
310 */
311typedef struct KFSHASHW
312{
313 /** Next entry with the same hash table slot. */
314 PKFSHASHW pNext;
315 /** Path hash value. */
316 KU32 uHashPath;
317 /** The path length (in wchar_t units). */
318 KU32 cwcPath;
319 /** The cache generation ID. */
320 KU32 uCacheGen;
321 /** The lookup error (when pFsObj is NULL). */
322 KFSLOOKUPERROR enmError;
323 /** The path. (Allocated after the structure.) */
324 const wchar_t *pwszPath;
325 /** Pointer to the matching FS object.
326 * This is NULL for negative path entries? */
327 PKFSOBJ pFsObj;
328} KFSHASHW;
329#endif
330
331
332/** @name KFSCACHE_F_XXX
333 * @{ */
334/** Whether to cache missing directory entries (KFSOBJ_TYPE_MISSING). */
335#define KFSCACHE_F_MISSING_OBJECTS KU32_C(0x00000001)
336/** Whether to cache missing paths. */
337#define KFSCACHE_F_MISSING_PATHS KU32_C(0x00000002)
338/** @} */
339
340
341/** Pointer to a cache. */
342typedef struct KFSCACHE *PKFSCACHE;
343/**
344 * Directory cache instance.
345 */
346typedef struct KFSCACHE
347{
348 /** Magic value (KFSCACHE_MAGIC). */
349 KU32 u32Magic;
350 /** Cache flags. */
351 KU32 fFlags;
352
353 /** The current cache generation for objects that already exists. */
354 KU32 uGeneration;
355 /** The current cache generation for missing objects, negative results, ++. */
356 KU32 uGenerationMissing;
357
358 /** Number of cache objects. */
359 KSIZE cObjects;
360 /** Memory occupied by the cache object structures. */
361 KSIZE cbObjects;
362 /** Number of lookups. */
363 KSIZE cLookups;
364 /** Number of hits in the path hash tables. */
365 KSIZE cPathHashHits;
366 /** Number of hits walking the file system hierarchy. */
367 KSIZE cWalkHits;
368
369 /** The root directory. */
370 KFSDIR RootDir;
371
372 /** File system hash table for ANSI filename strings. */
373 PKFSHASHA apAnsiPaths[KFSCACHE_CFG_PATH_HASH_TAB_SIZE];
374 /** Number of paths in the apAnsiPaths hash table. */
375 KSIZE cAnsiPaths;
376 /** Number of collisions in the apAnsiPaths hash table. */
377 KSIZE cAnsiPathCollisions;
378 /** Amount of memory used by the path entries. */
379 KSIZE cbAnsiPaths;
380
381#ifdef KFSCACHE_CFG_UTF16
382 /** Number of paths in the apUtf16Paths hash table. */
383 KSIZE cUtf16Paths;
384 /** Number of collisions in the apUtf16Paths hash table. */
385 KSIZE cUtf16PathCollisions;
386 /** Amount of memory used by the UTF-16 path entries. */
387 KSIZE cbUtf16Paths;
388 /** File system hash table for UTF-16 filename strings. */
389 PKFSHASHW apUtf16Paths[KFSCACHE_CFG_PATH_HASH_TAB_SIZE];
390#endif
391} KFSCACHE;
392
393/** Magic value for KFSCACHE::u32Magic (Jon Batiste). */
394#define KFSCACHE_MAGIC KU32_C(0x19861111)
395
396
397/*********************************************************************************************************************************
398* Internal Functions *
399*********************************************************************************************************************************/
400KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj);
401
402
403/**
404 * Retains a reference to a cache object, internal version.
405 *
406 * @returns pObj
407 * @param pObj The object.
408 */
409K_INLINE PKFSOBJ kFsCacheObjRetainInternal(PKFSOBJ pObj)
410{
411 KU32 cRefs = ++pObj->cRefs;
412 kHlpAssert(cRefs < 16384);
413 K_NOREF(cRefs);
414 return pObj;
415}
416
417
418#ifndef NDEBUG
419
420/**
421 * Debug printing.
422 * @param pszFormat Debug format string.
423 * @param ... Format argument.
424 */
425static void kFsCacheDbgPrintfV(const char *pszFormat, va_list va)
426{
427 if (1)
428 {
429 DWORD const dwSavedErr = GetLastError();
430
431 fprintf(stderr, "debug: ");
432 vfprintf(stderr, pszFormat, va);
433
434 SetLastError(dwSavedErr);
435 }
436}
437
438
439/**
440 * Debug printing.
441 * @param pszFormat Debug format string.
442 * @param ... Format argument.
443 */
444static void kFsCacheDbgPrintf(const char *pszFormat, ...)
445{
446 if (1)
447 {
448 va_list va;
449 va_start(va, pszFormat);
450 kFsCacheDbgPrintfV(pszFormat, va);
451 va_end(va);
452 }
453}
454
455#endif /* !NDEBUG */
456
457
458
459/**
460 * Hashes a string.
461 *
462 * @returns 32-bit string hash.
463 * @param pszString String to hash.
464 */
465static KU32 kFsCacheStrHash(const char *pszString)
466{
467 /* This algorithm was created for sdbm (a public-domain reimplementation of
468 ndbm) database library. it was found to do well in scrambling bits,
469 causing better distribution of the keys and fewer splits. it also happens
470 to be a good general hashing function with good distribution. the actual
471 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
472 is the faster version used in gawk. [there is even a faster, duff-device
473 version] the magic constant 65599 was picked out of thin air while
474 experimenting with different constants, and turns out to be a prime.
475 this is one of the algorithms used in berkeley db (see sleepycat) and
476 elsewhere. */
477 KU32 uHash = 0;
478 KU32 uChar;
479 while ((uChar = (unsigned char)*pszString++) != 0)
480 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
481 return uHash;
482}
483
484
485/**
486 * Hashes a string.
487 *
488 * @returns The string length.
489 * @param pszString String to hash.
490 * @param puHash Where to return the 32-bit string hash.
491 */
492static KSIZE kFsCacheStrHashEx(const char *pszString, KU32 *puHash)
493{
494 const char * const pszStart = pszString;
495 KU32 uHash = 0;
496 KU32 uChar;
497 while ((uChar = (unsigned char)*pszString) != 0)
498 {
499 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
500 pszString++;
501 }
502 *puHash = uHash;
503 return pszString - pszStart;
504}
505
506
507/**
508 * Hashes a string.
509 *
510 * @returns The string length in wchar_t units.
511 * @param pwszString String to hash.
512 * @param puHash Where to return the 32-bit string hash.
513 */
514static KSIZE kFsCacheUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
515{
516 const wchar_t * const pwszStart = pwszString;
517 KU32 uHash = 0;
518 KU32 uChar;
519 while ((uChar = *pwszString) != 0)
520 {
521 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
522 pwszString++;
523 }
524 *puHash = uHash;
525 return pwszString - pwszStart;
526}
527
528#if 0
529
530/**
531 * Converts the given string to unicode.
532 *
533 * @returns Length of the resulting string in wchar_t's.
534 * @param pszSrc The source string.
535 * @param pwszDst The destination buffer.
536 * @param cwcDst The size of the destination buffer in wchar_t's.
537 */
538static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
539{
540 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
541 KSIZE offDst = 0;
542 while (offDst < cwcDst)
543 {
544 char ch = *pszSrc++;
545 pwszDst[offDst++] = ch;
546 if (!ch)
547 return offDst - 1;
548 kHlpAssert((unsigned)ch < 127);
549 }
550
551 pwszDst[offDst - 1] = '\0';
552 return offDst;
553}
554
555
556/**
557 * Converts the given UTF-16 to a normal string.
558 *
559 * @returns Length of the resulting string.
560 * @param pwszSrc The source UTF-16 string.
561 * @param pszDst The destination buffer.
562 * @param cbDst The size of the destination buffer in bytes.
563 */
564static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
565{
566 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
567 KSIZE offDst = 0;
568 while (offDst < cbDst)
569 {
570 wchar_t wc = *pwszSrc++;
571 pszDst[offDst++] = (char)wc;
572 if (!wc)
573 return offDst - 1;
574 kHlpAssert((unsigned)wc < 127);
575 }
576
577 pszDst[offDst - 1] = '\0';
578 return offDst;
579}
580
581
582
583/** UTF-16 string length. */
584static KSIZE kwUtf16Len(wchar_t const *pwsz)
585{
586 KSIZE cwc = 0;
587 while (*pwsz != '\0')
588 cwc++, pwsz++;
589 return cwc;
590}
591
592/**
593 * Copy out the UTF-16 string following the convension of GetModuleFileName
594 */
595static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
596{
597 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
598 if (cwcSrc + 1 <= cwcDst)
599 {
600 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
601 return (DWORD)cwcSrc;
602 }
603 if (cwcDst > 0)
604 {
605 KSIZE cwcDstTmp = cwcDst - 1;
606 pwszDst[cwcDstTmp] = '\0';
607 if (cwcDstTmp > 0)
608 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
609 }
610 SetLastError(ERROR_INSUFFICIENT_BUFFER);
611 return (DWORD)cwcDst;
612}
613
614
615/**
616 * Copy out the ANSI string following the convension of GetModuleFileName
617 */
618static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
619{
620 KSIZE cchSrc = kHlpStrLen(pszSrc);
621 if (cchSrc + 1 <= cbDst)
622 {
623 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
624 return (DWORD)cchSrc;
625 }
626 if (cbDst > 0)
627 {
628 KSIZE cbDstTmp = cbDst - 1;
629 pszDst[cbDstTmp] = '\0';
630 if (cbDstTmp > 0)
631 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
632 }
633 SetLastError(ERROR_INSUFFICIENT_BUFFER);
634 return (DWORD)cbDst;
635}
636
637
638/**
639 * Normalizes the path so we get a consistent hash.
640 *
641 * @returns status code.
642 * @param pszPath The path.
643 * @param pszNormPath The output buffer.
644 * @param cbNormPath The size of the output buffer.
645 */
646static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
647{
648 char *pchSlash;
649 KSIZE cchNormPath;
650
651 /*
652 * We hash these to speed stuff up (nt_fullpath isn't cheap and we're
653 * gonna have many repeat queries and assume nobody do case changes to
654 * anything essential while kmk is running).
655 */
656 KU32 uHashPath;
657 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
658 KU32 const idxHashTab = uHashPath % K_ELEMENTS(g_apFsNormalizedPathsA);
659 PKFSNORMHASHA pHashEntry = g_apFsNormalizedPathsA[idxHashTab];
660 if (pHashEntry)
661 {
662 do
663 {
664 if ( pHashEntry->uHashPath == uHashPath
665 && pHashEntry->cchPath == cchPath
666 && kHlpMemComp(pHashEntry->pszPath, pszPath, cchPath) == 0)
667 {
668 if (cbNormPath > pHashEntry->cchNormPath)
669 {
670 KFSCACHE_LOG(("kwPathNormalize(%s) - hit\n", pszPath));
671 kHlpMemCopy(pszNormPath, pHashEntry->szNormPath, pHashEntry->cchNormPath + 1);
672 return 0;
673 }
674 return KERR_BUFFER_OVERFLOW;
675 }
676 pHashEntry = pHashEntry->pNext;
677 } while (pHashEntry);
678 }
679
680 /*
681 * Do it the slow way.
682 */
683 nt_fullpath(pszPath, pszNormPath, cbNormPath);
684 /** @todo nt_fullpath overflow handling?!?!? */
685
686 pchSlash = kHlpStrChr(pszNormPath, '/');
687 while (pchSlash)
688 {
689 *pchSlash = '\\';
690 pchSlash = kHlpStrChr(pchSlash + 1, '/');
691 }
692
693 /*
694 * Create a new hash table entry (ignore failures).
695 */
696 cchNormPath = kHlpStrLen(pszNormPath);
697 if (cchNormPath < KU16_MAX && cchPath < KU16_MAX)
698 {
699 pHashEntry = (PKFSNORMHASHA)kHlpAlloc(sizeof(*pHashEntry) + cchNormPath + 1 + cchPath + 1);
700 if (pHashEntry)
701 {
702 pHashEntry->cchNormPath = (KU16)cchNormPath;
703 pHashEntry->cchPath = (KU16)cchPath;
704 pHashEntry->uHashPath = uHashPath;
705 pHashEntry->pszPath = (char *)kHlpMemCopy(&pHashEntry->szNormPath[cchNormPath + 1], pszPath, cchPath + 1);
706 kHlpMemCopy(pHashEntry->szNormPath, pszNormPath, cchNormPath + 1);
707
708 pHashEntry->pNext = g_apFsNormalizedPathsA[idxHashTab];
709 g_apFsNormalizedPathsA[idxHashTab] = pHashEntry;
710 }
711 }
712
713 return 0;
714}
715
716
717/**
718 * Get the pointer to the filename part of the path.
719 *
720 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
721 * @returns Pointer to the terminator char if no filename.
722 * @param pszPath The path to parse.
723 */
724static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
725{
726 const wchar_t *pwszLast = NULL;
727 for (;;)
728 {
729 wchar_t wc = *pwszPath;
730#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
731 if (wc == '/' || wc == '\\' || wc == ':')
732 {
733 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
734 /* nothing */;
735 pwszLast = pwszPath;
736 }
737#else
738 if (wc == '/')
739 {
740 while ((wc = *++pszFilename) == '/')
741 /* betsuni */;
742 pwszLast = pwszPath;
743 }
744#endif
745 if (!wc)
746 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
747 pwszPath++;
748 }
749}
750
751
752/**
753 * Check if the path leads to a regular file (that exists).
754 *
755 * @returns K_TRUE / K_FALSE
756 * @param pszPath Path to the file to check out.
757 */
758static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
759{
760 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
761 KSIZE cchPath = kHlpStrLen(pszPath);
762 if ( cchPath > 3
763 && pszPath[cchPath - 4] == '.'
764 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
765 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
766 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
767 {
768 PKFSOBJ pFsObj = kFsCacheLookupA(pszPath);
769 if (pFsObj)
770 {
771 if (!(pFsObj->fAttribs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE))) /* also checks invalid */
772 return K_TRUE;
773 }
774 }
775 else
776 {
777 BirdStat_T Stat;
778 int rc = birdStatFollowLink(pszPath, &Stat);
779 if (rc == 0)
780 {
781 if (S_ISREG(Stat.st_mode))
782 return K_TRUE;
783 }
784 }
785 return K_FALSE;
786}
787
788
789
790
791
792
793/**
794 * Helper for getting the extension of a UTF-16 path.
795 *
796 * @returns Pointer to the extension or the terminator.
797 * @param pwszPath The path.
798 * @param pcwcExt Where to return the length of the extension.
799 */
800static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
801{
802 wchar_t const *pwszName = pwszPath;
803 wchar_t const *pwszExt = NULL;
804 for (;;)
805 {
806 wchar_t const wc = *pwszPath++;
807 if (wc == '.')
808 pwszExt = pwszPath;
809 else if (wc == '/' || wc == '\\' || wc == ':')
810 {
811 pwszName = pwszPath;
812 pwszExt = NULL;
813 }
814 else if (wc == '\0')
815 {
816 if (pwszExt)
817 {
818 *pcwcExt = pwszPath - pwszExt - 1;
819 return pwszExt;
820 }
821 *pcwcExt = 0;
822 return pwszPath - 1;
823 }
824 }
825}
826#endif
827
828
829/**
830 * Looks for '..' in the path.
831 *
832 * @returns K_TRUE if '..' component found, K_FALSE if not.
833 * @param pszPath The path.
834 * @param cchPath The length of the path.
835 */
836static KBOOL kFsCacheHasDotDot(const char *pszPath, KSIZE cchPath)
837{
838 const char *pchDot = (const char *)kHlpMemChr(pszPath, '.', cchPath);
839 while (pchDot)
840 {
841 if (pchDot[1] != '.')
842 pchDot = (const char *)kHlpMemChr(pchDot + 1, '.', &pszPath[cchPath] - pchDot - 1);
843 else
844 {
845 char ch;
846 if ( (ch = pchDot[2]) == '\0'
847 && IS_SLASH(ch))
848 {
849 if (pchDot == pszPath)
850 return K_TRUE;
851 ch = pchDot[-1];
852 if ( IS_SLASH(ch)
853 || ch == ':')
854 return K_TRUE;
855 }
856 pchDot = (const char *)kHlpMemChr(pchDot + 2, '.', &pszPath[cchPath] - pchDot - 2);
857 }
858 }
859
860 return K_FALSE;
861}
862
863
864/**
865 * Creates an ANSI hash table entry for the given path.
866 *
867 * @returns The hash table entry or NULL if out of memory.
868 * @param pCache The hash
869 * @param pFsObj The resulting object.
870 * @param pszPath The path.
871 * @param cchPath The length of the path.
872 * @param uHashPath The hash of the path.
873 * @param idxHashTab The hash table index of the path.
874 * @param enmError The lookup error.
875 */
876static PKFSHASHA kFsCacheCreatePathHashTabEntryA(PKFSCACHE pCache, PKFSOBJ pFsObj, const char *pszPath, KU32 cchPath,
877 KU32 uHashPath, KU32 idxHashTab, KFSLOOKUPERROR enmError)
878{
879 PKFSHASHA pHashEntry = (PKFSHASHA)kHlpAlloc(sizeof(*pHashEntry) + cchPath + 1);
880 if (pHashEntry)
881 {
882 pHashEntry->uHashPath = uHashPath;
883 pHashEntry->cchPath = cchPath;
884 pHashEntry->pszPath = (const char *)kHlpMemCopy(pHashEntry + 1, pszPath, cchPath + 1);
885 pHashEntry->pFsObj = pFsObj;
886 pHashEntry->enmError = enmError;
887 if (pFsObj)
888 pHashEntry->uCacheGen = pCache->uGeneration;
889 else if (enmError != KFSLOOKUPERROR_UNSUPPORTED)
890 pHashEntry->uCacheGen = pCache->uGenerationMissing;
891 else
892 pHashEntry->uCacheGen = KFSWOBJ_CACHE_GEN_IGNORE;
893
894 pHashEntry->pNext = pCache->apAnsiPaths[idxHashTab];
895 pCache->apAnsiPaths[idxHashTab] = pHashEntry;
896
897 pCache->cbAnsiPaths += sizeof(*pHashEntry) + cchPath + 1;
898 pCache->cAnsiPaths++;
899 if (pHashEntry->pNext)
900 pCache->cAnsiPathCollisions++;
901 }
902}
903
904
905/**
906 * Links the child in under the parent.
907 *
908 * @returns K_TRUE on success, K_FALSE if out of memory.
909 * @param pParent The parent node.
910 * @param pChild The child node.
911 */
912static KBOOL kFsCacheDirAddChild(PKFSCACHE pCache, PKFSDIR pParent, PKFSOBJ pChild, KFSLOOKUPERROR *penmError)
913{
914 if ((pParent->cChildren % 16) == 0)
915 {
916 void *pvNew = kHlpRealloc(pParent->papChildren, (pParent->cChildren + 16) * sizeof(pParent->papChildren[0]));
917 if (!pvNew)
918 return K_FALSE;
919 pParent->papChildren = (PKFSOBJ *)pvNew;
920 pCache->cbObjects += 16 * sizeof(pParent->papChildren[0]);
921 }
922 pParent->papChildren[pParent->cChildren++] = pChild;
923 return K_TRUE;
924}
925
926
927/**
928 * Creates a new cache object.
929 *
930 * @returns Pointer (with 1 reference) to the new object. The object will not
931 * be linked to the parent directory yet.
932 *
933 * NULL if we're out of memory.
934 *
935 * @param pCache The cache.
936 * @param pParent The parent directory.
937 * @param pszName The ANSI name.
938 * @param cchName The length of the ANSI name.
939 * @param pwszName The UTF-16 name.
940 * @param cwcName The length of the UTF-16 name.
941 * @param pszShortName The ANSI short name, NULL if none.
942 * @param cchShortName The length of the ANSI short name, 0 if none.
943 * @param pwszShortName The UTF-16 short name, NULL if none.
944 * @param cwcShortName The length of the UTF-16 short name, 0 if none.
945 * @param bObjType The objct type.
946 * @param penmError Where to explain failures.
947 */
948static PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,
949 char const *pszName, KU16 cchName, wchar_t const *pwszName, KU16 cwcName,
950#ifdef KFSCACHE_CFG_SHORT_NAMES
951 char const *pszShortName, KU16 cchShortName, wchar_t const *pwszShortName, KU16 cwcShortName,
952#endif
953 KU8 bObjType, KFSLOOKUPERROR *penmError)
954{
955 /*
956 * Allocate the object.
957 */
958 KBOOL const fDirish = bObjType != KFSOBJ_TYPE_FILE && bObjType == KFSOBJ_TYPE_OTHER;
959 KSIZE const cbObj = (fDirish ? sizeof(KFSDIR) : sizeof(KFSOBJ))
960 + (cwcName + 1) * sizeof(wchar_t) + cchName + 1
961#ifdef KFSCACHE_CFG_SHORT_NAMES
962 + (cwcShortName > 0 ? (cwcShortName + 1) * sizeof(wchar_t) + cchShortName + 1 : 0)
963#endif
964 ;
965 PKFSOBJ pObj = (PKFSOBJ)kHlpAlloc(cbObj);
966 if (pObj)
967 {
968 KU8 *pbExtra = (KU8 *)(pObj + 1);
969
970 pCache->cbObjects += cbObj;
971 pCache->cObjects++;
972
973 /*
974 * Initialize the object.
975 */
976 pObj->u32Magic = KFSOBJ_MAGIC;
977 pObj->cRefs = 1;
978 pObj->uCacheGen = bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing;
979 pObj->bObjType = bObjType;
980 pObj->fHaveStats = K_FALSE;
981 pObj->abUnused[0] = K_FALSE;
982 pObj->abUnused[1] = K_FALSE;
983 pObj->fFlags = pParent->Obj.fFlags;
984 pObj->pParent = pParent;
985
986#ifdef KFSCACHE_CFG_UTF16
987 pObj->cwcParent = pParent->Obj.cwcParent + pParent->Obj.cwcName + !!pParent->Obj.cwcName;
988 pObj->pwszName = (wchar_t *)kHlpMemCopy(pbExtra, pwszName, cwcName * sizeof(wchar_t));
989 pbExtra += cwcName * sizeof(wchar_t);
990 *pbExtra++ = '\0';
991 *pbExtra++ = '\0';
992# ifdef KFSCACHE_CFG_SHORT_NAMES
993 pObj->cwcShortParent = pParent->Obj.cwcShortParent + pParent->Obj.cwcShortName + !!pParent->Obj.cwcShortName;
994 if (cwcShortName)
995 {
996 pObj->pwszShortName = (wchar_t *)kHlpMemCopy(pbExtra, pwszShortName, cwcShortName * sizeof(wchar_t));
997 pbExtra += cwcShortName * sizeof(wchar_t);
998 *pbExtra++ = '\0';
999 *pbExtra++ = '\0';
1000 }
1001 else
1002 {
1003 pObj->pwszShortName = pObj->pwszName;
1004 pObj->cwcShortName = pObj->cwcName;
1005 }
1006# endif
1007#endif
1008 pObj->cchParent = pParent->Obj.cchParent + pParent->Obj.cchName + !!pParent->Obj.cchName;
1009 pObj->pszName = (char *)kHlpMemCopy(pbExtra, pszName, cchName);
1010 pbExtra += cchName;
1011 *pbExtra++ = '\0';
1012# ifdef KFSCACHE_CFG_SHORT_NAMES
1013 pObj->cchShortParent = pParent->Obj.cchShortParent + pParent->Obj.cchShortName + !!pParent->Obj.cchShortName;
1014 if (cchShortName)
1015 {
1016 pObj->pszShortName = (char *)kHlpMemCopy(pbExtra, pszShortName, cchShortName);
1017 pbExtra += cchShortName;
1018 *pbExtra++ = '\0';
1019 }
1020 else
1021 {
1022 pObj->pszShortName = pObj->pszName;
1023 pObj->cchShortName = pObj->cchName;
1024 }
1025#endif
1026 kHlpAssert(pbExtra - (KU8 *)pObj == cbObj);
1027
1028 /*
1029 * Type specific initilization.
1030 */
1031 if (fDirish)
1032 {
1033 PKFSDIR pDirObj = (PKFSDIR)pObj;
1034 pDirObj->cChildren = 0;
1035 pDirObj->papChildren = NULL;
1036 pDirObj->cHashTab = 0;
1037 pDirObj->paHashTab = NULL;
1038 pDirObj->hDir = INVALID_HANDLE_VALUE;
1039 pDirObj->uDevNo = pParent->uDevNo;
1040 pDirObj->fPopulated = K_FALSE;
1041 }
1042 }
1043 else
1044 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
1045 return pObj;
1046}
1047
1048
1049/**
1050 * Creates a new object given wide char names.
1051 *
1052 * This function just converts the paths and calls kFsCacheCreateObject.
1053 *
1054 *
1055 * @returns Pointer (with 1 reference) to the new object. The object will not
1056 * be linked to the parent directory yet.
1057 *
1058 * NULL if we're out of memory.
1059 *
1060 * @param pCache The cache.
1061 * @param pParent The parent directory.
1062 * @param pszName The ANSI name.
1063 * @param cchName The length of the ANSI name.
1064 * @param pwszName The UTF-16 name.
1065 * @param cwcName The length of the UTF-16 name.
1066 * @param pwszShortName The UTF-16 short name, NULL if none.
1067 * @param cwcShortName The length of the UTF-16 short name, 0 if none.
1068 * @param bObjType The objct type.
1069 * @param penmError Where to explain failures.
1070 */
1071static PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t const *pwszName, KU32 cwcName,
1072#ifdef KFSCACHE_CFG_SHORT_NAMES
1073 wchar_t const *pwszShortName, KU32 cwcShortName,
1074#endif
1075 KU8 bObjType, KFSLOOKUPERROR *penmError)
1076{
1077 /* Convert names to ANSI first so we know their lengths. */
1078 char szName[KFSCACHE_CFG_MAX_ANSI_NAME];
1079 int cchName = WideCharToMultiByte(CP_ACP, 0, pwszName, cwcName, szName, sizeof(szName) - 1, NULL, NULL);
1080 if (cchName >= 0)
1081 {
1082#ifdef KFSCACHE_CFG_SHORT_NAMES
1083 char szShortName[12*3 + 1];
1084 int cchShortName = 0;
1085 if ( cwcShortName == 0
1086 || (cchShortName = WideCharToMultiByte(CP_ACP, 0, pwszShortName, cwcShortName,
1087 szShortName, sizeof(szShortName) - 1, NULL, NULL)) > 0)
1088#endif
1089 {
1090 return kFsCacheCreateObject(pCache, pParent,
1091 szName, cchName, pwszName, cwcName,
1092#ifdef KFSCACHE_CFG_SHORT_NAMES
1093 szShortName, cchShortName, pwszShortName, cwcShortName,
1094#endif
1095 bObjType, penmError);
1096 }
1097 }
1098 *penmError = KFSLOOKUPERROR_ANSI_CONVERSION_ERROR;
1099 return NULL;
1100}
1101
1102
1103/**
1104 * Creates a missing object.
1105 *
1106 * This is used for caching negative results.
1107 *
1108 * @returns Pointer to the newly created object on success (already linked into
1109 * pParent). No reference.
1110 *
1111 * NULL on failure.
1112 *
1113 * @param pCache The cache.
1114 * @param pParent The parent directory.
1115 * @param pchName The name.
1116 * @param cchName The length of the name.
1117 * @param penmError Where to return failure explanations.
1118 */
1119static PKFSOBJ kFsCacheCreateMissingA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName,
1120 KFSLOOKUPERROR *penmError)
1121{
1122 /*
1123 * Just convert the name to UTF-16 and call kFsCacheCreateObject to do the job.
1124 */
1125 wchar_t wszName[KFSCACHE_CFG_MAX_PATH];
1126 int cwcName = MultiByteToWideChar(CP_ACP, 0, pchName, cchName, wszName, KFSCACHE_CFG_MAX_UTF16_NAME - 1);
1127 if (cwcName > 0)
1128 {
1129 /** @todo check that it actually doesn't exists before we add it. We should not
1130 * trust the directory enumeration here, or maybe we should?? */
1131
1132 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, pParent, pchName, cchName, wszName, cwcName,
1133#ifdef KFSCACHE_CFG_SHORT_NAMES
1134 NULL, 0, NULL, 0,
1135#endif
1136 KFSOBJ_TYPE_MISSING, penmError);
1137 if (pMissing)
1138 {
1139 KBOOL fRc = kFsCacheDirAddChild(pCache, pParent, pMissing, penmError);
1140 kFsCacheObjRelease(pCache, pMissing);
1141 return fRc ? pMissing : NULL;
1142 }
1143 return NULL;
1144 }
1145 *penmError = KFSLOOKUPERROR_UTF16_CONVERSION_ERROR;
1146 return NULL;
1147}
1148
1149
1150/**
1151 * Does the initial directory populating or refreshes it if it has been
1152 * invalidated.
1153 *
1154 * This assumes the parent directory is opened.
1155 *
1156 * @returns K_TRUE on success, K_FALSE on error.
1157 * @param pCache The cache.
1158 * @param pDir The directory.
1159 * @param penmError Where to store K_FALSE explanation.
1160 */
1161static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
1162{
1163 KBOOL fRefreshing = K_FALSE;
1164 /** @todo will have to make this more flexible wrt information classes since
1165 * older windows versions (XP, w2K) might not correctly support the
1166 * ones with file ID on all file systems. */
1167#ifdef KFSCACHE_CFG_SHORT_NAMES
1168 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
1169#else
1170 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
1171#endif
1172 MY_NTSTATUS rcNt;
1173 MY_IO_STATUS_BLOCK Ios;
1174 union
1175 {
1176 /* Include the structures for better alignment. */
1177 MY_FILE_ID_BOTH_DIR_INFORMATION WithId;
1178 MY_FILE_ID_FULL_DIR_INFORMATION NoId;
1179 /* Buffer padding. We're using a 56KB buffer here to avoid size troubles with CIFS and such. */
1180 KU8 abBuf[56*1024];
1181 } uBuf;
1182
1183 /*
1184 * Open the directory.
1185 */
1186 if (pDir->hDir == INVALID_HANDLE_VALUE)
1187 {
1188 MY_OBJECT_ATTRIBUTES ObjAttr;
1189 MY_UNICODE_STRING UniStr;
1190
1191 kHlpAssert(!pDir->fPopulated);
1192
1193 Ios.Information = -1;
1194 Ios.u.Status = -1;
1195
1196 UniStr.Buffer = (wchar_t *)pDir->Obj.pwszName;
1197 UniStr.Length = (USHORT)(pDir->Obj.cwcName * sizeof(wchar_t));
1198 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1199
1200 kHlpAssertStmtReturn(pDir->Obj.pParent, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
1201 kHlpAssertStmtReturn(pDir->Obj.pParent->hDir != INVALID_HANDLE_VALUE, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
1202 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pDir->Obj.pParent->hDir, NULL /*pSecAttr*/);
1203
1204 /** @todo FILE_OPEN_REPARSE_POINT? */
1205 rcNt = g_pfnNtCreateFile(&pDir->hDir,
1206 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1207 &ObjAttr,
1208 &Ios,
1209 NULL, /*cbFileInitialAlloc */
1210 FILE_ATTRIBUTE_NORMAL,
1211 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1212 FILE_OPEN,
1213 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1214 NULL, /*pEaBuffer*/
1215 0); /*cbEaBuffer*/
1216 if (MY_NT_SUCCESS(rcNt))
1217 { /* likely */ }
1218 else
1219 {
1220 pDir->hDir = INVALID_HANDLE_VALUE;
1221 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR;
1222 return K_FALSE;
1223 }
1224 }
1225 else if (pDir->fPopulated)
1226 {
1227 /** @todo refreshing directories. */
1228 __debugbreak();
1229 fRefreshing = K_TRUE;
1230 }
1231
1232
1233 /*
1234 * Enumerate the directory content.
1235 */
1236 Ios.Information = -1;
1237 Ios.u.Status = -1;
1238 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1239 NULL, /* hEvent */
1240 NULL, /* pfnApcComplete */
1241 NULL, /* pvApcCompleteCtx */
1242 &Ios,
1243 &uBuf,
1244 sizeof(uBuf),
1245 enmInfoClass,
1246 FALSE, /* fReturnSingleEntry */
1247 NULL, /* Filter / restart pos. */
1248 TRUE); /* fRestartScan */
1249 while (MY_NT_SUCCESS(rcNt))
1250 {
1251 /*
1252 * Process the entries in the buffer.
1253 */
1254 KSIZE offBuf = 0;
1255 for (;;)
1256 {
1257 union
1258 {
1259 KU8 *pb;
1260#ifdef KFSCACHE_CFG_SHORT_NAMES
1261 MY_FILE_ID_BOTH_DIR_INFORMATION *pWithId;
1262 MY_FILE_BOTH_DIR_INFORMATION *pNoId;
1263#else
1264 MY_FILE_ID_FULL_DIR_INFORMATION *pWithId;
1265 MY_FILE_FULL_DIR_INFORMATION *pNoId;
1266#endif
1267 } uPtr;
1268 PKFSOBJ pCur;
1269 KU32 offNext;
1270 KU32 cbMinCur;
1271 wchar_t *pwszFilename;
1272
1273 /* ASSUME only the FileName member differs between the two structures. */
1274 uPtr.pb = &uBuf.abBuf[offBuf];
1275#ifdef KFSCACHE_CFG_SHORT_NAMES
1276 pwszFilename = enmInfoClass == MyFileIdBothDirectoryInformation
1277 ? &uPtr.pWithId->FileName[0] : &uPtr.pNoId->FileName[0];
1278#else
1279 pwszFilename = enmInfoClass == MyFileIdFullDirectoryInformation
1280 ? &uPtr.pWithId->FileName[0] : &uPtr.pNoId->FileName[0];
1281#endif
1282 /*
1283 * Create the entry (not linked yet).
1284 */
1285 pCur = kFsCacheCreateObjectW(pCache, pDir, pwszFilename, uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1286#ifdef KFSCACHE_CFG_SHORT_NAMES
1287 uPtr.pNoId->ShortName, uPtr.pNoId->ShortNameLength / sizeof(wchar_t),
1288#endif
1289 uPtr.pNoId->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR
1290 : uPtr.pNoId->FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)
1291 ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE,
1292 penmError);
1293 if (!pCur)
1294 return K_FALSE;
1295 kHlpAssert(pCur->cRefs == 1);
1296
1297#ifdef KFSCACHE_CFG_SHORT_NAMES
1298 if (enmInfoClass == MyFileIdBothDirectoryInformation)
1299 birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
1300 else
1301 birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
1302#else
1303 if (enmInfoClass == MyFileIdBothDirectoryInformation)
1304 birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
1305 else
1306 birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
1307#endif
1308 pCur->Stats.st_dev = pDir->uDevNo;
1309
1310 /*
1311 * If we're updating we have to check the data.
1312 */
1313 if (fRefreshing)
1314 {
1315 __debugbreak();
1316 }
1317
1318 /*
1319 * If we've still got pCur, add it to the directory.
1320 */
1321 if (pCur)
1322 {
1323 KBOOL fRc = kFsCacheDirAddChild(pCache, pDir, pCur, penmError);
1324 kFsCacheObjRelease(pCache, pCur);
1325 if (fRc)
1326 { /* likely */ }
1327 else
1328 return K_FALSE;
1329 }
1330
1331 /*
1332 * Advance.
1333 */
1334 offNext = uPtr.pNoId->NextEntryOffset;
1335 if ( offNext >= cbMinCur
1336 && offNext < sizeof(uBuf))
1337 offBuf += offNext;
1338 else
1339 break;
1340 }
1341
1342 /*
1343 * Read the next chunk.
1344 */
1345 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1346 NULL, /* hEvent */
1347 NULL, /* pfnApcComplete */
1348 NULL, /* pvApcCompleteCtx */
1349 &Ios,
1350 &uBuf,
1351 sizeof(uBuf),
1352 enmInfoClass,
1353 FALSE, /* fReturnSingleEntry */
1354 NULL, /* Filter / restart pos. */
1355 FALSE); /* fRestartScan */
1356 }
1357
1358 if (rcNt == MY_STATUS_NO_MORE_FILES)
1359 return K_TRUE;
1360 kHlpAssertMsgFailed(("%#x\n", rcNt));
1361 *penmError = KFSLOOKUPERROR_DIR_READ_ERROR;
1362 return K_TRUE;
1363}
1364
1365
1366static KBOOL kFsCacheRefreshMissing(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1367{
1368 return K_TRUE;
1369}
1370
1371
1372static KBOOL kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1373{
1374 return K_TRUE;
1375}
1376
1377
1378static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError)
1379{
1380 return K_FALSE;
1381}
1382
1383
1384/**
1385 * Refreshes a path hash that has expired.
1386 *
1387 * @returns pHash on success, NULL if removed.
1388 * @param pCache The cache.
1389 * @param pHashEntry The path hash.
1390 * @param idxHashTab The hash table entry.
1391 */
1392static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
1393{
1394 /** @todo implement once we've start inserting uCacheGen nodes. */
1395 __debugbreak();
1396 K_NOREF(pCache);
1397 K_NOREF(idxHashTab);
1398 return pHashEntry;
1399}
1400
1401
1402/**
1403 * Look up a child node, ANSI version.
1404 *
1405 * @returns Pointer to the child if found, NULL if not.
1406 * @param pCache The cache.
1407 * @param pParent The parent directory to search.
1408 * @param pchName The child name to search for (not terminated).
1409 * @param cchName The length of the child name.
1410 */
1411static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
1412{
1413 /* Check for '.' first. */
1414 if (cchName != 1 || *pchName != '.')
1415 {
1416 KU32 cLeft;
1417 PKFSOBJ *ppCur;
1418
1419 if (pParent->paHashTab != NULL)
1420 {
1421 /** @todo directory hash table lookup. */
1422 }
1423
1424 /* Linear search. */
1425 cLeft = pParent->cChildren;
1426 ppCur = pParent->papChildren;
1427 while (cLeft-- > 0)
1428 {
1429 PKFSOBJ pCur = *ppCur++;
1430 if ( ( pCur->cchName == cchName
1431 && _memicmp(pCur->pszName, pchName, cchName) == 0)
1432#ifdef KFSCACHE_CFG_SHORT_NAMES
1433 || ( pCur->cchShortName == cchName
1434 && pCur->pszShortName != pCur->pszName
1435 && _memicmp(pCur->pszName, pchName, cchName) == 0)
1436#endif
1437 )
1438 return pCur;
1439 }
1440 return NULL;
1441 }
1442 return &pParent->Obj;
1443}
1444
1445
1446/**
1447 * Looks up a UNC share, ANSI version.
1448 *
1449 * We keep both the server and share in the root directory entry. This means we
1450 * have to clean up the entry name before we can insert it.
1451 *
1452 * @returns Pointer to the share root directory or an update-to-date missing
1453 * node.
1454 * @param pCache The cache.
1455 * @param pszPath The path.
1456 * @param poff Where to return the root dire.
1457 * @param penmError Where to return details as to why the lookup
1458 * failed.
1459 */
1460static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
1461{
1462#if 0 /* later */
1463 KU32 offStartServer;
1464 KU32 offEndServer;
1465 KU32 offStartShare;
1466
1467 KU32 offEnd = 2;
1468 while (IS_SLASH(pszPath[offEnd]))
1469 offEnd++;
1470
1471 offStartServer = offEnd;
1472 while ( (ch = pszPath[offEnd]) != '\0'
1473 && !IS_SLASH(ch))
1474 offEnd++;
1475 offEndServer = offEnd;
1476
1477 if (ch != '\0')
1478 { /* likely */ }
1479 else
1480 {
1481 *penmError = KFSLOOKUPERROR_NOT_FOUND;
1482 return NULL;
1483 }
1484
1485 while (IS_SLASH(pszPath[offEnd]))
1486 offEnd++;
1487 offStartServer = offEnd;
1488 while ( (ch = pszPath[offEnd]) != '\0'
1489 && !IS_SLASH(ch))
1490 offEnd++;
1491#endif
1492 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
1493 return NULL;
1494}
1495
1496
1497/**
1498 * Looks up a drive letter.
1499 *
1500 * Will enter the drive if necessary.
1501 *
1502 * @returns Pointer to the root directory of the drive or an update-to-date
1503 * missing node.
1504 * @param pCache The cache.
1505 * @param chLetter The uppercased drive letter.
1506 * @param penmError Where to return details as to why the lookup
1507 * failed.
1508 */
1509static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError)
1510{
1511 KU32 const uHash = chLetter - 'A';
1512 KU32 cLeft;
1513 PKFSOBJ *ppCur;
1514
1515 MY_UNICODE_STRING NtPath;
1516 wchar_t wszTmp[8];
1517 MY_NTSTATUS rcNt;
1518 char szTmp[4];
1519
1520 /*
1521 * Custom drive letter hashing.
1522 */
1523 if (pCache->RootDir.paHashTab)
1524 {
1525 /** @todo PKFSOBJHASH pHash = */
1526 }
1527
1528 /*
1529 * Special cased lookup.
1530 */
1531 cLeft = pCache->RootDir.cChildren;
1532 ppCur = pCache->RootDir.papChildren;
1533 while (cLeft-- > 0)
1534 {
1535 PKFSOBJ pCur = *ppCur++;
1536 if ( pCur->cchName == 2
1537 && pCur->pszName[0] == chLetter
1538 && pCur->pszName[1] == ':')
1539 {
1540 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
1541 return pCur;
1542 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
1543 if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
1544 return pCur;
1545 return NULL;
1546 }
1547 }
1548
1549 /*
1550 * Need to add it. We always keep the drive letters open for the benefit
1551 * of kFsCachePopuplateOrRefreshDir and others.
1552 */
1553 wszTmp[0] = szTmp[0] = chLetter;
1554 wszTmp[1] = szTmp[1] = ':';
1555 wszTmp[2] = szTmp[2] = '\\';
1556 wszTmp[3] = '.';
1557 wszTmp[4] = '\0';
1558 szTmp[2] = '\0';
1559
1560 NtPath.Buffer = NULL;
1561 NtPath.Length = 0;
1562 NtPath.MaximumLength = 0;
1563 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
1564 {
1565 HANDLE hDir;
1566 rcNt = birdOpenFileUniStr(&NtPath,
1567 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1568 FILE_ATTRIBUTE_NORMAL,
1569 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1570 FILE_OPEN,
1571 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1572 OBJ_CASE_INSENSITIVE,
1573 &hDir);
1574 birdFreeNtPath(&NtPath);
1575 if (MY_NT_SUCCESS(rcNt))
1576 {
1577 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1578#ifdef KFSCACHE_CFG_SHORT_NAMES
1579 NULL, 0, NULL, 0,
1580#endif
1581 KFSOBJ_TYPE_DIR, penmError);
1582 if (pDir)
1583 {
1584 /*
1585 * We need a little bit of extra info for a drive root. These things are typically
1586 * inherited by subdirectories down the tree, so, we do it all here for till that changes.
1587 */
1588 union
1589 {
1590 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
1591 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
1592 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
1593 } uBuf;
1594 MY_IO_STATUS_BLOCK Ios;
1595 KBOOL fRc;
1596
1597 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
1598 pDir->hDir = hDir;
1599
1600 /* Get the device number. */
1601 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
1602 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
1603
1604 /* Get the file system. */
1605 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
1606 Ios.Information = -1;
1607 Ios.u.Status = -1;
1608 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
1609 MyFileFsAttributeInformation);
1610 if (MY_NT_SUCCESS(rcNt))
1611 rcNt = Ios.u.Status;
1612 if (MY_NT_SUCCESS(rcNt))
1613 {
1614 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N'
1615 && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
1616 && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
1617 && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
1618 && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
1619 {
1620 DWORD dwDriveType = GetDriveTypeW(wszTmp);
1621 if ( dwDriveType == DRIVE_FIXED
1622 || dwDriveType == DRIVE_RAMDISK)
1623 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
1624 }
1625 }
1626
1627 /*
1628 * Link the new drive letter into the root dir.
1629 */
1630 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
1631 kFsCacheObjRelease(pCache, &pDir->Obj);
1632 return fRc ? &pDir->Obj : NULL;
1633 }
1634
1635 g_pfnNtClose(hDir);
1636 return NULL;
1637 }
1638
1639 /* Assume it doesn't exist if this happens... This may be a little to
1640 restrictive wrt status code checks. */
1641 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
1642 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
1643 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
1644 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
1645 ("%#x\n", rcNt),
1646 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
1647 NULL);
1648 }
1649 else
1650 {
1651 kHlpAssertFailed();
1652 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
1653 return NULL;
1654 }
1655
1656 /*
1657 * Maybe create a missing entry.
1658 */
1659 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1660 {
1661 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1662#ifdef KFSCACHE_CFG_SHORT_NAMES
1663 NULL, 0, NULL, 0,
1664#endif
1665 KFSOBJ_TYPE_MISSING, penmError);
1666 if (pMissing)
1667 {
1668 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
1669 kFsCacheObjRelease(pCache, pMissing);
1670 return fRc ? pMissing : NULL;
1671 }
1672 }
1673 else
1674 {
1675 /** @todo this isn't necessary correct for a root spec. */
1676 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1677 }
1678 return NULL;
1679}
1680
1681
1682/**
1683 * Walk the file system tree for the given absolute path, entering it into the
1684 * hash table.
1685 *
1686 * This will create any missing nodes while walking.
1687 *
1688 * The caller will have to do the path hash table insertion of the result.
1689 *
1690 * @returns Pointer to the tree node corresponding to @a pszPath.
1691 * NULL on lookup failure, see @a penmError for details.
1692 * @param pCache The cache.
1693 * @param pszPath The path to walk.
1694 * @param cchPath The length of the path.
1695 * @param penmError Where to return details as to why the lookup
1696 * failed.
1697 */
1698static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
1699{
1700 PKFSDIR pParent = &pCache->RootDir;
1701 PKFSOBJ pChild;
1702 KU32 off;
1703 char ch;
1704 KU32 cchSlashes;
1705 KU32 offEnd;
1706
1707 KFSCACHE_LOG(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
1708
1709 /*
1710 * The root "directory" needs special handling, so we keep it outside the
1711 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
1712 */
1713 cchSlashes = 0;
1714 off = 0;
1715 if ( pszPath[1] == ':'
1716 && IS_ALPHA(pszPath[0]))
1717 {
1718 /* Drive letter. */
1719 offEnd = 2;
1720 kHlpAssert(IS_SLASH(pszPath[2]));
1721 pChild = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);
1722 }
1723 else if ( IS_SLASH(pszPath[0])
1724 && IS_SLASH(pszPath[1]) )
1725 pChild = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);
1726 else
1727 {
1728 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
1729 return NULL;
1730 }
1731 if (pChild)
1732 { /* likely */ }
1733 else
1734 return NULL;
1735
1736 /* Count slashes trailing the root spec. */
1737 if (offEnd < cchPath)
1738 {
1739 kHlpAssert(IS_SLASH(pszPath[offEnd]));
1740 do
1741 cchSlashes++;
1742 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
1743 }
1744
1745 /* Done already? */
1746 if (offEnd >= cchPath)
1747 {
1748 if ( pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE
1749 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
1750 || kFsCacheRefreshObj(pCache, pChild, penmError))
1751 return kFsCacheObjRetainInternal(pChild);
1752 return NULL;
1753 }
1754
1755 /* Check that we've got a valid result and not a cached negative one. */
1756 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
1757 { /* likely */ }
1758 else
1759 {
1760 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
1761 kHlpAssert(pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
1762 return pChild;
1763 }
1764
1765 /* Next component. */
1766 pParent = (PKFSDIR)pChild;
1767 off = offEnd + cchSlashes;
1768
1769
1770 /*
1771 * Walk loop.
1772 */
1773 for (;;)
1774 {
1775 /*
1776 * Find the end of the component, counting trailing slashes.
1777 */
1778 cchSlashes = 0;
1779 offEnd = off + 1;
1780 while ((ch = pszPath[offEnd]) != '\0')
1781 {
1782 if (!IS_SLASH(ch))
1783 offEnd++;
1784 else
1785 {
1786 do
1787 cchSlashes++;
1788 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
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 == KFSWOBJ_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 = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
1812 if (pChild != NULL)
1813 { /* probably likely */ }
1814 else
1815 {
1816 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1817 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
1818 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
1819 {
1820 if (pChild)
1821 return 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 + cchSlashes;
1831 if ( cchSlashes == 0
1832 || off >= cchPath)
1833 {
1834 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
1835 || pChild->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE
1836 || pChild->uCacheGen == pCache->uGenerationMissing
1837 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
1838 { /* likely */ }
1839 else
1840 return NULL;
1841 return 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 == KFSWOBJ_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 * This deals with paths that are relative and paths that contains '..'
1873 * elements.
1874 *
1875 * @returns Pointer to object corresponding to @a pszPath on success.
1876 * NULL if this isn't a path we care to cache.
1877 *
1878 * @param pCache The cache.
1879 * @param pszPath The path.
1880 * @param cchPath The length of the path.
1881 * @param penmError Where to return details as to why the lookup
1882 * failed.
1883 */
1884static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
1885{
1886 /*
1887 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
1888 * ends up calling it anyway.
1889 */
1890 char szFull[KFSCACHE_CFG_MAX_PATH];
1891 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
1892 if ( cchFull >= 3
1893 && cchFull < sizeof(szFull))
1894 {
1895 PKFSOBJ pFsObj;
1896 KFSCACHE_LOG(("kFsCacheLookupSlowA(%s)\n", pszPath));
1897 pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError);
1898
1899#if 0 /* No need to do this until it's actually queried. */
1900 /* Cache the resulting path. */
1901 if ( pFsObj
1902 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
1903 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
1904 {
1905 KU32 uHashPath = kFsCacheStrHash(szFull);
1906 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
1907 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
1908 }
1909#endif
1910 return pFsObj;
1911 }
1912
1913 /* The path is too long! */
1914 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
1915 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
1916 return NULL;
1917}
1918
1919
1920/**
1921 * Looks up a KFSOBJ for the given ANSI path.
1922 *
1923 * This will first try the hash table. If not in the hash table, the file
1924 * system cache tree is walked, missing bits filled in and finally a hash table
1925 * entry is created.
1926 *
1927 * Only drive letter paths are cachable. We don't do any UNC paths at this
1928 * point.
1929 *
1930 * @returns Reference to object corresponding to @a pszPath on success, this
1931 * must be released by kwFsCacheRelease.
1932 * NULL if not a path we care to cache.
1933 * @param pCache The cache.
1934 * @param pszPath The path to lookup.
1935 * @param penmError Where to return details as to why the lookup
1936 * failed.
1937 */
1938static PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
1939{
1940 /*
1941 * Do hash table lookup of the path.
1942 */
1943 KU32 uHashPath;
1944 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
1945 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
1946 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab];
1947 if (pHashEntry)
1948 {
1949 do
1950 {
1951 if ( pHashEntry->uHashPath == uHashPath
1952 && pHashEntry->cchPath == cchPath
1953 && kHlpMemComp(pHashEntry->pszPath, pszPath, cchPath) == 0)
1954 {
1955 if ( pHashEntry->uCacheGen == KFSWOBJ_CACHE_GEN_IGNORE
1956 || pHashEntry->uCacheGen == pCache->uGeneration
1957 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
1958 {
1959 pCache->cLookups++;
1960 pCache->cPathHashHits++;
1961 KFSCACHE_LOG(("kFsCacheLookupA(%s) - hit %p\n", pszPath, pHashEntry->pFsObj));
1962 *penmError = pHashEntry->enmError;
1963 if (pHashEntry->pFsObj)
1964 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
1965 return NULL;
1966 }
1967 break;
1968 }
1969 pHashEntry = pHashEntry->pNext;
1970 } while (pHashEntry);
1971 }
1972
1973 /*
1974 * Create an entry for it by walking the file system cache and filling in the blanks.
1975 */
1976 if ( cchPath > 0
1977 && cchPath < KFSCACHE_CFG_MAX_PATH)
1978 {
1979 PKFSOBJ pFsObj;
1980
1981 /* Is absolute without any '..' bits? */
1982 if ( cchPath >= 3
1983 && ( ( pszPath[1] == ':' /* Drive letter */
1984 && IS_SLASH(pszPath[2])
1985 && IS_ALPHA(pszPath[0]) )
1986 || ( IS_SLASH(pszPath[0]) /* UNC */
1987 && IS_SLASH(pszPath[1]) ) )
1988 && !kFsCacheHasDotDot(pszPath, cchPath) )
1989 pFsObj = kFsCacheLookupAbsoluteA(pCache, pszPath, cchPath, penmError);
1990 else
1991 pFsObj = kFsCacheLookupSlowA(pCache, pszPath, cchPath, penmError);
1992 if ( pFsObj
1993 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
1994 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
1995 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
1996 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath, idxHashTab, *penmError);
1997
1998 pCache->cLookups++;
1999 if (pFsObj)
2000 pCache->cWalkHits++;
2001 return pFsObj;
2002 }
2003
2004 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2005 return NULL;
2006}
2007
2008
2009/**
2010 * Destroys a cache object which has a zero reference count.
2011 *
2012 * @returns 0
2013 * @param pCache The cache.
2014 * @param pObj The object.
2015 */
2016KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
2017{
2018 kHlpAssert(pObj->cRefs == 0);
2019 kHlpAssert(pObj->pParent == NULL);
2020
2021 switch (pObj->bObjType)
2022 {
2023 case KFSOBJ_TYPE_MISSING:
2024 //case KFSOBJ_TYPE_MISSING | KFSOBJ_TYPE_F_INVALID:
2025 /* nothing else to do here */
2026 pCache->cbObjects -= sizeof(*pObj);
2027 break;
2028
2029 case KFSOBJ_TYPE_DIR:
2030 //case KFSOBJ_TYPE_DIR | KFSOBJ_TYPE_F_INVALID:
2031 case KFSOBJ_TYPE_FILE:
2032 //case KFSOBJ_TYPE_FILE | KFSOBJ_TYPE_F_INVALID:
2033 kHlpAssertFailed();
2034 break;
2035 default:
2036 kHlpAssertFailed();
2037 }
2038 pCache->cObjects--;
2039 free(pObj);
2040 return 0;
2041}
2042
2043
2044/**
2045 * Releases a reference to a cache object.
2046 *
2047 * @returns New reference count.
2048 * @param pCache The cache.
2049 * @param pObj The object.
2050 */
2051KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
2052{
2053 KU32 cRefs = --pObj->cRefs;
2054 if (cRefs)
2055 return cRefs;
2056 return kFsCacheObjDestroy(pCache, pObj);
2057}
2058
2059
2060/**
2061 * Retains a reference to a cahce object.
2062 *
2063 * @returns New reference count.
2064 * @param pObj The object.
2065 */
2066KU32 kFsCacheObjRetain(PKFSOBJ pObj)
2067{
2068 KU32 cRefs = ++pObj->cRefs;
2069 kHlpAssert(cRefs < 16384);
2070 return cRefs;
2071}
2072
2073
2074
2075PKFSCACHE kFsCacheCreate(KU32 fFlags)
2076{
2077 PKFSCACHE pCache;
2078 birdResolveImports();
2079
2080 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
2081 if (pCache)
2082 {
2083 /* Dummy root dir entry. */
2084 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC;
2085 pCache->RootDir.Obj.cRefs = 1;
2086 pCache->RootDir.Obj.uCacheGen = KFSWOBJ_CACHE_GEN_IGNORE;
2087 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR;
2088 pCache->RootDir.Obj.fHaveStats = K_FALSE;
2089 pCache->RootDir.Obj.pParent = NULL;
2090 pCache->RootDir.Obj.pszName = "";
2091 pCache->RootDir.Obj.cchName = 0;
2092 pCache->RootDir.Obj.cchParent = 0;
2093#ifdef KFSCACHE_CFG_UTF16
2094 pCache->RootDir.Obj.cwcName = 0;
2095 pCache->RootDir.Obj.cwcParent = 0;
2096 pCache->RootDir.Obj.pwszName = L"";
2097#endif
2098
2099#ifdef KFSCACHE_CFG_SHORT_NAMES
2100 pCache->RootDir.Obj.pszShortName = NULL;
2101 pCache->RootDir.Obj.cchShortName = 0;
2102 pCache->RootDir.Obj.cchShortParent = 0;
2103# ifdef KFSCACHE_CFG_UTF16
2104 pCache->RootDir.Obj.cwcShortName;
2105 pCache->RootDir.Obj.cwcShortParent;
2106 pCache->RootDir.Obj.pwszShortName;
2107# endif
2108#endif
2109 pCache->RootDir.cChildren = 0;
2110 pCache->RootDir.papChildren = NULL;
2111 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
2112 pCache->RootDir.cHashTab = 251;
2113 pCache->RootDir.paHashTab = (PKFSOBJHASH)kHlpAllocZ( pCache->RootDir.cHashTab
2114 * sizeof(pCache->RootDir.paHashTab[0]));
2115 if (pCache->RootDir.paHashTab)
2116 {
2117 /* The cache itself. */
2118 pCache->u32Magic = KFSCACHE_MAGIC;
2119 pCache->fFlags = fFlags;
2120 pCache->uGeneration = 1;
2121 pCache->cObjects = 1;
2122 pCache->cbObjects = sizeof(pCache->RootDir) + pCache->RootDir.cHashTab * sizeof(pCache->RootDir.paHashTab[0]);
2123 pCache->cPathHashHits = 0;
2124 pCache->cWalkHits = 0;
2125 pCache->cAnsiPaths = 0;
2126 pCache->cAnsiPathCollisions = 0;
2127 pCache->cbAnsiPaths = 0;
2128#ifdef KFSCACHE_CFG_UTF16
2129 pCache->cUtf16Paths = 0;
2130 pCache->cUtf16PathCollisions = 0;
2131 pCache->cbUtf16Paths = 0;
2132#endif
2133 return pCache;
2134 }
2135
2136 kHlpFree(pCache);
2137 }
2138 return NULL;
2139}
2140
Note: See TracBrowser for help on using the repository browser.