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

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

updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 132.7 KB
Line 
1/* $Id: kFsCache.c 2863 2016-09-02 16:32: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->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1457 || pDir->Obj.uCacheGen == pCache->uGeneration) )
1458 return K_TRUE;
1459 return kFsCachePopuplateOrRefreshDir(pCache, pDir, penmError ? penmError : &enmIgnored);
1460}
1461
1462
1463/**
1464 * Checks whether the modified timestamp differs on this directory.
1465 *
1466 * @returns K_TRUE if possibly modified, K_FALSE if definitely not modified.
1467 * @param pDir The directory..
1468 */
1469static KBOOL kFsCacheDirIsModified(PKFSDIR pDir)
1470{
1471 if ( pDir->hDir != INVALID_HANDLE_VALUE
1472 && (pDir->Obj.fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
1473 {
1474 MY_IO_STATUS_BLOCK Ios;
1475 MY_FILE_BASIC_INFORMATION BasicInfo;
1476 MY_NTSTATUS rcNt;
1477
1478 Ios.Information = -1;
1479 Ios.u.Status = -1;
1480
1481 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
1482 if (MY_NT_SUCCESS(rcNt))
1483 return BasicInfo.LastWriteTime.QuadPart != pDir->iLastWrite;
1484 }
1485 /* The cache root never changes. */
1486 else if (!pDir->Obj.pParent)
1487 return K_FALSE;
1488
1489 return K_TRUE;
1490}
1491
1492
1493static KBOOL kFsCacheRefreshMissing(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1494{
1495 /*
1496 * If we can, we start by checking whether the parent directory
1497 * has been modified. If it has, we need to check if this entry
1498 * was added or not, most likely it wasn't added.
1499 */
1500 if (!kFsCacheDirIsModified(pMissing->pParent))
1501 {
1502 KFSCACHE_LOG(("Parent of missing not written to %s/%s\n", pMissing->pParent->Obj.pszName, pMissing->pszName));
1503 pMissing->uCacheGen = pCache->uGenerationMissing;
1504 }
1505 else
1506 {
1507 MY_UNICODE_STRING UniStr;
1508 MY_OBJECT_ATTRIBUTES ObjAttr;
1509 MY_FILE_BASIC_INFORMATION BasicInfo;
1510 MY_NTSTATUS rcNt;
1511
1512 UniStr.Buffer = (wchar_t *)pMissing->pwszName;
1513 UniStr.Length = (USHORT)(pMissing->cwcName * sizeof(wchar_t));
1514 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1515
1516 kHlpAssert(pMissing->pParent->hDir != INVALID_HANDLE_VALUE);
1517 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pMissing->pParent->hDir, NULL /*pSecAttr*/);
1518
1519 rcNt = g_pfnNtQueryAttributesFile(&ObjAttr, &BasicInfo);
1520 if (!MY_NT_SUCCESS(rcNt))
1521 {
1522 /*
1523 * Probably more likely that a missing node stays missing.
1524 */
1525 pMissing->uCacheGen = pCache->uGenerationMissing;
1526 KFSCACHE_LOG(("Still missing %s/%s\n", pMissing->pParent->Obj.pszName, pMissing->pszName));
1527 }
1528 else
1529 {
1530 /*
1531 * We must metamorphose this node. This is tedious business
1532 * because we need to check the file name casing. We might
1533 * just as well update the parent directory...
1534 */
1535 KFSCACHE_LOG(("Birth of %s/%s with attribs %#x...\n",
1536 pMissing->pParent->Obj.pszName, pMissing->pszName, BasicInfo.FileAttributes));
1537 /** @todo */
1538 __debugbreak();
1539 }
1540 }
1541
1542 return K_TRUE;
1543}
1544
1545
1546static KBOOL kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1547{
1548 if (kFsCacheRefreshMissing(pCache, pMissing, penmError))
1549 {
1550 if ( pMissing->bObjType == KFSOBJ_TYPE_DIR
1551 || pMissing->bObjType == KFSOBJ_TYPE_MISSING)
1552 return K_TRUE;
1553 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1554 }
1555
1556 return K_FALSE;
1557}
1558
1559
1560/**
1561 * Generic object refresh.
1562 *
1563 * This does not refresh the content of directories.
1564 *
1565 * @returns K_TRUE on success. K_FALSE and *penmError on failure.
1566 * @param pCache The cache.
1567 * @param pObj The object.
1568 * @param penmError Where to return error info.
1569 */
1570static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError)
1571{
1572 KBOOL fRc;
1573
1574 /*
1575 * Since we generally assume nothing goes away in this cache, we only really
1576 * have a hard time with negative entries. So, missing stuff goes to
1577 * complicated land.
1578 */
1579 if (pObj->bObjType == KFSOBJ_TYPE_MISSING)
1580 fRc = kFsCacheRefreshMissing(pCache, pObj, penmError);
1581 else
1582 {
1583 /*
1584 * This object is supposed to exist, so all we need to do is query essential
1585 * stats again. Since we've already got handles on directories, there are
1586 * two ways to go about this.
1587 */
1588 union
1589 {
1590 MY_FILE_NETWORK_OPEN_INFORMATION FullInfo;
1591 MY_FILE_STANDARD_INFORMATION StdInfo;
1592#ifdef KFSCACHE_CFG_SHORT_NAMES
1593 MY_FILE_ID_BOTH_DIR_INFORMATION WithId;
1594 //MY_FILE_BOTH_DIR_INFORMATION NoId;
1595#else
1596 MY_FILE_ID_FULL_DIR_INFORMATION WithId;
1597 //MY_FILE_FULL_DIR_INFORMATION NoId;
1598#endif
1599 KU8 abPadding[ sizeof(wchar_t) * KFSCACHE_CFG_MAX_UTF16_NAME
1600 + sizeof(MY_FILE_ID_BOTH_DIR_INFORMATION)];
1601 } uBuf;
1602 MY_IO_STATUS_BLOCK Ios;
1603 MY_NTSTATUS rcNt;
1604 if ( pObj->bObjType != KFSOBJ_TYPE_DIR
1605 || ((PKFSDIR)pObj)->hDir == INVALID_HANDLE_VALUE)
1606 {
1607#if 0 /** @todo performance check these two alternatives. */
1608 MY_UNICODE_STRING UniStr;
1609 MY_OBJECT_ATTRIBUTES ObjAttr;
1610
1611 UniStr.Buffer = (wchar_t *)pObj->pwszName;
1612 UniStr.Length = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1613 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1614
1615 kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1616 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pObj->pParent->hDir, NULL /*pSecAttr*/);
1617
1618 rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.FullInfo);
1619 if (MY_NT_SUCCESS(rcNt))
1620 {
1621 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
1622 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1623 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1624 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1625 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1626 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
1627 pObj->Stats.st_blksize = 65536;
1628 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1629 / BIRD_STAT_BLOCK_SIZE;
1630 }
1631#else
1632 /* This alternative lets us keep the inode number up to date and
1633 detect name case changes. */
1634 MY_UNICODE_STRING UniStr;
1635# ifdef KFSCACHE_CFG_SHORT_NAMES
1636 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
1637# else
1638 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
1639# endif
1640
1641 UniStr.Buffer = (wchar_t *)pObj->pwszName;
1642 UniStr.Length = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1643 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1644
1645 kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1646
1647 Ios.Information = -1;
1648 Ios.u.Status = -1;
1649 rcNt = g_pfnNtQueryDirectoryFile(pObj->pParent->hDir,
1650 NULL, /* hEvent */
1651 NULL, /* pfnApcComplete */
1652 NULL, /* pvApcCompleteCtx */
1653 &Ios,
1654 &uBuf,
1655 sizeof(uBuf),
1656 enmInfoClass,
1657 TRUE, /* fReturnSingleEntry */
1658 &UniStr, /* Filter / restart pos. */
1659 TRUE); /* fRestartScan */
1660
1661 if (MY_NT_SUCCESS(rcNt))
1662 {
1663 if (pObj->Stats.st_ino == uBuf.WithId.FileId.QuadPart)
1664 KFSCACHE_LOG(("Refreshing %s/%s, no ID change...\n", pObj->pParent->Obj.pszName, pObj->pszName));
1665 else if ( pObj->cwcName == uBuf.WithId.FileNameLength / sizeof(wchar_t)
1666# ifdef KFSCACHE_CFG_SHORT_NAMES
1667 && ( uBuf.WithId.ShortNameLength == 0
1668 ? pObj->pwszName == pObj->pwszShortName
1669 || ( pObj->cwcName == pObj->cwcShortName
1670 && memcmp(pObj->pwszName, pObj->pwszShortName, pObj->cwcName * sizeof(wchar_t)) == 0)
1671 : pObj->cwcShortName == uBuf.WithId.ShortNameLength / sizeof(wchar_t)
1672 && memcmp(pObj->pwszShortName, uBuf.WithId.ShortName, uBuf.WithId.ShortNameLength) == 0
1673 )
1674# endif
1675 && memcmp(pObj->pwszName, uBuf.WithId.FileName, uBuf.WithId.FileNameLength) == 0
1676 )
1677 {
1678 KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx...\n",
1679 pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1680 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1681 }
1682 else
1683 {
1684 KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx and names too...\n",
1685 pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1686 __debugbreak();
1687 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1688 /** @todo implement as needed. */
1689 }
1690
1691 pObj->Stats.st_size = uBuf.WithId.EndOfFile.QuadPart;
1692 birdNtTimeToTimeSpec(uBuf.WithId.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1693 birdNtTimeToTimeSpec(uBuf.WithId.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1694 birdNtTimeToTimeSpec(uBuf.WithId.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1695 birdNtTimeToTimeSpec(uBuf.WithId.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1696 pObj->Stats.st_attribs = uBuf.WithId.FileAttributes;
1697 pObj->Stats.st_blksize = 65536;
1698 pObj->Stats.st_blocks = (uBuf.WithId.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1699 / BIRD_STAT_BLOCK_SIZE;
1700 }
1701#endif
1702 if (MY_NT_SUCCESS(rcNt))
1703 {
1704 pObj->uCacheGen = pCache->uGeneration;
1705 fRc = K_TRUE;
1706 }
1707 else
1708 {
1709 /* ouch! */
1710 kHlpAssertMsgFailed(("%#x\n", rcNt));
1711 __debugbreak();
1712 fRc = K_FALSE;
1713 }
1714 }
1715 else
1716 {
1717 /*
1718 * An open directory. Query information via the handle, the
1719 * file ID shouldn't have been able to change, so we can use
1720 * NtQueryInformationFile. Right...
1721 */
1722 PKFSDIR pDir = (PKFSDIR)pObj;
1723 Ios.Information = -1;
1724 Ios.u.Status = -1;
1725 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &uBuf.FullInfo, sizeof(uBuf.FullInfo),
1726 MyFileNetworkOpenInformation);
1727 if (MY_NT_SUCCESS(rcNt))
1728 rcNt = Ios.u.Status;
1729 if (MY_NT_SUCCESS(rcNt))
1730 {
1731 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
1732 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1733 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1734 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1735 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1736 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
1737 pObj->Stats.st_blksize = 65536;
1738 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1739 / BIRD_STAT_BLOCK_SIZE;
1740
1741 if ( pDir->iLastWrite == uBuf.FullInfo.LastWriteTime.QuadPart
1742 && (pObj->fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
1743 KFSCACHE_LOG(("Refreshing %s/%s/ - no re-populating necessary.\n",
1744 pObj->pParent->Obj.pszName, pObj->pszName));
1745 else
1746 {
1747 KFSCACHE_LOG(("Refreshing %s/%s/ - needs re-populating...\n",
1748 pObj->pParent->Obj.pszName, pObj->pszName));
1749 pDir->fNeedRePopulating = K_TRUE;
1750#if 0
1751 /* Refresh the link count. */
1752 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
1753 if (MY_NT_SUCCESS(rcNt))
1754 rcNt = Ios.s.Status;
1755 if (MY_NT_SUCCESS(rcNt))
1756 pObj->Stats.st_nlink = StdInfo.NumberOfLinks;
1757#endif
1758 }
1759 }
1760 if (MY_NT_SUCCESS(rcNt))
1761 {
1762 pObj->uCacheGen = pCache->uGeneration;
1763 fRc = K_TRUE;
1764 }
1765 else
1766 {
1767 /* ouch! */
1768 kHlpAssertMsgFailed(("%#x\n", rcNt));
1769 __debugbreak();
1770 fRc = K_FALSE;
1771 }
1772 }
1773 }
1774
1775 return fRc;
1776}
1777
1778
1779
1780/**
1781 * Looks up a drive letter.
1782 *
1783 * Will enter the drive if necessary.
1784 *
1785 * @returns Pointer to the root directory of the drive or an update-to-date
1786 * missing node.
1787 * @param pCache The cache.
1788 * @param chLetter The uppercased drive letter.
1789 * @param penmError Where to return details as to why the lookup
1790 * failed.
1791 */
1792static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError)
1793{
1794 KU32 const uHash = chLetter - 'A';
1795 KU32 cLeft;
1796 PKFSOBJ *ppCur;
1797
1798 MY_UNICODE_STRING NtPath;
1799 wchar_t wszTmp[8];
1800 char szTmp[4];
1801
1802 /*
1803 * Custom drive letter hashing.
1804 */
1805 if (pCache->RootDir.paHashTab)
1806 {
1807 /** @todo PKFSOBJHASH pHash = */
1808 }
1809
1810 /*
1811 * Special cased lookup.
1812 */
1813 cLeft = pCache->RootDir.cChildren;
1814 ppCur = pCache->RootDir.papChildren;
1815 while (cLeft-- > 0)
1816 {
1817 PKFSOBJ pCur = *ppCur++;
1818 if ( pCur->cchName == 2
1819 && pCur->pszName[0] == chLetter
1820 && pCur->pszName[1] == ':')
1821 {
1822 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
1823 return pCur;
1824 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
1825 if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
1826 return pCur;
1827 return NULL;
1828 }
1829 }
1830
1831 /*
1832 * Need to add it. We always keep the drive letters open for the benefit
1833 * of kFsCachePopuplateOrRefreshDir and others.
1834 */
1835 wszTmp[0] = szTmp[0] = chLetter;
1836 wszTmp[1] = szTmp[1] = ':';
1837 wszTmp[2] = szTmp[2] = '\\';
1838 wszTmp[3] = '.';
1839 wszTmp[4] = '\0';
1840 szTmp[2] = '\0';
1841
1842 NtPath.Buffer = NULL;
1843 NtPath.Length = 0;
1844 NtPath.MaximumLength = 0;
1845 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
1846 {
1847 HANDLE hDir;
1848 MY_NTSTATUS rcNt;
1849 rcNt = birdOpenFileUniStr(&NtPath,
1850 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1851 FILE_ATTRIBUTE_NORMAL,
1852 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1853 FILE_OPEN,
1854 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1855 OBJ_CASE_INSENSITIVE,
1856 &hDir);
1857 birdFreeNtPath(&NtPath);
1858 if (MY_NT_SUCCESS(rcNt))
1859 {
1860 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1861#ifdef KFSCACHE_CFG_SHORT_NAMES
1862 NULL, 0, NULL, 0,
1863#endif
1864 KFSOBJ_TYPE_DIR, penmError);
1865 if (pDir)
1866 {
1867 /*
1868 * We need a little bit of extra info for a drive root. These things are typically
1869 * inherited by subdirectories down the tree, so, we do it all here for till that changes.
1870 */
1871 union
1872 {
1873 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
1874 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
1875 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
1876 } uBuf;
1877 MY_IO_STATUS_BLOCK Ios;
1878 KBOOL fRc;
1879
1880 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
1881 pDir->hDir = hDir;
1882
1883 if (birdStatHandle(hDir, &pDir->Obj.Stats, pDir->Obj.pszName) == 0)
1884 {
1885 pDir->Obj.fHaveStats = K_TRUE;
1886 pDir->uDevNo = pDir->Obj.Stats.st_dev;
1887 }
1888 else
1889 {
1890 /* Just in case. */
1891 pDir->Obj.fHaveStats = K_FALSE;
1892 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
1893 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
1894 }
1895
1896 /* Get the file system. */
1897 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
1898 Ios.Information = -1;
1899 Ios.u.Status = -1;
1900 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
1901 MyFileFsAttributeInformation);
1902 if (MY_NT_SUCCESS(rcNt))
1903 rcNt = Ios.u.Status;
1904 if (MY_NT_SUCCESS(rcNt))
1905 {
1906 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N'
1907 && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
1908 && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
1909 && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
1910 && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
1911 {
1912 DWORD dwDriveType = GetDriveTypeW(wszTmp);
1913 if ( dwDriveType == DRIVE_FIXED
1914 || dwDriveType == DRIVE_RAMDISK)
1915 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
1916 }
1917 }
1918
1919 /*
1920 * Link the new drive letter into the root dir.
1921 */
1922 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
1923 kFsCacheObjRelease(pCache, &pDir->Obj);
1924 return fRc ? &pDir->Obj : NULL;
1925 }
1926
1927 g_pfnNtClose(hDir);
1928 return NULL;
1929 }
1930
1931 /* Assume it doesn't exist if this happens... This may be a little to
1932 restrictive wrt status code checks. */
1933 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
1934 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
1935 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
1936 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
1937 ("%#x\n", rcNt),
1938 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
1939 NULL);
1940 }
1941 else
1942 {
1943 kHlpAssertFailed();
1944 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
1945 return NULL;
1946 }
1947
1948 /*
1949 * Maybe create a missing entry.
1950 */
1951 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
1952 {
1953 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1954#ifdef KFSCACHE_CFG_SHORT_NAMES
1955 NULL, 0, NULL, 0,
1956#endif
1957 KFSOBJ_TYPE_MISSING, penmError);
1958 if (pMissing)
1959 {
1960 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
1961 kFsCacheObjRelease(pCache, pMissing);
1962 return fRc ? pMissing : NULL;
1963 }
1964 }
1965 else
1966 {
1967 /** @todo this isn't necessary correct for a root spec. */
1968 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
1969 }
1970 return NULL;
1971}
1972
1973
1974/**
1975 * Look up a child node, ANSI version.
1976 *
1977 * @returns Pointer to the child if found, NULL if not.
1978 * @param pCache The cache.
1979 * @param pParent The parent directory to search.
1980 * @param pchName The child name to search for (not terminated).
1981 * @param cchName The length of the child name.
1982 */
1983static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
1984{
1985 /* Check for '.' first. */
1986 if (cchName != 1 || *pchName != '.')
1987 {
1988 KU32 cLeft;
1989 PKFSOBJ *ppCur;
1990
1991 if (pParent->paHashTab != NULL)
1992 {
1993 /** @todo directory hash table lookup. */
1994 }
1995
1996 /* Linear search. */
1997 cLeft = pParent->cChildren;
1998 ppCur = pParent->papChildren;
1999 while (cLeft-- > 0)
2000 {
2001 PKFSOBJ pCur = *ppCur++;
2002 if ( ( pCur->cchName == cchName
2003 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2004#ifdef KFSCACHE_CFG_SHORT_NAMES
2005 || ( pCur->cchShortName == cchName
2006 && pCur->pszShortName != pCur->pszName
2007 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2008#endif
2009 )
2010 return pCur;
2011 }
2012 return NULL;
2013 }
2014 return &pParent->Obj;
2015}
2016
2017
2018/**
2019 * Look up a child node, UTF-16 version.
2020 *
2021 * @returns Pointer to the child if found, NULL if not.
2022 * @param pCache The cache.
2023 * @param pParent The parent directory to search.
2024 * @param pwcName The child name to search for (not terminated).
2025 * @param cwcName The length of the child name (in wchar_t's).
2026 */
2027static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
2028{
2029 /* Check for '.' first. */
2030 if (cwcName != 1 || *pwcName != '.')
2031 {
2032 KU32 cLeft;
2033 PKFSOBJ *ppCur;
2034
2035 if (pParent->paHashTab != NULL)
2036 {
2037 /** @todo directory hash table lookup. */
2038 }
2039
2040 /* Linear search. */
2041 cLeft = pParent->cChildren;
2042 ppCur = pParent->papChildren;
2043 while (cLeft-- > 0)
2044 {
2045 PKFSOBJ pCur = *ppCur++;
2046 if ( ( pCur->cwcName == cwcName
2047 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2048#ifdef KFSCACHE_CFG_SHORT_NAMES
2049 || ( pCur->cwcShortName == cwcName
2050 && pCur->pwszShortName != pCur->pwszName
2051 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2052#endif
2053 )
2054 return pCur;
2055 }
2056 return NULL;
2057 }
2058 return &pParent->Obj;
2059}
2060
2061
2062/**
2063 * Looks up a UNC share, ANSI version.
2064 *
2065 * We keep both the server and share in the root directory entry. This means we
2066 * have to clean up the entry name before we can insert it.
2067 *
2068 * @returns Pointer to the share root directory or an update-to-date missing
2069 * node.
2070 * @param pCache The cache.
2071 * @param pszPath The path.
2072 * @param poff Where to return the root dire.
2073 * @param penmError Where to return details as to why the lookup
2074 * failed.
2075 */
2076static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
2077{
2078#if 0 /* later */
2079 KU32 offStartServer;
2080 KU32 offEndServer;
2081 KU32 offStartShare;
2082
2083 KU32 offEnd = 2;
2084 while (IS_SLASH(pszPath[offEnd]))
2085 offEnd++;
2086
2087 offStartServer = offEnd;
2088 while ( (ch = pszPath[offEnd]) != '\0'
2089 && !IS_SLASH(ch))
2090 offEnd++;
2091 offEndServer = offEnd;
2092
2093 if (ch != '\0')
2094 { /* likely */ }
2095 else
2096 {
2097 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2098 return NULL;
2099 }
2100
2101 while (IS_SLASH(pszPath[offEnd]))
2102 offEnd++;
2103 offStartServer = offEnd;
2104 while ( (ch = pszPath[offEnd]) != '\0'
2105 && !IS_SLASH(ch))
2106 offEnd++;
2107#endif
2108 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2109 return NULL;
2110}
2111
2112
2113/**
2114 * Looks up a UNC share, UTF-16 version.
2115 *
2116 * We keep both the server and share in the root directory entry. This means we
2117 * have to clean up the entry name before we can insert it.
2118 *
2119 * @returns Pointer to the share root directory or an update-to-date missing
2120 * node.
2121 * @param pCache The cache.
2122 * @param pwszPath The path.
2123 * @param poff Where to return the root dire.
2124 * @param penmError Where to return details as to why the lookup
2125 * failed.
2126 */
2127static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
2128{
2129#if 0 /* later */
2130 KU32 offStartServer;
2131 KU32 offEndServer;
2132 KU32 offStartShare;
2133
2134 KU32 offEnd = 2;
2135 while (IS_SLASH(pwszPath[offEnd]))
2136 offEnd++;
2137
2138 offStartServer = offEnd;
2139 while ( (ch = pwszPath[offEnd]) != '\0'
2140 && !IS_SLASH(ch))
2141 offEnd++;
2142 offEndServer = offEnd;
2143
2144 if (ch != '\0')
2145 { /* likely */ }
2146 else
2147 {
2148 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2149 return NULL;
2150 }
2151
2152 while (IS_SLASH(pwszPath[offEnd]))
2153 offEnd++;
2154 offStartServer = offEnd;
2155 while ( (ch = pwszPath[offEnd]) != '\0'
2156 && !IS_SLASH(ch))
2157 offEnd++;
2158#endif
2159 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2160 return NULL;
2161}
2162
2163
2164/**
2165 * Walks an full path relative to the given directory, ANSI version.
2166 *
2167 * This will create any missing nodes while walking.
2168 *
2169 * The caller will have to do the path hash table insertion of the result.
2170 *
2171 * @returns Pointer to the tree node corresponding to @a pszPath.
2172 * NULL on lookup failure, see @a penmError for details.
2173 * @param pCache The cache.
2174 * @param pParent The directory to start the lookup in.
2175 * @param pszPath The path to walk.
2176 * @param cchPath The length of the path.
2177 * @param penmError Where to return details as to why the lookup
2178 * failed.
2179 */
2180PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath,
2181 KFSLOOKUPERROR *penmError)
2182{
2183 /*
2184 * Walk loop.
2185 */
2186 KU32 off = 0;
2187 for (;;)
2188 {
2189 PKFSOBJ pChild;
2190
2191 /*
2192 * Find the end of the component, counting trailing slashes.
2193 */
2194 char ch;
2195 KU32 cchSlashes = 0;
2196 KU32 offEnd = off + 1;
2197 while ((ch = pszPath[offEnd]) != '\0')
2198 {
2199 if (!IS_SLASH(ch))
2200 offEnd++;
2201 else
2202 {
2203 do
2204 cchSlashes++;
2205 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2206 break;
2207 }
2208 }
2209
2210 /*
2211 * Do we need to populate or refresh this directory first?
2212 */
2213 if ( pParent->fPopulated
2214 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2215 || pParent->Obj.uCacheGen == pCache->uGeneration) )
2216 { /* likely */ }
2217 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2218 { /* likely */ }
2219 else
2220 return NULL;
2221
2222 /*
2223 * Search the current node for the name.
2224 *
2225 * If we don't find it, we may insert a missing node depending on
2226 * the cache configuration.
2227 */
2228 pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
2229 if (pChild != NULL)
2230 { /* probably likely */ }
2231 else
2232 {
2233 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2234 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
2235 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
2236 {
2237 if (pChild)
2238 return kFsCacheObjRetainInternal(pChild);
2239 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2240 }
2241 else
2242 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2243 return NULL;
2244 }
2245
2246 /* Advance off and check if we're done already. */
2247 off = offEnd + cchSlashes;
2248 if ( cchSlashes == 0
2249 || off >= cchPath)
2250 {
2251 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2252 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2253 || pChild->uCacheGen == pCache->uGenerationMissing
2254 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2255 { /* likely */ }
2256 else
2257 return NULL;
2258 return kFsCacheObjRetainInternal(pChild);
2259 }
2260
2261 /*
2262 * Check that it's a directory. If a missing entry, we may have to
2263 * refresh it and re-examin it.
2264 */
2265 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2266 pParent = (PKFSDIR)pChild;
2267 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2268 {
2269 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2270 return NULL;
2271 }
2272 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2273 || pChild->uCacheGen == pCache->uGenerationMissing)
2274 {
2275 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2276 return NULL;
2277 }
2278 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2279 pParent = (PKFSDIR)pChild;
2280 else
2281 return NULL;
2282 }
2283
2284 return NULL;
2285
2286}
2287
2288
2289/**
2290 * Walks an full path relative to the given directory, UTF-16 version.
2291 *
2292 * This will create any missing nodes while walking.
2293 *
2294 * The caller will have to do the path hash table insertion of the result.
2295 *
2296 * @returns Pointer to the tree node corresponding to @a pszPath.
2297 * NULL on lookup failure, see @a penmError for details.
2298 * @param pCache The cache.
2299 * @param pParent The directory to start the lookup in.
2300 * @param pszPath The path to walk. No dot-dot bits allowed!
2301 * @param cchPath The length of the path.
2302 * @param penmError Where to return details as to why the lookup
2303 * failed.
2304 */
2305PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath,
2306 KFSLOOKUPERROR *penmError)
2307{
2308 /*
2309 * Walk loop.
2310 */
2311 KU32 off = 0;
2312 for (;;)
2313 {
2314 PKFSOBJ pChild;
2315
2316 /*
2317 * Find the end of the component, counting trailing slashes.
2318 */
2319 wchar_t wc;
2320 KU32 cwcSlashes = 0;
2321 KU32 offEnd = off + 1;
2322 while ((wc = pwszPath[offEnd]) != '\0')
2323 {
2324 if (!IS_SLASH(wc))
2325 offEnd++;
2326 else
2327 {
2328 do
2329 cwcSlashes++;
2330 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2331 break;
2332 }
2333 }
2334
2335 /*
2336 * Do we need to populate or refresh this directory first?
2337 */
2338 if ( pParent->fPopulated
2339 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2340 || pParent->Obj.uCacheGen == pCache->uGeneration) )
2341 { /* likely */ }
2342 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2343 { /* likely */ }
2344 else
2345 return NULL;
2346
2347 /*
2348 * Search the current node for the name.
2349 *
2350 * If we don't find it, we may insert a missing node depending on
2351 * the cache configuration.
2352 */
2353 pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off);
2354 if (pChild != NULL)
2355 { /* probably likely */ }
2356 else
2357 {
2358 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2359 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
2360 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
2361 {
2362 if (pChild)
2363 return kFsCacheObjRetainInternal(pChild);
2364 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2365 }
2366 else
2367 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2368 return NULL;
2369 }
2370
2371 /* Advance off and check if we're done already. */
2372 off = offEnd + cwcSlashes;
2373 if ( cwcSlashes == 0
2374 || off >= cwcPath)
2375 {
2376 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2377 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2378 || pChild->uCacheGen == pCache->uGenerationMissing
2379 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2380 { /* likely */ }
2381 else
2382 return NULL;
2383 return kFsCacheObjRetainInternal(pChild);
2384 }
2385
2386 /*
2387 * Check that it's a directory. If a missing entry, we may have to
2388 * refresh it and re-examin it.
2389 */
2390 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2391 pParent = (PKFSDIR)pChild;
2392 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2393 {
2394 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2395 return NULL;
2396 }
2397 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2398 || pChild->uCacheGen == pCache->uGenerationMissing)
2399 {
2400 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2401 return NULL;
2402 }
2403 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2404 pParent = (PKFSDIR)pChild;
2405 else
2406 return NULL;
2407 }
2408
2409 return NULL;
2410
2411}
2412
2413/**
2414 * Walk the file system tree for the given absolute path, entering it into the
2415 * hash table.
2416 *
2417 * This will create any missing nodes while walking.
2418 *
2419 * The caller will have to do the path hash table insertion of the result.
2420 *
2421 * @returns Pointer to the tree node corresponding to @a pszPath.
2422 * NULL on lookup failure, see @a penmError for details.
2423 * @param pCache The cache.
2424 * @param pszPath The path to walk. No dot-dot bits allowed!
2425 * @param cchPath The length of the path.
2426 * @param penmError Where to return details as to why the lookup
2427 * failed.
2428 */
2429static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
2430{
2431 PKFSOBJ pChild;
2432 KU32 cchSlashes;
2433 KU32 offEnd;
2434
2435 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
2436
2437 /*
2438 * The root "directory" needs special handling, so we keep it outside the
2439 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
2440 */
2441 cchSlashes = 0;
2442 if ( pszPath[1] == ':'
2443 && IS_ALPHA(pszPath[0]))
2444 {
2445 /* Drive letter. */
2446 offEnd = 2;
2447 kHlpAssert(IS_SLASH(pszPath[2]));
2448 pChild = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);
2449 }
2450 else if ( IS_SLASH(pszPath[0])
2451 && IS_SLASH(pszPath[1]) )
2452 pChild = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);
2453 else
2454 {
2455 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2456 return NULL;
2457 }
2458 if (pChild)
2459 { /* likely */ }
2460 else
2461 return NULL;
2462
2463 /* Count slashes trailing the root spec. */
2464 if (offEnd < cchPath)
2465 {
2466 kHlpAssert(IS_SLASH(pszPath[offEnd]));
2467 do
2468 cchSlashes++;
2469 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2470 }
2471
2472 /* Done already? */
2473 if (offEnd >= cchPath)
2474 {
2475 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2476 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
2477 || kFsCacheRefreshObj(pCache, pChild, penmError))
2478 return kFsCacheObjRetainInternal(pChild);
2479 return NULL;
2480 }
2481
2482 /* Check that we've got a valid result and not a cached negative one. */
2483 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2484 { /* likely */ }
2485 else
2486 {
2487 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
2488 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
2489 return pChild;
2490 }
2491
2492 /*
2493 * Now that we've found a valid root directory, lookup the
2494 * remainder of the path starting with it.
2495 */
2496 return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pChild, &pszPath[offEnd + cchSlashes],
2497 cchPath - offEnd - cchSlashes, penmError);
2498}
2499
2500
2501/**
2502 * Walk the file system tree for the given absolute path, UTF-16 version.
2503 *
2504 * This will create any missing nodes while walking.
2505 *
2506 * The caller will have to do the path hash table insertion of the result.
2507 *
2508 * @returns Pointer to the tree node corresponding to @a pszPath.
2509 * NULL on lookup failure, see @a penmError for details.
2510 * @param pCache The cache.
2511 * @param pwszPath The path to walk.
2512 * @param cwcPath The length of the path (in wchar_t's).
2513 * @param penmError Where to return details as to why the lookup
2514 * failed.
2515 */
2516static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KFSLOOKUPERROR *penmError)
2517{
2518 PKFSDIR pParent = &pCache->RootDir;
2519 PKFSOBJ pChild;
2520 KU32 off;
2521 KU32 cwcSlashes;
2522 KU32 offEnd;
2523
2524 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath));
2525
2526 /*
2527 * The root "directory" needs special handling, so we keep it outside the
2528 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
2529 */
2530 cwcSlashes = 0;
2531 off = 0;
2532 if ( pwszPath[1] == ':'
2533 && IS_ALPHA(pwszPath[0]))
2534 {
2535 /* Drive letter. */
2536 offEnd = 2;
2537 kHlpAssert(IS_SLASH(pwszPath[2]));
2538 pChild = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError);
2539 }
2540 else if ( IS_SLASH(pwszPath[0])
2541 && IS_SLASH(pwszPath[1]) )
2542 pChild = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError);
2543 else
2544 {
2545 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2546 return NULL;
2547 }
2548 if (pChild)
2549 { /* likely */ }
2550 else
2551 return NULL;
2552
2553 /* Count slashes trailing the root spec. */
2554 if (offEnd < cwcPath)
2555 {
2556 kHlpAssert(IS_SLASH(pwszPath[offEnd]));
2557 do
2558 cwcSlashes++;
2559 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2560 }
2561
2562 /* Done already? */
2563 if (offEnd >= cwcPath)
2564 {
2565 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2566 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
2567 || kFsCacheRefreshObj(pCache, pChild, penmError))
2568 return kFsCacheObjRetainInternal(pChild);
2569 return NULL;
2570 }
2571
2572 /* Check that we've got a valid result and not a cached negative one. */
2573 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2574 { /* likely */ }
2575 else
2576 {
2577 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
2578 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
2579 return pChild;
2580 }
2581
2582 /*
2583 * Now that we've found a valid root directory, lookup the
2584 * remainder of the path starting with it.
2585 */
2586 return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pChild, &pwszPath[offEnd + cwcSlashes],
2587 cwcPath - offEnd - cwcSlashes, penmError);
2588}
2589
2590
2591/**
2592 * This deals with paths that are relative and paths that contains '..'
2593 * elements, ANSI version.
2594 *
2595 * @returns Pointer to object corresponding to @a pszPath on success.
2596 * NULL if this isn't a path we care to cache.
2597 *
2598 * @param pCache The cache.
2599 * @param pszPath The path.
2600 * @param cchPath The length of the path.
2601 * @param penmError Where to return details as to why the lookup
2602 * failed.
2603 */
2604static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
2605{
2606 /*
2607 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2608 * ends up calling it anyway.
2609 */
2610 char szFull[KFSCACHE_CFG_MAX_PATH];
2611 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
2612 if ( cchFull >= 3
2613 && cchFull < sizeof(szFull))
2614 {
2615 PKFSOBJ pFsObj;
2616 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath));
2617 pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError);
2618
2619#if 0 /* No need to do this until it's actually queried. */
2620 /* Cache the resulting path. */
2621 if ( pFsObj
2622 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2623 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2624 {
2625 KU32 uHashPath = kFsCacheStrHash(szFull);
2626 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2627 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2628 }
2629#endif
2630 return pFsObj;
2631 }
2632
2633 /* The path is too long! */
2634 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
2635 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2636 return NULL;
2637}
2638
2639
2640/**
2641 * This deals with paths that are relative and paths that contains '..'
2642 * elements, UTF-16 version.
2643 *
2644 * @returns Pointer to object corresponding to @a pszPath on success.
2645 * NULL if this isn't a path we care to cache.
2646 *
2647 * @param pCache The cache.
2648 * @param pwszPath The path.
2649 * @param cwcPath The length of the path (in wchar_t's).
2650 * @param penmError Where to return details as to why the lookup
2651 * failed.
2652 */
2653static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KFSLOOKUPERROR *penmError)
2654{
2655 /*
2656 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2657 * ends up calling it anyway.
2658 */
2659 wchar_t wszFull[KFSCACHE_CFG_MAX_PATH];
2660 UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL);
2661 if ( cwcFull >= 3
2662 && cwcFull < KFSCACHE_CFG_MAX_PATH)
2663 {
2664 PKFSOBJ pFsObj;
2665 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
2666 pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError);
2667
2668#if 0 /* No need to do this until it's actually queried. */
2669 /* Cache the resulting path. */
2670 if ( pFsObj
2671 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2672 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2673 {
2674 KU32 uHashPath = kFsCacheStrHash(szFull);
2675 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2676 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2677 }
2678#endif
2679 return pFsObj;
2680 }
2681
2682 /* The path is too long! */
2683 kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull));
2684 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2685 return NULL;
2686}
2687
2688
2689/**
2690 * Refreshes a path hash that has expired, ANSI version.
2691 *
2692 * @returns pHash on success, NULL if removed.
2693 * @param pCache The cache.
2694 * @param pHashEntry The path hash.
2695 * @param idxHashTab The hash table entry.
2696 */
2697static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
2698{
2699 if (!pHashEntry->pFsObj)
2700 {
2701 if (pHashEntry->fAbsolute)
2702 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, &pHashEntry->enmError);
2703 else
2704 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, &pHashEntry->enmError);
2705 }
2706 else
2707 {
2708 KU8 bOldType = pHashEntry->pFsObj->bObjType;
2709 KFSLOOKUPERROR enmError;
2710 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
2711 {
2712 if (pHashEntry->pFsObj->bObjType == bOldType)
2713 { }
2714 else
2715 {
2716 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
2717 if (pHashEntry->fAbsolute)
2718 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
2719 &pHashEntry->enmError);
2720 else
2721 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
2722 &pHashEntry->enmError);
2723 }
2724 }
2725 else
2726 {
2727 __debugbreak();
2728 /** @todo just remove this entry. */
2729 return NULL;
2730 }
2731 }
2732 pHashEntry->uCacheGen = pCache->uGenerationMissing;
2733 return pHashEntry;
2734}
2735
2736
2737/**
2738 * Refreshes a path hash that has expired, UTF-16 version.
2739 *
2740 * @returns pHash on success, NULL if removed.
2741 * @param pCache The cache.
2742 * @param pHashEntry The path hash.
2743 * @param idxHashTab The hash table entry.
2744 */
2745static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab)
2746{
2747 if (!pHashEntry->pFsObj)
2748 {
2749 if (pHashEntry->fAbsolute)
2750 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, &pHashEntry->enmError);
2751 else
2752 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, &pHashEntry->enmError);
2753 }
2754 else
2755 {
2756 KU8 bOldType = pHashEntry->pFsObj->bObjType;
2757 KFSLOOKUPERROR enmError;
2758 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
2759 {
2760 if (pHashEntry->pFsObj->bObjType == bOldType)
2761 { }
2762 else
2763 {
2764 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
2765 if (pHashEntry->fAbsolute)
2766 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
2767 &pHashEntry->enmError);
2768 else
2769 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
2770 &pHashEntry->enmError);
2771 }
2772 }
2773 else
2774 {
2775 __debugbreak();
2776 /** @todo just remove this entry. */
2777 return NULL;
2778 }
2779 }
2780 pHashEntry->uCacheGen = pCache->uGenerationMissing;
2781 return pHashEntry;
2782}
2783
2784
2785/**
2786 * Internal lookup worker that looks up a KFSOBJ for the given ANSI path with
2787 * length and hash.
2788 *
2789 * This will first try the hash table. If not in the hash table, the file
2790 * system cache tree is walked, missing bits filled in and finally a hash table
2791 * entry is created.
2792 *
2793 * Only drive letter paths are cachable. We don't do any UNC paths at this
2794 * point.
2795 *
2796 * @returns Reference to object corresponding to @a pszPath on success, this
2797 * must be released by kFsCacheObjRelease.
2798 * NULL if not a path we care to cache.
2799 * @param pCache The cache.
2800 * @param pchPath The path to lookup.
2801 * @param cchPath The path length.
2802 * @param uHashPath The hash of the path.
2803 * @param penmError Where to return details as to why the lookup
2804 * failed.
2805 */
2806static PKFSOBJ kFsCacheLookupHashedA(PKFSCACHE pCache, const char *pchPath, KU32 cchPath, KU32 uHashPath,
2807 KFSLOOKUPERROR *penmError)
2808{
2809 /*
2810 * Do hash table lookup of the path.
2811 */
2812 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
2813 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab];
2814 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2815 if (pHashEntry)
2816 {
2817 do
2818 {
2819 if ( pHashEntry->uHashPath == uHashPath
2820 && pHashEntry->cchPath == cchPath
2821 && kHlpMemComp(pHashEntry->pszPath, pchPath, cchPath) == 0)
2822 {
2823 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2824 || pHashEntry->uCacheGen == (pHashEntry->pFsObj ? pCache->uGeneration : pCache->uGenerationMissing)
2825 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
2826 {
2827 pCache->cLookups++;
2828 pCache->cPathHashHits++;
2829 KFSCACHE_LOG2(("kFsCacheLookupA(%*.*s) - hit %p\n", cchPath, cchPath, pchPath, pHashEntry->pFsObj));
2830 *penmError = pHashEntry->enmError;
2831 if (pHashEntry->pFsObj)
2832 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
2833 return NULL;
2834 }
2835 break;
2836 }
2837 pHashEntry = pHashEntry->pNext;
2838 } while (pHashEntry);
2839 }
2840
2841 /*
2842 * Create an entry for it by walking the file system cache and filling in the blanks.
2843 */
2844 if ( cchPath > 0
2845 && cchPath < KFSCACHE_CFG_MAX_PATH)
2846 {
2847 PKFSOBJ pFsObj;
2848 KBOOL fAbsolute;
2849
2850 /* Is absolute without any '..' bits? */
2851 if ( cchPath >= 3
2852 && ( ( pchPath[1] == ':' /* Drive letter */
2853 && IS_SLASH(pchPath[2])
2854 && IS_ALPHA(pchPath[0]) )
2855 || ( IS_SLASH(pchPath[0]) /* UNC */
2856 && IS_SLASH(pchPath[1]) ) )
2857 && !kFsCacheHasDotDotA(pchPath, cchPath) )
2858 {
2859 pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, penmError);
2860 fAbsolute = K_TRUE;
2861 }
2862 else
2863 {
2864 pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, penmError);
2865 fAbsolute = K_FALSE;
2866 }
2867 if ( pFsObj
2868 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2869 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
2870 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
2871 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pchPath, cchPath, uHashPath, idxHashTab, fAbsolute, *penmError);
2872
2873 pCache->cLookups++;
2874 if (pFsObj)
2875 pCache->cWalkHits++;
2876 return pFsObj;
2877 }
2878
2879 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2880 return NULL;
2881}
2882
2883
2884/**
2885 * Internal lookup worker that looks up a KFSOBJ for the given UTF-16 path with
2886 * length and hash.
2887 *
2888 * This will first try the hash table. If not in the hash table, the file
2889 * system cache tree is walked, missing bits filled in and finally a hash table
2890 * entry is created.
2891 *
2892 * Only drive letter paths are cachable. We don't do any UNC paths at this
2893 * point.
2894 *
2895 * @returns Reference to object corresponding to @a pwcPath on success, this
2896 * must be released by kFsCacheObjRelease.
2897 * NULL if not a path we care to cache.
2898 * @param pCache The cache.
2899 * @param pwcPath The path to lookup.
2900 * @param cwcPath The length of the path (in wchar_t's).
2901 * @param uHashPath The hash of the path.
2902 * @param penmError Where to return details as to why the lookup
2903 * failed.
2904 */
2905static PKFSOBJ kFsCacheLookupHashedW(PKFSCACHE pCache, const wchar_t *pwcPath, KU32 cwcPath, KU32 uHashPath,
2906 KFSLOOKUPERROR *penmError)
2907{
2908 /*
2909 * Do hash table lookup of the path.
2910 */
2911 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
2912 PKFSHASHW pHashEntry = pCache->apUtf16Paths[idxHashTab];
2913 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2914 if (pHashEntry)
2915 {
2916 do
2917 {
2918 if ( pHashEntry->uHashPath == uHashPath
2919 && pHashEntry->cwcPath == cwcPath
2920 && kHlpMemComp(pHashEntry->pwszPath, pwcPath, cwcPath) == 0)
2921 {
2922 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2923 || pHashEntry->uCacheGen == (pHashEntry->pFsObj ? pCache->uGeneration : pCache->uGenerationMissing)
2924 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) )
2925 {
2926 pCache->cLookups++;
2927 pCache->cPathHashHits++;
2928 KFSCACHE_LOG2(("kFsCacheLookupW(%*.*ls) - hit %p\n", cwcPath, cwcPath, pwcPath, pHashEntry->pFsObj));
2929 *penmError = pHashEntry->enmError;
2930 if (pHashEntry->pFsObj)
2931 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
2932 return NULL;
2933 }
2934 break;
2935 }
2936 pHashEntry = pHashEntry->pNext;
2937 } while (pHashEntry);
2938 }
2939
2940 /*
2941 * Create an entry for it by walking the file system cache and filling in the blanks.
2942 */
2943 if ( cwcPath > 0
2944 && cwcPath < KFSCACHE_CFG_MAX_PATH)
2945 {
2946 PKFSOBJ pFsObj;
2947 KBOOL fAbsolute;
2948
2949 /* Is absolute without any '..' bits? */
2950 if ( cwcPath >= 3
2951 && ( ( pwcPath[1] == ':' /* Drive letter */
2952 && IS_SLASH(pwcPath[2])
2953 && IS_ALPHA(pwcPath[0]) )
2954 || ( IS_SLASH(pwcPath[0]) /* UNC */
2955 && IS_SLASH(pwcPath[1]) ) )
2956 && !kFsCacheHasDotDotW(pwcPath, cwcPath) )
2957 {
2958 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, penmError);
2959 fAbsolute = K_TRUE;
2960 }
2961 else
2962 {
2963 pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, penmError);
2964 fAbsolute = K_FALSE;
2965 }
2966 if ( pFsObj
2967 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2968 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
2969 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
2970 kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwcPath, cwcPath, uHashPath, idxHashTab, fAbsolute, *penmError);
2971
2972 pCache->cLookups++;
2973 if (pFsObj)
2974 pCache->cWalkHits++;
2975 return pFsObj;
2976 }
2977
2978 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2979 return NULL;
2980}
2981
2982
2983
2984/**
2985 * Looks up a KFSOBJ for the given ANSI path.
2986 *
2987 * This will first try the hash table. If not in the hash table, the file
2988 * system cache tree is walked, missing bits filled in and finally a hash table
2989 * entry is created.
2990 *
2991 * Only drive letter paths are cachable. We don't do any UNC paths at this
2992 * point.
2993 *
2994 * @returns Reference to object corresponding to @a pszPath on success, this
2995 * must be released by kFsCacheObjRelease.
2996 * NULL if not a path we care to cache.
2997 * @param pCache The cache.
2998 * @param pszPath The path to lookup.
2999 * @param penmError Where to return details as to why the lookup
3000 * failed.
3001 */
3002PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3003{
3004 KU32 uHashPath;
3005 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
3006 return kFsCacheLookupHashedA(pCache, pszPath, cchPath, uHashPath, penmError);
3007}
3008
3009
3010/**
3011 * Looks up a KFSOBJ for the given UTF-16 path.
3012 *
3013 * This will first try the hash table. If not in the hash table, the file
3014 * system cache tree is walked, missing bits filled in and finally a hash table
3015 * entry is created.
3016 *
3017 * Only drive letter paths are cachable. We don't do any UNC paths at this
3018 * point.
3019 *
3020 * @returns Reference to object corresponding to @a pwszPath on success, this
3021 * must be released by kFsCacheObjRelease.
3022 * NULL if not a path we care to cache.
3023 * @param pCache The cache.
3024 * @param pwszPath The path to lookup.
3025 * @param penmError Where to return details as to why the lookup
3026 * failed.
3027 */
3028PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3029{
3030 KU32 uHashPath;
3031 KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath);
3032 return kFsCacheLookupHashedW(pCache, pwszPath, cwcPath, uHashPath, penmError);
3033}
3034
3035
3036/**
3037 * Looks up a KFSOBJ for the given ANSI path.
3038 *
3039 * This will first try the hash table. If not in the hash table, the file
3040 * system cache tree is walked, missing bits filled in and finally a hash table
3041 * entry is created.
3042 *
3043 * Only drive letter paths are cachable. We don't do any UNC paths at this
3044 * point.
3045 *
3046 * @returns Reference to object corresponding to @a pchPath on success, this
3047 * must be released by kFsCacheObjRelease.
3048 * NULL if not a path we care to cache.
3049 * @param pCache The cache.
3050 * @param pchPath The path to lookup (does not need to be nul
3051 * terminated).
3052 * @param cchPath The path length.
3053 * @param penmError Where to return details as to why the lookup
3054 * failed.
3055 */
3056PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError)
3057{
3058 return kFsCacheLookupHashedA(pCache, pchPath, (KU32)cchPath, kFsCacheStrHashN(pchPath, cchPath), penmError);
3059}
3060
3061
3062/**
3063 * Looks up a KFSOBJ for the given UTF-16 path.
3064 *
3065 * This will first try the hash table. If not in the hash table, the file
3066 * system cache tree is walked, missing bits filled in and finally a hash table
3067 * entry is created.
3068 *
3069 * Only drive letter paths are cachable. We don't do any UNC paths at this
3070 * point.
3071 *
3072 * @returns Reference to object corresponding to @a pwchPath on success, this
3073 * must be released by kFsCacheObjRelease.
3074 * NULL if not a path we care to cache.
3075 * @param pCache The cache.
3076 * @param pwcPath The path to lookup (does not need to be nul
3077 * terminated).
3078 * @param cwcPath The path length (in wchar_t's).
3079 * @param penmError Where to return details as to why the lookup
3080 * failed.
3081 */
3082PKFSOBJ kFsCacheLookupWithLengthW(PKFSCACHE pCache, const wchar_t *pwcPath, KSIZE cwcPath, KFSLOOKUPERROR *penmError)
3083{
3084 return kFsCacheLookupHashedW(pCache, pwcPath, (KU32)cwcPath, kFsCacheUtf16HashN(pwcPath, cwcPath), penmError);
3085}
3086
3087
3088/**
3089 * Wrapper around kFsCacheLookupA that drops KFSOBJ_TYPE_MISSING and returns
3090 * KFSLOOKUPERROR_NOT_FOUND instead.
3091 *
3092 * @returns Reference to object corresponding to @a pszPath on success, this
3093 * must be released by kFsCacheObjRelease.
3094 * NULL if not a path we care to cache.
3095 * @param pCache The cache.
3096 * @param pszPath The path to lookup.
3097 * @param penmError Where to return details as to why the lookup
3098 * failed.
3099 */
3100PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3101{
3102 PKFSOBJ pObj = kFsCacheLookupA(pCache, pszPath, penmError);
3103 if (pObj)
3104 {
3105 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3106 return pObj;
3107
3108 kFsCacheObjRelease(pCache, pObj);
3109 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3110 }
3111 return NULL;
3112}
3113
3114
3115/**
3116 * Wrapper around kFsCacheLookupW that drops KFSOBJ_TYPE_MISSING and returns
3117 * KFSLOOKUPERROR_NOT_FOUND instead.
3118 *
3119 * @returns Reference to object corresponding to @a pszPath on success, this
3120 * must be released by kFsCacheObjRelease.
3121 * NULL if not a path we care to cache.
3122 * @param pCache The cache.
3123 * @param pwszPath The path to lookup.
3124 * @param penmError Where to return details as to why the lookup
3125 * failed.
3126 */
3127PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3128{
3129 PKFSOBJ pObj = kFsCacheLookupW(pCache, pwszPath, penmError);
3130 if (pObj)
3131 {
3132 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3133 return pObj;
3134
3135 kFsCacheObjRelease(pCache, pObj);
3136 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3137 }
3138 return NULL;
3139}
3140
3141
3142/**
3143 * Destroys a cache object which has a zero reference count.
3144 *
3145 * @returns 0
3146 * @param pCache The cache.
3147 * @param pObj The object.
3148 */
3149KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
3150{
3151 kHlpAssert(pObj->cRefs == 0);
3152 kHlpAssert(pObj->pParent == NULL);
3153 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3154
3155 KFSCACHE_LOG(("Destroying %s/%s, type=%d\n", pObj->pParent ? pObj->pParent->Obj.pszName : "", pObj->pszName, pObj->bObjType));
3156__debugbreak();
3157
3158 /*
3159 * Invalidate the structure.
3160 */
3161 pObj->u32Magic = ~KFSOBJ_MAGIC;
3162
3163 /*
3164 * Destroy any user data first.
3165 */
3166 while (pObj->pUserDataHead != NULL)
3167 {
3168 PKFSUSERDATA pUserData = pObj->pUserDataHead;
3169 pObj->pUserDataHead = pUserData->pNext;
3170 if (pUserData->pfnDestructor)
3171 pUserData->pfnDestructor(pCache, pObj, pUserData);
3172 kHlpFree(pUserData);
3173 }
3174
3175 /*
3176 * Do type specific destruction
3177 */
3178 switch (pObj->bObjType)
3179 {
3180 case KFSOBJ_TYPE_MISSING:
3181 /* nothing else to do here */
3182 pCache->cbObjects -= sizeof(KFSDIR);
3183 break;
3184
3185 case KFSOBJ_TYPE_DIR:
3186 {
3187 PKFSDIR pDir = (PKFSDIR)pObj;
3188 KU32 cChildren = pDir->cChildren;
3189 pCache->cbObjects -= sizeof(*pDir)
3190 + K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
3191 + pDir->cHashTab * sizeof(pDir->paHashTab);
3192
3193 pDir->cChildren = 0;
3194 while (cChildren-- > 0)
3195 kFsCacheObjRelease(pCache, pDir->papChildren[cChildren]);
3196 kHlpFree(pDir->papChildren);
3197 pDir->papChildren = NULL;
3198
3199 kHlpFree(pDir->paHashTab);
3200 pDir->paHashTab = NULL;
3201 break;
3202 }
3203
3204 case KFSOBJ_TYPE_FILE:
3205 case KFSOBJ_TYPE_OTHER:
3206 pCache->cbObjects -= sizeof(*pObj);
3207 break;
3208
3209 default:
3210 return 0;
3211 }
3212
3213 /*
3214 * Common bits.
3215 */
3216 pCache->cbObjects -= pObj->cchName + 1;
3217#ifdef KFSCACHE_CFG_UTF16
3218 pCache->cbObjects -= (pObj->cwcName + 1) * sizeof(wchar_t);
3219#endif
3220#ifdef KFSCACHE_CFG_SHORT_NAMES
3221 if (pObj->pszName != pObj->pszShortName)
3222 {
3223 pCache->cbObjects -= pObj->cchShortName + 1;
3224# ifdef KFSCACHE_CFG_UTF16
3225 pCache->cbObjects -= (pObj->cwcShortName + 1) * sizeof(wchar_t);
3226# endif
3227 }
3228#endif
3229 pCache->cObjects--;
3230
3231 kHlpFree(pObj);
3232 return 0;
3233}
3234
3235
3236/**
3237 * Releases a reference to a cache object.
3238 *
3239 * @returns New reference count.
3240 * @param pCache The cache.
3241 * @param pObj The object.
3242 */
3243KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
3244{
3245 if (pObj)
3246 {
3247 KU32 cRefs;
3248 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3249 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3250
3251 cRefs = --pObj->cRefs;
3252 if (cRefs)
3253 return cRefs;
3254 return kFsCacheObjDestroy(pCache, pObj);
3255 }
3256 return 0;
3257}
3258
3259
3260/**
3261 * Retains a reference to a cahce object.
3262 *
3263 * @returns New reference count.
3264 * @param pObj The object.
3265 */
3266KU32 kFsCacheObjRetain(PKFSOBJ pObj)
3267{
3268 KU32 cRefs;
3269 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3270 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3271
3272 cRefs = ++pObj->cRefs;
3273 kHlpAssert(cRefs < 16384);
3274 return cRefs;
3275}
3276
3277
3278/**
3279 * Associates an item of user data with the given object.
3280 *
3281 * If the data needs cleaning up before being free, set the
3282 * PKFSUSERDATA::pfnDestructor member of the returned structure.
3283 *
3284 * @returns Pointer to the user data on success.
3285 * NULL if out of memory or key already in use.
3286 *
3287 * @param pCache The cache.
3288 * @param pObj The object.
3289 * @param uKey The user data key.
3290 * @param cbUserData The size of the user data.
3291 */
3292PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData)
3293{
3294 kHlpAssert(cbUserData >= sizeof(*pNew));
3295 if (kFsCacheObjGetUserData(pCache, pObj, uKey) == NULL)
3296 {
3297 PKFSUSERDATA pNew = (PKFSUSERDATA)kHlpAllocZ(cbUserData);
3298 if (pNew)
3299 {
3300 pNew->uKey = uKey;
3301 pNew->pfnDestructor = NULL;
3302 pNew->pNext = pObj->pUserDataHead;
3303 pObj->pUserDataHead = pNew;
3304 return pNew;
3305 }
3306 }
3307
3308 return NULL;
3309}
3310
3311
3312/**
3313 * Retrieves an item of user data associated with the given object.
3314 *
3315 * @returns Pointer to the associated user data if found, otherwise NULL.
3316 * @param pCache The cache.
3317 * @param pObj The object.
3318 * @param uKey The user data key.
3319 */
3320PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey)
3321{
3322 PKFSUSERDATA pCur;
3323
3324 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3325 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3326
3327 for (pCur = pObj->pUserDataHead; pCur; pCur = pCur->pNext)
3328 if (pCur->uKey == uKey)
3329 return pCur;
3330 return NULL;
3331}
3332
3333
3334/**
3335 * Gets the full path to @a pObj, ANSI version.
3336 *
3337 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3338 * @param pObj The object to get the full path to.
3339 * @param pszPath Where to return the path
3340 * @param cbPath The size of the output buffer.
3341 * @param chSlash The slash to use.
3342 */
3343KBOOL kFsCacheObjGetFullPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
3344{
3345 KSIZE off = pObj->cchParent;
3346 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3347 if (off > 0)
3348 {
3349 KSIZE offEnd = off + pObj->cchName;
3350 if (offEnd < cbPath)
3351 {
3352 PKFSDIR pAncestor;
3353
3354 pszPath[off + pObj->cchName] = '\0';
3355 memcpy(&pszPath[off], pObj->pszName, pObj->cchName);
3356
3357 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3358 {
3359 kHlpAssert(off > 1);
3360 kHlpAssert(pAncestor != NULL);
3361 kHlpAssert(pAncestor->Obj.cchName > 0);
3362 pszPath[--off] = chSlash;
3363 off -= pAncestor->Obj.cchName;
3364 kHlpAssert(pAncestor->Obj.cchParent == off);
3365 memcpy(&pszPath[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3366 }
3367 return K_TRUE;
3368 }
3369 }
3370 else
3371 {
3372 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
3373 off = pObj->cchName;
3374 if (off + fDriveLetter < cbPath)
3375 {
3376 memcpy(pszPath, pObj->pszName, off);
3377 if (fDriveLetter)
3378 pszPath[off++] = chSlash;
3379 pszPath[off] = '\0';
3380 return K_TRUE;
3381 }
3382 }
3383
3384 return K_FALSE;
3385}
3386
3387
3388/**
3389 * Gets the full path to @a pObj, UTF-16 version.
3390 *
3391 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3392 * @param pObj The object to get the full path to.
3393 * @param pszPath Where to return the path
3394 * @param cbPath The size of the output buffer.
3395 * @param wcSlash The slash to use.
3396 */
3397KBOOL kFsCacheObjGetFullPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
3398{
3399 KSIZE off = pObj->cwcParent;
3400 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3401 if (off > 0)
3402 {
3403 KSIZE offEnd = off + pObj->cwcName;
3404 if (offEnd < cwcPath)
3405 {
3406 PKFSDIR pAncestor;
3407
3408 pwszPath[off + pObj->cwcName] = '\0';
3409 memcpy(&pwszPath[off], pObj->pwszName, pObj->cwcName * sizeof(wchar_t));
3410
3411 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3412 {
3413 kHlpAssert(off > 1);
3414 kHlpAssert(pAncestor != NULL);
3415 kHlpAssert(pAncestor->Obj.cwcName > 0);
3416 pwszPath[--off] = wcSlash;
3417 off -= pAncestor->Obj.cwcName;
3418 kHlpAssert(pAncestor->Obj.cwcParent == off);
3419 memcpy(&pwszPath[off], pAncestor->Obj.pwszName, pAncestor->Obj.cwcName * sizeof(wchar_t));
3420 }
3421 return K_TRUE;
3422 }
3423 }
3424 else
3425 {
3426 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
3427 off = pObj->cwcName;
3428 if (off + fDriveLetter < cwcPath)
3429 {
3430 memcpy(pwszPath, pObj->pwszName, off * sizeof(wchar_t));
3431 if (fDriveLetter)
3432 pwszPath[off++] = wcSlash;
3433 pwszPath[off] = '\0';
3434 return K_TRUE;
3435 }
3436 }
3437
3438 return K_FALSE;
3439}
3440
3441
3442#ifdef KFSCACHE_CFG_SHORT_NAMES
3443
3444/**
3445 * Gets the full short path to @a pObj, ANSI version.
3446 *
3447 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3448 * @param pObj The object to get the full path to.
3449 * @param pszPath Where to return the path
3450 * @param cbPath The size of the output buffer.
3451 * @param chSlash The slash to use.
3452 */
3453KBOOL kFsCacheObjGetFullShortPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
3454{
3455 KSIZE off = pObj->cchShortParent;
3456 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3457 if (off > 0)
3458 {
3459 KSIZE offEnd = off + pObj->cchShortName;
3460 if (offEnd < cbPath)
3461 {
3462 PKFSDIR pAncestor;
3463
3464 pszPath[off + pObj->cchShortName] = '\0';
3465 memcpy(&pszPath[off], pObj->pszShortName, pObj->cchShortName);
3466
3467 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3468 {
3469 kHlpAssert(off > 1);
3470 kHlpAssert(pAncestor != NULL);
3471 kHlpAssert(pAncestor->Obj.cchShortName > 0);
3472 pszPath[--off] = chSlash;
3473 off -= pAncestor->Obj.cchShortName;
3474 kHlpAssert(pAncestor->Obj.cchShortParent == off);
3475 memcpy(&pszPath[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
3476 }
3477 return K_TRUE;
3478 }
3479 }
3480 else
3481 {
3482 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
3483 off = pObj->cchShortName;
3484 if (off + fDriveLetter < cbPath)
3485 {
3486 memcpy(pszPath, pObj->pszShortName, off);
3487 if (fDriveLetter)
3488 pszPath[off++] = chSlash;
3489 pszPath[off] = '\0';
3490 return K_TRUE;
3491 }
3492 }
3493
3494 return K_FALSE;
3495}
3496
3497
3498/**
3499 * Gets the full short path to @a pObj, UTF-16 version.
3500 *
3501 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3502 * @param pObj The object to get the full path to.
3503 * @param pszPath Where to return the path
3504 * @param cbPath The size of the output buffer.
3505 * @param wcSlash The slash to use.
3506 */
3507KBOOL kFsCacheObjGetFullShortPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
3508{
3509 KSIZE off = pObj->cwcShortParent;
3510 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3511 if (off > 0)
3512 {
3513 KSIZE offEnd = off + pObj->cwcShortName;
3514 if (offEnd < cwcPath)
3515 {
3516 PKFSDIR pAncestor;
3517
3518 pwszPath[off + pObj->cwcShortName] = '\0';
3519 memcpy(&pwszPath[off], pObj->pwszShortName, pObj->cwcShortName * sizeof(wchar_t));
3520
3521 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3522 {
3523 kHlpAssert(off > 1);
3524 kHlpAssert(pAncestor != NULL);
3525 kHlpAssert(pAncestor->Obj.cwcShortName > 0);
3526 pwszPath[--off] = wcSlash;
3527 off -= pAncestor->Obj.cwcShortName;
3528 kHlpAssert(pAncestor->Obj.cwcShortParent == off);
3529 memcpy(&pwszPath[off], pAncestor->Obj.pwszShortName, pAncestor->Obj.cwcShortName * sizeof(wchar_t));
3530 }
3531 return K_TRUE;
3532 }
3533 }
3534 else
3535 {
3536 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
3537 off = pObj->cwcShortName;
3538 if (off + fDriveLetter < cwcPath)
3539 {
3540 memcpy(pwszPath, pObj->pwszShortName, off * sizeof(wchar_t));
3541 if (fDriveLetter)
3542 pwszPath[off++] = wcSlash;
3543 pwszPath[off] = '\0';
3544 return K_TRUE;
3545 }
3546 }
3547
3548 return K_FALSE;
3549}
3550
3551#endif /* KFSCACHE_CFG_SHORT_NAMES */
3552
3553
3554
3555/**
3556 * Read the specified bits from the files into the given buffer, simple version.
3557 *
3558 * @returns K_TRUE on success (all requested bytes read),
3559 * K_FALSE on any kind of failure.
3560 *
3561 * @param pCache The cache.
3562 * @param pFileObj The file object.
3563 * @param offStart Where to start reading.
3564 * @param pvBuf Where to store what we read.
3565 * @param cbToRead How much to read (exact).
3566 */
3567KBOOL kFsCacheFileSimpleOpenReadClose(PKFSCACHE pCache, PKFSOBJ pFileObj, KU64 offStart, void *pvBuf, KSIZE cbToRead)
3568{
3569 /*
3570 * Open the file relative to the parent directory.
3571 */
3572 MY_NTSTATUS rcNt;
3573 HANDLE hFile;
3574 MY_IO_STATUS_BLOCK Ios;
3575 MY_OBJECT_ATTRIBUTES ObjAttr;
3576 MY_UNICODE_STRING UniStr;
3577
3578 kHlpAssertReturn(pFileObj->bObjType == KFSOBJ_TYPE_FILE, K_FALSE);
3579 kHlpAssert(pFileObj->pParent);
3580 kHlpAssertReturn(pFileObj->pParent->hDir != INVALID_HANDLE_VALUE, K_FALSE);
3581 kHlpAssertReturn(offStart == 0, K_FALSE); /** @todo when needed */
3582
3583 Ios.Information = -1;
3584 Ios.u.Status = -1;
3585
3586 UniStr.Buffer = (wchar_t *)pFileObj->pwszName;
3587 UniStr.Length = (USHORT)(pFileObj->cwcName * sizeof(wchar_t));
3588 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
3589
3590 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFileObj->pParent->hDir, NULL /*pSecAttr*/);
3591
3592 rcNt = g_pfnNtCreateFile(&hFile,
3593 GENERIC_READ | SYNCHRONIZE,
3594 &ObjAttr,
3595 &Ios,
3596 NULL, /*cbFileInitialAlloc */
3597 FILE_ATTRIBUTE_NORMAL,
3598 FILE_SHARE_READ,
3599 FILE_OPEN,
3600 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
3601 NULL, /*pEaBuffer*/
3602 0); /*cbEaBuffer*/
3603 if (MY_NT_SUCCESS(rcNt))
3604 {
3605 LARGE_INTEGER offFile;
3606 offFile.QuadPart = offStart;
3607
3608 Ios.Information = -1;
3609 Ios.u.Status = -1;
3610 rcNt = g_pfnNtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApcComplete*/, NULL /*pvApcCtx*/, &Ios,
3611 pvBuf, (KU32)cbToRead, !offStart ? &offFile : NULL, NULL /*puKey*/);
3612 if (MY_NT_SUCCESS(rcNt))
3613 rcNt = Ios.u.Status;
3614 if (MY_NT_SUCCESS(rcNt))
3615 {
3616 if (Ios.Information == cbToRead)
3617 {
3618 g_pfnNtClose(hFile);
3619 return K_TRUE;
3620 }
3621 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': Information=%p\n", pFileObj->pwszName, Ios.Information));
3622 }
3623 else
3624 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': %#x\n", pFileObj->pwszName, rcNt));
3625 g_pfnNtClose(hFile);
3626 }
3627 else
3628 KFSCACHE_LOG(("Error opening '%ls' for caching: %#x\n", pFileObj->pwszName, rcNt));
3629 return K_FALSE;
3630}
3631
3632
3633/**
3634 * Invalidate all cache entries of missing files.
3635 *
3636 * @param pCache The cache.
3637 */
3638void kFsCacheInvalidateMissing(PKFSCACHE pCache)
3639{
3640 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3641 pCache->uGenerationMissing++;
3642 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
3643 KFSCACHE_LOG(("Invalidate missing %#x\n", pCache->uGenerationMissing));
3644}
3645
3646
3647/**
3648 * Invalidate all cache entries of missing files.
3649 *
3650 * @param pCache The cache.
3651 */
3652void kFsCacheInvalidateAll(PKFSCACHE pCache)
3653{
3654 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3655 pCache->uGenerationMissing++;
3656 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
3657 pCache->uGeneration++;
3658 kHlpAssert(pCache->uGeneration < KU32_MAX);
3659 KFSCACHE_LOG(("Invalidate all %#x/%#x\n", pCache->uGenerationMissing, pCache->uGeneration));
3660
3661}
3662
3663
3664
3665PKFSCACHE kFsCacheCreate(KU32 fFlags)
3666{
3667 PKFSCACHE pCache;
3668 birdResolveImports();
3669
3670 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
3671 if (pCache)
3672 {
3673 /* Dummy root dir entry. */
3674 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC;
3675 pCache->RootDir.Obj.cRefs = 1;
3676 pCache->RootDir.Obj.uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
3677 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR;
3678 pCache->RootDir.Obj.fHaveStats = K_FALSE;
3679 pCache->RootDir.Obj.pParent = NULL;
3680 pCache->RootDir.Obj.pszName = "";
3681 pCache->RootDir.Obj.cchName = 0;
3682 pCache->RootDir.Obj.cchParent = 0;
3683#ifdef KFSCACHE_CFG_UTF16
3684 pCache->RootDir.Obj.cwcName = 0;
3685 pCache->RootDir.Obj.cwcParent = 0;
3686 pCache->RootDir.Obj.pwszName = L"";
3687#endif
3688
3689#ifdef KFSCACHE_CFG_SHORT_NAMES
3690 pCache->RootDir.Obj.pszShortName = NULL;
3691 pCache->RootDir.Obj.cchShortName = 0;
3692 pCache->RootDir.Obj.cchShortParent = 0;
3693# ifdef KFSCACHE_CFG_UTF16
3694 pCache->RootDir.Obj.cwcShortName;
3695 pCache->RootDir.Obj.cwcShortParent;
3696 pCache->RootDir.Obj.pwszShortName;
3697# endif
3698#endif
3699 pCache->RootDir.cChildren = 0;
3700 pCache->RootDir.cChildrenAllocated = 0;
3701 pCache->RootDir.papChildren = NULL;
3702 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
3703 pCache->RootDir.cHashTab = 251;
3704 pCache->RootDir.paHashTab = (PKFSOBJHASH)kHlpAllocZ( pCache->RootDir.cHashTab
3705 * sizeof(pCache->RootDir.paHashTab[0]));
3706 if (pCache->RootDir.paHashTab)
3707 {
3708 /* The cache itself. */
3709 pCache->u32Magic = KFSCACHE_MAGIC;
3710 pCache->fFlags = fFlags;
3711 pCache->uGeneration = KU32_MAX / 2;
3712 pCache->uGenerationMissing = 1;
3713 pCache->cObjects = 1;
3714 pCache->cbObjects = sizeof(pCache->RootDir) + pCache->RootDir.cHashTab * sizeof(pCache->RootDir.paHashTab[0]);
3715 pCache->cPathHashHits = 0;
3716 pCache->cWalkHits = 0;
3717 pCache->cAnsiPaths = 0;
3718 pCache->cAnsiPathCollisions = 0;
3719 pCache->cbAnsiPaths = 0;
3720#ifdef KFSCACHE_CFG_UTF16
3721 pCache->cUtf16Paths = 0;
3722 pCache->cUtf16PathCollisions = 0;
3723 pCache->cbUtf16Paths = 0;
3724#endif
3725 return pCache;
3726 }
3727
3728 kHlpFree(pCache);
3729 }
3730 return NULL;
3731}
3732
Note: See TracBrowser for help on using the repository browser.