VirtualBox

source: kBuild/trunk/src/lib/nt/kFsCache.c@ 2864

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

updates

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette