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

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

updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 137.1 KB
Line 
1/* $Id: kFsCache.c 2866 2016-09-02 23:04:26Z 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 /*
810 * Convert the names to ANSI first, that way we know all the lengths.
811 */
812 char szName[KFSCACHE_CFG_MAX_ANSI_NAME];
813 int cchName = WideCharToMultiByte(CP_ACP, 0, pwcName, cwcName, szName, sizeof(szName) - 1, NULL, NULL);
814 if (cchName >= 0)
815 {
816#ifdef KFSCACHE_CFG_SHORT_NAMES
817 char szShortName[12*3 + 1];
818 int cchShortName = 0;
819 if ( cwcShortName == 0
820 || (cchShortName = WideCharToMultiByte(CP_ACP, 0, pwcShortName, cwcShortName,
821 szShortName, sizeof(szShortName) - 1, NULL, NULL)) > 0)
822#endif
823 {
824 /*
825 * Shortening is easy for non-directory objects, for
826 * directory object we're only good when the length doesn't change
827 * on any of the components (cchParent et al).
828 *
829 * This deals with your typical xxxx.ext.tmp -> xxxx.ext renames.
830 */
831 if ( cchName <= pCur->cchName
832#ifdef KFSCACHE_CFG_UTF16
833 && cwcName <= pCur->cwcName
834#endif
835#ifdef KFSCACHE_CFG_SHORT_NAMES
836 && ( cchShortName == 0
837 || ( cchShortName <= pCur->cchShortName
838 && pCur->pszShortName != pCur->pszName
839# ifdef KFSCACHE_CFG_UTF16
840 && cwcShortName <= pCur->cwcShortName
841 && pCur->pwszShortName != pCur->pwszName
842# endif
843 )
844 )
845#endif
846 )
847 {
848 if ( pCur->bObjType != KFSOBJ_TYPE_DIR
849 || ( cchName == pCur->cchName
850#ifdef KFSCACHE_CFG_UTF16
851 && cwcName == pCur->cwcName
852#endif
853#ifdef KFSCACHE_CFG_SHORT_NAMES
854 && ( cchShortName == 0
855 || ( cchShortName == pCur->cchShortName
856# ifdef KFSCACHE_CFG_UTF16
857 && cwcShortName == pCur->cwcShortName
858 )
859# endif
860 )
861#endif
862 )
863 )
864 {
865 KFSCACHE_LOG(("Refreshing %ls - name changed to '%*.*ls'\n", pCur->pwszName, cwcName, cwcName, pwcName));
866 *(char *)kHlpMemPCopy((void *)pCur->pszName, szName, cchName) = '\0';
867 pCur->cchName = cchName;
868#ifdef KFSCACHE_CFG_UTF16
869 *(wchar_t *)kHlpMemPCopy((void *)pCur->pwszName, pwcName, cwcName * sizeof(wchar_t)) = '\0';
870 pCur->cwcName = cwcName;
871#endif
872#ifdef KFSCACHE_CFG_SHORT_NAMES
873 *(char *)kHlpMemPCopy((void *)pCur->pszShortName, szShortName, cchShortName) = '\0';
874 pCur->cchShortName = cchShortName;
875# ifdef KFSCACHE_CFG_UTF16
876 *(wchar_t *)kHlpMemPCopy((void *)pCur->pwszShortName, pwcShortName, cwcShortName * sizeof(wchar_t)) = '\0';
877 pCur->cwcShortName = cwcShortName;
878# endif
879#endif
880 return pCur;
881 }
882 }
883 }
884 }
885
886
887 fprintf(stderr,
888 "kFsCacheDirRefreshOldChildName - not implemented!\n"
889 " Old name: %#x '%ls'\n"
890 " New name: %#x '%*.*ls'\n"
891 " Old short: %#x '%ls'\n"
892 " New short: %#x '%*.*ls'\n",
893 pCur->cwcName, pCur->pwszName,
894 cwcName, cwcName, cwcName, pwcName,
895 pCur->cwcShortName, pCur->pwszShortName,
896 cwcShortName, cwcShortName, cwcShortName, pwcShortName);
897 __debugbreak();
898 /** @todo implement this. It's not entirely straight forward, especially if
899 * the name increases! Also, it's something that may happend during
900 * individual object refresh and we might want to share code... */
901
902 return pCur;
903}
904
905
906/**
907 * Worker for kFsCacheDirFindOldChild that checks the names after an old object
908 * has been found the file ID.
909 *
910 * @returns Pointer to the existing object if found, NULL if not.
911 * @param pDirRePop Repopulation data.
912 * @param pCur The object to check the names of.
913 * @param pwcName The file name.
914 * @param cwcName The length of the filename (in wchar_t's).
915 * @param pwcShortName The short name, if present.
916 * @param cwcShortName The length of the short name (in wchar_t's).
917 */
918K_INLINE PKFSOBJ kFsCacheDirCheckOldChildName(PKFSDIRREPOP pDirRePop, PKFSOBJ pCur, wchar_t const *pwcName, KU32 cwcName
919#ifdef KFSCACHE_CFG_SHORT_NAMES
920 , wchar_t const *pwcShortName, KU32 cwcShortName
921#endif
922 )
923{
924 if ( pCur->cwcName == cwcName
925 && kHlpMemComp(pCur->pwszName, pwcName, cwcName * sizeof(wchar_t)) == 0)
926 {
927#ifdef KFSCACHE_CFG_SHORT_NAMES
928 if (cwcShortName == 0
929 ? pCur->pwszShortName == pCur->pwszName
930 || ( pCur->cwcShortName == cwcName
931 && kHlpMemComp(pCur->pwszShortName, pCur->pwszName, cwcName * sizeof(wchar_t)) == 0)
932 : pCur->cwcShortName == cwcShortName
933 && kHlpMemComp(pCur->pwszShortName, pwcShortName, cwcShortName * sizeof(wchar_t)) == 0 )
934#endif
935 {
936 return pCur;
937 }
938 }
939#ifdef KFSCACHE_CFG_SHORT_NAMES
940 return kFsCacheDirRefreshOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
941#else
942 return kFsCacheDirRefreshOldChildName(pDirRePop, pCur, pwcName, cwcName);
943#endif
944}
945
946
947/**
948 * Worker for kFsCachePopuplateOrRefreshDir that locates an old child object
949 * while re-populating a directory.
950 *
951 * @returns Pointer to the existing object if found, NULL if not.
952 * @param pDirRePop Repopulation data.
953 * @param idFile The file ID, 0 if none.
954 * @param pwcName The file name.
955 * @param cwcName The length of the filename (in wchar_t's).
956 * @param pwcShortName The short name, if present.
957 * @param cwcShortName The length of the short name (in wchar_t's).
958 */
959static PKFSOBJ kFsCacheDirFindOldChildSlow(PKFSDIRREPOP pDirRePop, KI64 idFile, wchar_t const *pwcName, KU32 cwcName
960#ifdef KFSCACHE_CFG_SHORT_NAMES
961 , wchar_t const *pwcShortName, KU32 cwcShortName
962#endif
963 )
964{
965 KU32 cOldChildren = pDirRePop->cOldChildren;
966 KU32 const iNextOldChild = K_MIN(pDirRePop->iNextOldChild, cOldChildren - 1);
967 KU32 iCur;
968 KI32 cInc;
969 KI32 cDirLefts;
970
971 kHlpAssertReturn(cOldChildren > 0, NULL);
972
973 /*
974 * Search by file ID first, if we've got one.
975 * ASSUMES that KU32 wraps around when -1 is added to 0.
976 */
977 if ( idFile != 0
978 && idFile != KI64_MAX
979 && idFile != KI64_MIN)
980 {
981 cInc = pDirRePop->cNextOldChildInc;
982 kHlpAssert(cInc == -1 || cInc == 1);
983 for (cDirLefts = 2; cDirLefts > 0; cDirLefts--)
984 {
985 for (iCur = iNextOldChild; iCur < cOldChildren; iCur += cInc)
986 {
987 PKFSOBJ pCur = pDirRePop->papOldChildren[iCur];
988 if (pCur->Stats.st_ino == idFile)
989 {
990 /* Remove it and check the name. */
991 pDirRePop->cOldChildren = --cOldChildren;
992 if (iCur < cOldChildren)
993 pDirRePop->papOldChildren[iCur] = pDirRePop->papOldChildren[cOldChildren];
994 else
995 cInc = -1;
996 pDirRePop->cNextOldChildInc = cInc;
997 pDirRePop->iNextOldChild = iCur + cInc;
998
999#ifdef KFSCACHE_CFG_SHORT_NAMES
1000 return kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1001#else
1002 return kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1003#endif
1004 }
1005 }
1006 cInc = -cInc;
1007 }
1008 }
1009
1010 /*
1011 * Search by name.
1012 * ASSUMES that KU32 wraps around when -1 is added to 0.
1013 */
1014 cInc = pDirRePop->cNextOldChildInc;
1015 kHlpAssert(cInc == -1 || cInc == 1);
1016 for (cDirLefts = 2; cDirLefts > 0; cDirLefts--)
1017 {
1018 for (iCur = iNextOldChild; iCur < cOldChildren; iCur += cInc)
1019 {
1020 PKFSOBJ pCur = pDirRePop->papOldChildren[iCur];
1021 if ( ( pCur->cwcName == cwcName
1022 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
1023#ifdef KFSCACHE_CFG_SHORT_NAMES
1024 || ( pCur->cwcShortName == cwcName
1025 && pCur->pwszShortName != pCur->pwszName
1026 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
1027#endif
1028 )
1029 {
1030 /* Do this first so the compiler can share the rest with the above file ID return. */
1031 if (pCur->Stats.st_ino == idFile)
1032 { /* likely */ }
1033 else
1034 pCur = kFsCacheDirRefreshOldChildFileId(pDirRePop, pCur, idFile);
1035
1036 /* Remove it and check the name. */
1037 pDirRePop->cOldChildren = --cOldChildren;
1038 if (iCur < cOldChildren)
1039 pDirRePop->papOldChildren[iCur] = pDirRePop->papOldChildren[cOldChildren];
1040 else
1041 cInc = -1;
1042 pDirRePop->cNextOldChildInc = cInc;
1043 pDirRePop->iNextOldChild = iCur + cInc;
1044
1045#ifdef KFSCACHE_CFG_SHORT_NAMES
1046 return kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1047#else
1048 return kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1049#endif
1050 }
1051 }
1052 cInc = -cInc;
1053 }
1054
1055 return NULL;
1056}
1057
1058
1059
1060/**
1061 * Worker for kFsCachePopuplateOrRefreshDir that locates an old child object
1062 * while re-populating a directory.
1063 *
1064 * @returns Pointer to the existing object if found, NULL if not.
1065 * @param pDirRePop Repopulation data.
1066 * @param idFile The file ID, 0 if none.
1067 * @param pwcName The file name.
1068 * @param cwcName The length of the filename (in wchar_t's).
1069 * @param pwcShortName The short name, if present.
1070 * @param cwcShortName The length of the short name (in wchar_t's).
1071 */
1072K_INLINE PKFSOBJ kFsCacheDirFindOldChild(PKFSDIRREPOP pDirRePop, KI64 idFile, wchar_t const *pwcName, KU32 cwcName
1073#ifdef KFSCACHE_CFG_SHORT_NAMES
1074 , wchar_t const *pwcShortName, KU32 cwcShortName
1075#endif
1076 )
1077{
1078 /*
1079 * We only check the iNextOldChild element here, hoping that the compiler
1080 * will actually inline this code, letting the slow version of the function
1081 * do the rest.
1082 */
1083 KU32 cOldChildren = pDirRePop->cOldChildren;
1084 if (cOldChildren > 0)
1085 {
1086 KU32 const iNextOldChild = K_MIN(pDirRePop->iNextOldChild, cOldChildren);
1087 PKFSOBJ pCur = pDirRePop->papOldChildren[iNextOldChild];
1088
1089 if ( pCur->Stats.st_ino == idFile
1090 && idFile != 0
1091 && idFile != KI64_MAX
1092 && idFile != KI64_MIN)
1093 pCur = kFsCacheDirCheckOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1094 else if ( pCur->cwcName == cwcName
1095 && kHlpMemComp(pCur->pwszName, pwcName, cwcName * sizeof(wchar_t)) == 0)
1096 {
1097 if (pCur->Stats.st_ino == idFile)
1098 { /* likely */ }
1099 else
1100 pCur = kFsCacheDirRefreshOldChildFileId(pDirRePop, pCur, idFile);
1101
1102#ifdef KFSCACHE_CFG_SHORT_NAMES
1103 if (cwcShortName == 0
1104 ? pCur->pwszShortName == pCur->pwszName
1105 || ( pCur->cwcShortName == cwcName
1106 && kHlpMemComp(pCur->pwszShortName, pCur->pwszName, cwcName * sizeof(wchar_t)) == 0)
1107 : pCur->cwcShortName == cwcShortName
1108 && kHlpMemComp(pCur->pwszShortName, pwcShortName, cwcShortName * sizeof(wchar_t)) == 0 )
1109 { /* likely */ }
1110 else
1111 pCur = kFsCacheDirRefreshOldChildName(pDirRePop, pCur, pwcName, cwcName, pwcShortName, cwcShortName);
1112#endif
1113 }
1114 else
1115 pCur = NULL;
1116 if (pCur)
1117 {
1118 /*
1119 * Got a match. Remove the child from the array, replacing it with
1120 * the last element. (This means we're reversing the second half of
1121 * the elements, which is why we need cNextOldChildInc.)
1122 */
1123 pDirRePop->cOldChildren = --cOldChildren;
1124 if (iNextOldChild < cOldChildren)
1125 pDirRePop->papOldChildren[iNextOldChild] = pDirRePop->papOldChildren[cOldChildren];
1126 pDirRePop->iNextOldChild = iNextOldChild + pDirRePop->cNextOldChildInc;
1127 return pCur;
1128 }
1129
1130#ifdef KFSCACHE_CFG_SHORT_NAMES
1131 return kFsCacheDirFindOldChildSlow(pDirRePop, idFile, pwcName, cwcName, pwcShortName, cwcShortName);
1132#else
1133 return kFsCacheDirFindOldChildSlow(pDirRePop, idFile, pwcName, cwcName);
1134#endif
1135 }
1136
1137 return NULL;
1138}
1139
1140
1141
1142/**
1143 * Does the initial directory populating or refreshes it if it has been
1144 * invalidated.
1145 *
1146 * This assumes the parent directory is opened.
1147 *
1148 * @returns K_TRUE on success, K_FALSE on error.
1149 * @param pCache The cache.
1150 * @param pDir The directory.
1151 * @param penmError Where to store K_FALSE explanation.
1152 */
1153static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
1154{
1155 KBOOL fRefreshing = K_FALSE;
1156 KFSDIRREPOP DirRePop = { NULL, 0, 0, 0, NULL };
1157 MY_UNICODE_STRING UniStrStar = { 1 * sizeof(wchar_t), 2 * sizeof(wchar_t), L"*" };
1158
1159 /** @todo May have to make this more flexible wrt information classes since
1160 * older windows versions (XP, w2K) might not correctly support the
1161 * ones with file ID on all file systems. */
1162#ifdef KFSCACHE_CFG_SHORT_NAMES
1163 MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdBothDirectoryInformation;
1164 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
1165#else
1166 MY_FILE_INFORMATION_CLASS const enmInfoClassWithId = MyFileIdFullDirectoryInformation;
1167 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
1168#endif
1169 MY_NTSTATUS rcNt;
1170 MY_IO_STATUS_BLOCK Ios;
1171 union
1172 {
1173 /* Include the structures for better alignment. */
1174 MY_FILE_ID_BOTH_DIR_INFORMATION WithId;
1175 MY_FILE_ID_FULL_DIR_INFORMATION NoId;
1176 /* Buffer padding. We're using a 56KB buffer here to avoid size troubles with CIFS and such. */
1177 KU8 abBuf[56*1024];
1178 } uBuf;
1179
1180
1181 /*
1182 * Open the directory.
1183 */
1184 if (pDir->hDir == INVALID_HANDLE_VALUE)
1185 {
1186 MY_OBJECT_ATTRIBUTES ObjAttr;
1187 MY_UNICODE_STRING UniStr;
1188
1189 kHlpAssert(!pDir->fPopulated);
1190
1191 Ios.Information = -1;
1192 Ios.u.Status = -1;
1193
1194 UniStr.Buffer = (wchar_t *)pDir->Obj.pwszName;
1195 UniStr.Length = (USHORT)(pDir->Obj.cwcName * sizeof(wchar_t));
1196 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1197
1198 kHlpAssertStmtReturn(pDir->Obj.pParent, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
1199 kHlpAssertStmtReturn(pDir->Obj.pParent->hDir != INVALID_HANDLE_VALUE, *penmError = KFSLOOKUPERROR_INTERNAL_ERROR, K_FALSE);
1200 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pDir->Obj.pParent->hDir, NULL /*pSecAttr*/);
1201
1202 /** @todo FILE_OPEN_REPARSE_POINT? */
1203 rcNt = g_pfnNtCreateFile(&pDir->hDir,
1204 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1205 &ObjAttr,
1206 &Ios,
1207 NULL, /*cbFileInitialAlloc */
1208 FILE_ATTRIBUTE_NORMAL,
1209 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1210 FILE_OPEN,
1211 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1212 NULL, /*pEaBuffer*/
1213 0); /*cbEaBuffer*/
1214 if (MY_NT_SUCCESS(rcNt))
1215 { /* likely */ }
1216 else
1217 {
1218 pDir->hDir = INVALID_HANDLE_VALUE;
1219 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR;
1220 return K_FALSE;
1221 }
1222 }
1223 /*
1224 * When re-populating, we replace papChildren in the directory and pick
1225 * from the old one as we go along.
1226 */
1227 else if (pDir->fPopulated)
1228 {
1229 KU32 cAllocated = K_ALIGN_Z(pDir->cChildren, 16);
1230 void *pvNew = kHlpAlloc(sizeof(pDir->papChildren[0]) * cAllocated);
1231 if (pvNew)
1232 {
1233 DirRePop.papOldChildren = pDir->papChildren;
1234 DirRePop.cOldChildren = pDir->cChildren;
1235 DirRePop.iNextOldChild = 0;
1236 DirRePop.cNextOldChildInc = 1;
1237 DirRePop.pCache = pCache;
1238
1239 pDir->cChildren = 0;
1240 pDir->cChildrenAllocated = cAllocated;
1241 pDir->papChildren = (PKFSOBJ *)pvNew;
1242 }
1243 else
1244 {
1245 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
1246 return K_FALSE;
1247 }
1248
1249 fRefreshing = K_TRUE;
1250 }
1251 if (!fRefreshing)
1252 KFSCACHE_LOG(("Populating %s...\n", pDir->Obj.pszName));
1253 else
1254 KFSCACHE_LOG(("Refreshing %s...\n", pDir->Obj.pszName));
1255
1256 /*
1257 * Enumerate the directory content.
1258 *
1259 * Note! The "*" filter is necessary because kFsCacheRefreshObj may have
1260 * previously quried a single file name and just passing NULL would
1261 * restart that single file name query.
1262 */
1263 Ios.Information = -1;
1264 Ios.u.Status = -1;
1265 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1266 NULL, /* hEvent */
1267 NULL, /* pfnApcComplete */
1268 NULL, /* pvApcCompleteCtx */
1269 &Ios,
1270 &uBuf,
1271 sizeof(uBuf),
1272 enmInfoClass,
1273 FALSE, /* fReturnSingleEntry */
1274 &UniStrStar, /* Filter / restart pos. */
1275 TRUE); /* fRestartScan */
1276 while (MY_NT_SUCCESS(rcNt))
1277 {
1278 /*
1279 * Process the entries in the buffer.
1280 */
1281 KSIZE offBuf = 0;
1282 for (;;)
1283 {
1284 union
1285 {
1286 KU8 *pb;
1287#ifdef KFSCACHE_CFG_SHORT_NAMES
1288 MY_FILE_ID_BOTH_DIR_INFORMATION *pWithId;
1289 MY_FILE_BOTH_DIR_INFORMATION *pNoId;
1290#else
1291 MY_FILE_ID_FULL_DIR_INFORMATION *pWithId;
1292 MY_FILE_FULL_DIR_INFORMATION *pNoId;
1293#endif
1294 } uPtr;
1295 PKFSOBJ pCur;
1296 KU32 offNext;
1297 KU32 cbMinCur;
1298 wchar_t *pwchFilename;
1299
1300 /* ASSUME only the FileName member differs between the two structures. */
1301 uPtr.pb = &uBuf.abBuf[offBuf];
1302 if (enmInfoClass == enmInfoClassWithId)
1303 {
1304 pwchFilename = &uPtr.pWithId->FileName[0];
1305 cbMinCur = (KU32)((uintptr_t)&uPtr.pWithId->FileName[0] - (uintptr_t)uPtr.pWithId);
1306 cbMinCur += uPtr.pNoId->FileNameLength;
1307 }
1308 else
1309 {
1310 pwchFilename = &uPtr.pNoId->FileName[0];
1311 cbMinCur = (KU32)((uintptr_t)&uPtr.pNoId->FileName[0] - (uintptr_t)uPtr.pNoId);
1312 cbMinCur += uPtr.pNoId->FileNameLength;
1313 }
1314
1315 /* We need to skip the '.' and '..' entries. */
1316 if ( *pwchFilename != '.'
1317 || uPtr.pNoId->FileNameLength > 4
1318 || !( uPtr.pNoId->FileNameLength == 2
1319 || ( uPtr.pNoId->FileNameLength == 4
1320 && pwchFilename[1] == '.') )
1321 )
1322 {
1323 KBOOL fRc;
1324 KU8 const bObjType = uPtr.pNoId->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR
1325 : uPtr.pNoId->FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)
1326 ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE;
1327
1328 /*
1329 * If refreshing, we must first see if this directory entry already
1330 * exists.
1331 */
1332 if (!fRefreshing)
1333 pCur = NULL;
1334 else
1335 {
1336 pCur = kFsCacheDirFindOldChild(&DirRePop,
1337 enmInfoClass == enmInfoClassWithId ? uPtr.pWithId->FileId.QuadPart : 0,
1338 pwchFilename, uPtr.pWithId->FileNameLength / sizeof(wchar_t)
1339#ifdef KFSCACHE_CFG_SHORT_NAMES
1340 , uPtr.pWithId->ShortName, uPtr.pWithId->ShortNameLength / sizeof(wchar_t)
1341#endif
1342 );
1343 if (pCur)
1344 {
1345 if (pCur->bObjType == bObjType)
1346 {
1347 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
1348 {
1349 PKFSDIR pCurDir = (PKFSDIR)pCur;
1350 if ( !pCurDir->fPopulated
1351 || ( pCurDir->iLastWrite == uPtr.pWithId->LastWriteTime.QuadPart
1352 && (pCur->fFlags & KFSOBJ_F_WORKING_DIR_MTIME) ) )
1353 { /* kind of likely */ }
1354 else
1355 {
1356 KFSCACHE_LOG(("Refreshing %s/%s/ - %s/ needs re-populating...\n",
1357 pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pCur->pszName));
1358 pCurDir->fNeedRePopulating = K_TRUE;
1359 }
1360 }
1361 }
1362 else if (pCur->bObjType == KFSOBJ_TYPE_MISSING)
1363 {
1364 KFSCACHE_LOG(("Refreshing %s/%s/ - %s appeared as %u, was missing.\n",
1365 pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pCur->pszName, bObjType));
1366 pCur->bObjType = bObjType;
1367 }
1368 else
1369 {
1370 KFSCACHE_LOG(("Refreshing %s/%s/ - %s changed type from %u to %u! Dropping old object.\n",
1371 pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pCur->pszName,
1372 pCur->bObjType, bObjType));
1373 kFsCacheObjRelease(pCache, pCur);
1374 pCur = NULL;
1375 }
1376 }
1377 else
1378 KFSCACHE_LOG(("Refreshing %s/%s/ - %*.*ls added.\n", pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName,
1379 uPtr.pNoId->FileNameLength / sizeof(wchar_t), uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1380 pwchFilename));
1381 }
1382
1383 if (!pCur)
1384 {
1385 /*
1386 * Create the entry (not linked yet).
1387 */
1388 pCur = kFsCacheCreateObjectW(pCache, pDir, pwchFilename, uPtr.pNoId->FileNameLength / sizeof(wchar_t),
1389#ifdef KFSCACHE_CFG_SHORT_NAMES
1390 uPtr.pNoId->ShortName, uPtr.pNoId->ShortNameLength / sizeof(wchar_t),
1391#endif
1392 bObjType, penmError);
1393 if (!pCur)
1394 return K_FALSE;
1395 kHlpAssert(pCur->cRefs == 1);
1396 }
1397
1398#ifdef KFSCACHE_CFG_SHORT_NAMES
1399 if (enmInfoClass == enmInfoClassWithId)
1400 birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
1401 else
1402 birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
1403#else
1404 if (enmInfoClass == enmInfoClassWithId)
1405 birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
1406 else
1407 birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
1408#endif
1409 pCur->Stats.st_dev = pDir->uDevNo;
1410 pCur->fHaveStats = K_TRUE;
1411
1412 /*
1413 * Add the entry to the directory.
1414 */
1415 fRc = kFsCacheDirAddChild(pCache, pDir, pCur, penmError);
1416 kFsCacheObjRelease(pCache, pCur);
1417 if (fRc)
1418 { /* likely */ }
1419 else
1420 {
1421 rcNt = STATUS_NO_MEMORY;
1422 break;
1423 }
1424 }
1425 /*
1426 * When seeing '.' we update the directory info.
1427 */
1428 else if (uPtr.pNoId->FileNameLength == 2)
1429 {
1430 pDir->iLastWrite = uPtr.pNoId->LastWriteTime.QuadPart;
1431#ifdef KFSCACHE_CFG_SHORT_NAMES
1432 if (enmInfoClass == enmInfoClassWithId)
1433 birdStatFillFromFileIdBothDirInfo(&pDir->Obj.Stats, uPtr.pWithId, pDir->Obj.pszName);
1434 else
1435 birdStatFillFromFileBothDirInfo(&pDir->Obj.Stats, uPtr.pNoId, pDir->Obj.pszName);
1436#else
1437 if (enmInfoClass == enmInfoClassWithId)
1438 birdStatFillFromFileIdFullDirInfo(&pDir->Obj.Stats, uPtr.pWithId, pDir->Obj.pszName);
1439 else
1440 birdStatFillFromFileFullDirInfo(&pDir->Obj.Stats, uPtr.pNoId, pDir->Obj.pszName);
1441#endif
1442 }
1443
1444 /*
1445 * Advance.
1446 */
1447 offNext = uPtr.pNoId->NextEntryOffset;
1448 if ( offNext >= cbMinCur
1449 && offNext < sizeof(uBuf))
1450 offBuf += offNext;
1451 else
1452 break;
1453 }
1454
1455 /*
1456 * Read the next chunk.
1457 */
1458 rcNt = g_pfnNtQueryDirectoryFile(pDir->hDir,
1459 NULL, /* hEvent */
1460 NULL, /* pfnApcComplete */
1461 NULL, /* pvApcCompleteCtx */
1462 &Ios,
1463 &uBuf,
1464 sizeof(uBuf),
1465 enmInfoClass,
1466 FALSE, /* fReturnSingleEntry */
1467 &UniStrStar, /* Filter / restart pos. */
1468 FALSE); /* fRestartScan */
1469 }
1470
1471 if (rcNt == MY_STATUS_NO_MORE_FILES)
1472 {
1473 /*
1474 * If refreshing, add missing children objects and ditch the rest.
1475 * We ignore errors while adding missing children (lazy bird).
1476 */
1477 if (!fRefreshing)
1478 { /* more likely */ }
1479 else
1480 {
1481 while (DirRePop.cOldChildren > 0)
1482 {
1483 KFSLOOKUPERROR enmErrorIgn;
1484 PKFSOBJ pOldChild = DirRePop.papOldChildren[--DirRePop.cOldChildren];
1485 if (pOldChild->bObjType == KFSOBJ_TYPE_MISSING)
1486 kFsCacheDirAddChild(pCache, pDir, pOldChild, &enmErrorIgn);
1487 else
1488 {
1489 KFSCACHE_LOG(("Refreshing %s/%s/ - %s was removed.\n",
1490 pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pOldChild->pszName));
1491 kHlpAssert(pOldChild->bObjType != KFSOBJ_TYPE_DIR);
1492 }
1493 kFsCacheObjRelease(pCache, pOldChild);
1494 }
1495 kHlpFree(DirRePop.papOldChildren);
1496 }
1497
1498 /*
1499 * Mark the directory as fully populated and up to date.
1500 */
1501 pDir->fPopulated = K_TRUE;
1502 pDir->fNeedRePopulating = K_FALSE;
1503 if (pDir->Obj.uCacheGen != KFSOBJ_CACHE_GEN_IGNORE)
1504 pDir->Obj.uCacheGen = pCache->uGeneration;
1505 return K_TRUE;
1506 }
1507
1508 /*
1509 * If we failed during refresh, add back remaining old children.
1510 */
1511 if (!fRefreshing)
1512 {
1513 while (DirRePop.cOldChildren > 0)
1514 {
1515 KFSLOOKUPERROR enmErrorIgn;
1516 PKFSOBJ pOldChild = DirRePop.papOldChildren[--DirRePop.cOldChildren];
1517 kFsCacheDirAddChild(pCache, pDir, pOldChild, &enmErrorIgn);
1518 kFsCacheObjRelease(pCache, pOldChild);
1519 }
1520 kHlpFree(DirRePop.papOldChildren);
1521 }
1522
1523 kHlpAssertMsgFailed(("%#x\n", rcNt));
1524 *penmError = KFSLOOKUPERROR_DIR_READ_ERROR;
1525 return K_TRUE;
1526}
1527
1528
1529/**
1530 * Does the initial directory populating or refreshes it if it has been
1531 * invalidated.
1532 *
1533 * This assumes the parent directory is opened.
1534 *
1535 * @returns K_TRUE on success, K_FALSE on error.
1536 * @param pCache The cache.
1537 * @param pDir The directory.
1538 * @param penmError Where to store K_FALSE explanation. Optional.
1539 */
1540KBOOL kFsCacheDirEnsurePopuplated(PKFSCACHE pCache, PKFSDIR pDir, KFSLOOKUPERROR *penmError)
1541{
1542 KFSLOOKUPERROR enmIgnored;
1543 if ( pDir->fPopulated
1544 && !pDir->fNeedRePopulating
1545 && ( pDir->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
1546 || pDir->Obj.uCacheGen == pCache->uGeneration) )
1547 return K_TRUE;
1548 return kFsCachePopuplateOrRefreshDir(pCache, pDir, penmError ? penmError : &enmIgnored);
1549}
1550
1551
1552/**
1553 * Checks whether the modified timestamp differs on this directory.
1554 *
1555 * @returns K_TRUE if possibly modified, K_FALSE if definitely not modified.
1556 * @param pDir The directory..
1557 */
1558static KBOOL kFsCacheDirIsModified(PKFSDIR pDir)
1559{
1560 if ( pDir->hDir != INVALID_HANDLE_VALUE
1561 && (pDir->Obj.fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
1562 {
1563 MY_IO_STATUS_BLOCK Ios;
1564 MY_FILE_BASIC_INFORMATION BasicInfo;
1565 MY_NTSTATUS rcNt;
1566
1567 Ios.Information = -1;
1568 Ios.u.Status = -1;
1569
1570 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
1571 if (MY_NT_SUCCESS(rcNt))
1572 return BasicInfo.LastWriteTime.QuadPart != pDir->iLastWrite;
1573 }
1574 /* The cache root never changes. */
1575 else if (!pDir->Obj.pParent)
1576 return K_FALSE;
1577
1578 return K_TRUE;
1579}
1580
1581
1582static KBOOL kFsCacheRefreshMissing(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1583{
1584 /*
1585 * If we can, we start by checking whether the parent directory
1586 * has been modified. If it has, we need to check if this entry
1587 * was added or not, most likely it wasn't added.
1588 */
1589 if (!kFsCacheDirIsModified(pMissing->pParent))
1590 {
1591 KFSCACHE_LOG(("Parent of missing not written to %s/%s\n", pMissing->pParent->Obj.pszName, pMissing->pszName));
1592 pMissing->uCacheGen = pCache->uGenerationMissing;
1593 }
1594 else
1595 {
1596 MY_UNICODE_STRING UniStr;
1597 MY_OBJECT_ATTRIBUTES ObjAttr;
1598 MY_FILE_BASIC_INFORMATION BasicInfo;
1599 MY_NTSTATUS rcNt;
1600
1601 UniStr.Buffer = (wchar_t *)pMissing->pwszName;
1602 UniStr.Length = (USHORT)(pMissing->cwcName * sizeof(wchar_t));
1603 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1604
1605 kHlpAssert(pMissing->pParent->hDir != INVALID_HANDLE_VALUE);
1606 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pMissing->pParent->hDir, NULL /*pSecAttr*/);
1607
1608 rcNt = g_pfnNtQueryAttributesFile(&ObjAttr, &BasicInfo);
1609 if (!MY_NT_SUCCESS(rcNt))
1610 {
1611 /*
1612 * Probably more likely that a missing node stays missing.
1613 */
1614 pMissing->uCacheGen = pCache->uGenerationMissing;
1615 KFSCACHE_LOG(("Still missing %s/%s\n", pMissing->pParent->Obj.pszName, pMissing->pszName));
1616 }
1617 else
1618 {
1619 /*
1620 * We must metamorphose this node. This is tedious business
1621 * because we need to check the file name casing. We might
1622 * just as well update the parent directory...
1623 */
1624 KU8 const bObjType = BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY ? KFSOBJ_TYPE_DIR
1625 : BasicInfo.FileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)
1626 ? KFSOBJ_TYPE_OTHER : KFSOBJ_TYPE_FILE;
1627
1628 KFSCACHE_LOG(("Birth of %s/%s as %d with attribs %#x...\n",
1629 pMissing->pParent->Obj.pszName, pMissing->pszName, bObjType, BasicInfo.FileAttributes));
1630 pMissing->bObjType = bObjType;
1631 pMissing->uCacheGen = pCache->uGenerationMissing;
1632/**
1633 * @todo refresh missing object names when it appears.
1634 */
1635 }
1636 }
1637
1638 return K_TRUE;
1639}
1640
1641
1642static KBOOL kFsCacheRefreshMissingIntermediateDir(PKFSCACHE pCache, PKFSOBJ pMissing, KFSLOOKUPERROR *penmError)
1643{
1644 if (kFsCacheRefreshMissing(pCache, pMissing, penmError))
1645 {
1646 if ( pMissing->bObjType == KFSOBJ_TYPE_DIR
1647 || pMissing->bObjType == KFSOBJ_TYPE_MISSING)
1648 return K_TRUE;
1649 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
1650 }
1651
1652 return K_FALSE;
1653}
1654
1655
1656/**
1657 * Generic object refresh.
1658 *
1659 * This does not refresh the content of directories.
1660 *
1661 * @returns K_TRUE on success. K_FALSE and *penmError on failure.
1662 * @param pCache The cache.
1663 * @param pObj The object.
1664 * @param penmError Where to return error info.
1665 */
1666static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError)
1667{
1668 KBOOL fRc;
1669
1670 /*
1671 * Since we generally assume nothing goes away in this cache, we only really
1672 * have a hard time with negative entries. So, missing stuff goes to
1673 * complicated land.
1674 */
1675 if (pObj->bObjType == KFSOBJ_TYPE_MISSING)
1676 fRc = kFsCacheRefreshMissing(pCache, pObj, penmError);
1677 else
1678 {
1679 /*
1680 * This object is supposed to exist, so all we need to do is query essential
1681 * stats again. Since we've already got handles on directories, there are
1682 * two ways to go about this.
1683 */
1684 union
1685 {
1686 MY_FILE_NETWORK_OPEN_INFORMATION FullInfo;
1687 MY_FILE_STANDARD_INFORMATION StdInfo;
1688#ifdef KFSCACHE_CFG_SHORT_NAMES
1689 MY_FILE_ID_BOTH_DIR_INFORMATION WithId;
1690 //MY_FILE_BOTH_DIR_INFORMATION NoId;
1691#else
1692 MY_FILE_ID_FULL_DIR_INFORMATION WithId;
1693 //MY_FILE_FULL_DIR_INFORMATION NoId;
1694#endif
1695 KU8 abPadding[ sizeof(wchar_t) * KFSCACHE_CFG_MAX_UTF16_NAME
1696 + sizeof(MY_FILE_ID_BOTH_DIR_INFORMATION)];
1697 } uBuf;
1698 MY_IO_STATUS_BLOCK Ios;
1699 MY_NTSTATUS rcNt;
1700 if ( pObj->bObjType != KFSOBJ_TYPE_DIR
1701 || ((PKFSDIR)pObj)->hDir == INVALID_HANDLE_VALUE)
1702 {
1703#if 0 /** @todo performance check these two alternatives. */
1704 MY_UNICODE_STRING UniStr;
1705 MY_OBJECT_ATTRIBUTES ObjAttr;
1706
1707 UniStr.Buffer = (wchar_t *)pObj->pwszName;
1708 UniStr.Length = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1709 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1710
1711 kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1712 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pObj->pParent->hDir, NULL /*pSecAttr*/);
1713
1714 rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.FullInfo);
1715 if (MY_NT_SUCCESS(rcNt))
1716 {
1717 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
1718 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1719 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1720 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1721 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1722 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
1723 pObj->Stats.st_blksize = 65536;
1724 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1725 / BIRD_STAT_BLOCK_SIZE;
1726 }
1727#else
1728 /* This alternative lets us keep the inode number up to date and
1729 detect name case changes. */
1730 MY_UNICODE_STRING UniStr;
1731# ifdef KFSCACHE_CFG_SHORT_NAMES
1732 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdBothDirectoryInformation;
1733# else
1734 MY_FILE_INFORMATION_CLASS enmInfoClass = MyFileIdFullDirectoryInformation;
1735# endif
1736
1737 UniStr.Buffer = (wchar_t *)pObj->pwszName;
1738 UniStr.Length = (USHORT)(pObj->cwcName * sizeof(wchar_t));
1739 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
1740
1741 kHlpAssert(pObj->pParent->hDir != INVALID_HANDLE_VALUE);
1742
1743 Ios.Information = -1;
1744 Ios.u.Status = -1;
1745 rcNt = g_pfnNtQueryDirectoryFile(pObj->pParent->hDir,
1746 NULL, /* hEvent */
1747 NULL, /* pfnApcComplete */
1748 NULL, /* pvApcCompleteCtx */
1749 &Ios,
1750 &uBuf,
1751 sizeof(uBuf),
1752 enmInfoClass,
1753 TRUE, /* fReturnSingleEntry */
1754 &UniStr, /* Filter / restart pos. */
1755 TRUE); /* fRestartScan */
1756
1757 if (MY_NT_SUCCESS(rcNt))
1758 {
1759 if (pObj->Stats.st_ino == uBuf.WithId.FileId.QuadPart)
1760 KFSCACHE_LOG(("Refreshing %s/%s, no ID change...\n", pObj->pParent->Obj.pszName, pObj->pszName));
1761 else if ( pObj->cwcName == uBuf.WithId.FileNameLength / sizeof(wchar_t)
1762# ifdef KFSCACHE_CFG_SHORT_NAMES
1763 && ( uBuf.WithId.ShortNameLength == 0
1764 ? pObj->pwszName == pObj->pwszShortName
1765 || ( pObj->cwcName == pObj->cwcShortName
1766 && memcmp(pObj->pwszName, pObj->pwszShortName, pObj->cwcName * sizeof(wchar_t)) == 0)
1767 : pObj->cwcShortName == uBuf.WithId.ShortNameLength / sizeof(wchar_t)
1768 && memcmp(pObj->pwszShortName, uBuf.WithId.ShortName, uBuf.WithId.ShortNameLength) == 0
1769 )
1770# endif
1771 && memcmp(pObj->pwszName, uBuf.WithId.FileName, uBuf.WithId.FileNameLength) == 0
1772 )
1773 {
1774 KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx...\n",
1775 pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1776 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1777 }
1778 else
1779 {
1780 KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx and names too...\n",
1781 pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
1782 fprintf(stderr, "kFsCacheRefreshObj - ID + name change not implemented!!\n");
1783 __debugbreak();
1784 pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
1785 /** @todo implement as needed. */
1786 }
1787
1788 pObj->Stats.st_size = uBuf.WithId.EndOfFile.QuadPart;
1789 birdNtTimeToTimeSpec(uBuf.WithId.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1790 birdNtTimeToTimeSpec(uBuf.WithId.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1791 birdNtTimeToTimeSpec(uBuf.WithId.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1792 birdNtTimeToTimeSpec(uBuf.WithId.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1793 pObj->Stats.st_attribs = uBuf.WithId.FileAttributes;
1794 pObj->Stats.st_blksize = 65536;
1795 pObj->Stats.st_blocks = (uBuf.WithId.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1796 / BIRD_STAT_BLOCK_SIZE;
1797 }
1798#endif
1799 if (MY_NT_SUCCESS(rcNt))
1800 {
1801 pObj->uCacheGen = pCache->uGeneration;
1802 fRc = K_TRUE;
1803 }
1804 else
1805 {
1806 /* ouch! */
1807 kHlpAssertMsgFailed(("%#x\n", rcNt));
1808 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on non-dir - not implemented!\n", rcNt);
1809 __debugbreak();
1810 fRc = K_FALSE;
1811 }
1812 }
1813 else
1814 {
1815 /*
1816 * An open directory. Query information via the handle, the
1817 * file ID shouldn't have been able to change, so we can use
1818 * NtQueryInformationFile. Right...
1819 */
1820 PKFSDIR pDir = (PKFSDIR)pObj;
1821 Ios.Information = -1;
1822 Ios.u.Status = -1;
1823 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &uBuf.FullInfo, sizeof(uBuf.FullInfo),
1824 MyFileNetworkOpenInformation);
1825 if (MY_NT_SUCCESS(rcNt))
1826 rcNt = Ios.u.Status;
1827 if (MY_NT_SUCCESS(rcNt))
1828 {
1829 pObj->Stats.st_size = uBuf.FullInfo.EndOfFile.QuadPart;
1830 birdNtTimeToTimeSpec(uBuf.FullInfo.CreationTime.QuadPart, &pObj->Stats.st_birthtim);
1831 birdNtTimeToTimeSpec(uBuf.FullInfo.ChangeTime.QuadPart, &pObj->Stats.st_ctim);
1832 birdNtTimeToTimeSpec(uBuf.FullInfo.LastWriteTime.QuadPart, &pObj->Stats.st_mtim);
1833 birdNtTimeToTimeSpec(uBuf.FullInfo.LastAccessTime.QuadPart, &pObj->Stats.st_atim);
1834 pObj->Stats.st_attribs = uBuf.FullInfo.FileAttributes;
1835 pObj->Stats.st_blksize = 65536;
1836 pObj->Stats.st_blocks = (uBuf.FullInfo.AllocationSize.QuadPart + BIRD_STAT_BLOCK_SIZE - 1)
1837 / BIRD_STAT_BLOCK_SIZE;
1838
1839 if ( pDir->iLastWrite == uBuf.FullInfo.LastWriteTime.QuadPart
1840 && (pObj->fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
1841 KFSCACHE_LOG(("Refreshing %s/%s/ - no re-populating necessary.\n",
1842 pObj->pParent->Obj.pszName, pObj->pszName));
1843 else
1844 {
1845 KFSCACHE_LOG(("Refreshing %s/%s/ - needs re-populating...\n",
1846 pObj->pParent->Obj.pszName, pObj->pszName));
1847 pDir->fNeedRePopulating = K_TRUE;
1848#if 0
1849 /* Refresh the link count. */
1850 rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
1851 if (MY_NT_SUCCESS(rcNt))
1852 rcNt = Ios.s.Status;
1853 if (MY_NT_SUCCESS(rcNt))
1854 pObj->Stats.st_nlink = StdInfo.NumberOfLinks;
1855#endif
1856 }
1857 }
1858 if (MY_NT_SUCCESS(rcNt))
1859 {
1860 pObj->uCacheGen = pCache->uGeneration;
1861 fRc = K_TRUE;
1862 }
1863 else
1864 {
1865 /* ouch! */
1866 kHlpAssertMsgFailed(("%#x\n", rcNt));
1867 fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on dir - not implemented!\n", rcNt);
1868 __debugbreak();
1869 fRc = K_FALSE;
1870 }
1871 }
1872 }
1873
1874 return fRc;
1875}
1876
1877
1878
1879/**
1880 * Looks up a drive letter.
1881 *
1882 * Will enter the drive if necessary.
1883 *
1884 * @returns Pointer to the root directory of the drive or an update-to-date
1885 * missing node.
1886 * @param pCache The cache.
1887 * @param chLetter The uppercased drive letter.
1888 * @param penmError Where to return details as to why the lookup
1889 * failed.
1890 */
1891static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError)
1892{
1893 KU32 const uHash = chLetter - 'A';
1894 KU32 cLeft;
1895 PKFSOBJ *ppCur;
1896
1897 MY_UNICODE_STRING NtPath;
1898 wchar_t wszTmp[8];
1899 char szTmp[4];
1900
1901 /*
1902 * Custom drive letter hashing.
1903 */
1904 if (pCache->RootDir.paHashTab)
1905 {
1906 /** @todo PKFSOBJHASH pHash = */
1907 }
1908
1909 /*
1910 * Special cased lookup.
1911 */
1912 cLeft = pCache->RootDir.cChildren;
1913 ppCur = pCache->RootDir.papChildren;
1914 while (cLeft-- > 0)
1915 {
1916 PKFSOBJ pCur = *ppCur++;
1917 if ( pCur->cchName == 2
1918 && pCur->pszName[0] == chLetter
1919 && pCur->pszName[1] == ':')
1920 {
1921 if (pCur->bObjType == KFSOBJ_TYPE_DIR)
1922 return pCur;
1923 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
1924 if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
1925 return pCur;
1926 return NULL;
1927 }
1928 }
1929
1930 /*
1931 * Need to add it. We always keep the drive letters open for the benefit
1932 * of kFsCachePopuplateOrRefreshDir and others.
1933 */
1934 wszTmp[0] = szTmp[0] = chLetter;
1935 wszTmp[1] = szTmp[1] = ':';
1936 wszTmp[2] = szTmp[2] = '\\';
1937 wszTmp[3] = '.';
1938 wszTmp[4] = '\0';
1939 szTmp[2] = '\0';
1940
1941 NtPath.Buffer = NULL;
1942 NtPath.Length = 0;
1943 NtPath.MaximumLength = 0;
1944 if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, &NtPath, NULL, NULL))
1945 {
1946 HANDLE hDir;
1947 MY_NTSTATUS rcNt;
1948 rcNt = birdOpenFileUniStr(&NtPath,
1949 FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
1950 FILE_ATTRIBUTE_NORMAL,
1951 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1952 FILE_OPEN,
1953 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1954 OBJ_CASE_INSENSITIVE,
1955 &hDir);
1956 birdFreeNtPath(&NtPath);
1957 if (MY_NT_SUCCESS(rcNt))
1958 {
1959 PKFSDIR pDir = (PKFSDIR)kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
1960#ifdef KFSCACHE_CFG_SHORT_NAMES
1961 NULL, 0, NULL, 0,
1962#endif
1963 KFSOBJ_TYPE_DIR, penmError);
1964 if (pDir)
1965 {
1966 /*
1967 * We need a little bit of extra info for a drive root. These things are typically
1968 * inherited by subdirectories down the tree, so, we do it all here for till that changes.
1969 */
1970 union
1971 {
1972 MY_FILE_FS_VOLUME_INFORMATION VolInfo;
1973 MY_FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
1974 char abPadding[sizeof(MY_FILE_FS_VOLUME_INFORMATION) + 512];
1975 } uBuf;
1976 MY_IO_STATUS_BLOCK Ios;
1977 KBOOL fRc;
1978
1979 kHlpAssert(pDir->hDir == INVALID_HANDLE_VALUE);
1980 pDir->hDir = hDir;
1981
1982 if (birdStatHandle(hDir, &pDir->Obj.Stats, pDir->Obj.pszName) == 0)
1983 {
1984 pDir->Obj.fHaveStats = K_TRUE;
1985 pDir->uDevNo = pDir->Obj.Stats.st_dev;
1986 }
1987 else
1988 {
1989 /* Just in case. */
1990 pDir->Obj.fHaveStats = K_FALSE;
1991 rcNt = birdQueryVolumeDeviceNumber(hDir, &uBuf.VolInfo, sizeof(uBuf), &pDir->uDevNo);
1992 kHlpAssertMsg(MY_NT_SUCCESS(rcNt), ("%#x\n", rcNt));
1993 }
1994
1995 /* Get the file system. */
1996 pDir->Obj.fFlags &= ~(KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME);
1997 Ios.Information = -1;
1998 Ios.u.Status = -1;
1999 rcNt = g_pfnNtQueryVolumeInformationFile(hDir, &Ios, &uBuf.FsAttrInfo, sizeof(uBuf),
2000 MyFileFsAttributeInformation);
2001 if (MY_NT_SUCCESS(rcNt))
2002 rcNt = Ios.u.Status;
2003 if (MY_NT_SUCCESS(rcNt))
2004 {
2005 if ( uBuf.FsAttrInfo.FileSystemName[0] == 'N'
2006 && uBuf.FsAttrInfo.FileSystemName[1] == 'T'
2007 && uBuf.FsAttrInfo.FileSystemName[2] == 'F'
2008 && uBuf.FsAttrInfo.FileSystemName[3] == 'S'
2009 && uBuf.FsAttrInfo.FileSystemName[4] == '\0')
2010 {
2011 DWORD dwDriveType = GetDriveTypeW(wszTmp);
2012 if ( dwDriveType == DRIVE_FIXED
2013 || dwDriveType == DRIVE_RAMDISK)
2014 pDir->Obj.fFlags |= KFSOBJ_F_NTFS | KFSOBJ_F_WORKING_DIR_MTIME;
2015 }
2016 }
2017
2018 /*
2019 * Link the new drive letter into the root dir.
2020 */
2021 fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
2022 kFsCacheObjRelease(pCache, &pDir->Obj);
2023 return fRc ? &pDir->Obj : NULL;
2024 }
2025
2026 g_pfnNtClose(hDir);
2027 return NULL;
2028 }
2029
2030 /* Assume it doesn't exist if this happens... This may be a little to
2031 restrictive wrt status code checks. */
2032 kHlpAssertMsgStmtReturn( rcNt == MY_STATUS_OBJECT_NAME_NOT_FOUND
2033 || rcNt == MY_STATUS_OBJECT_PATH_NOT_FOUND
2034 || rcNt == MY_STATUS_OBJECT_PATH_INVALID
2035 || rcNt == MY_STATUS_OBJECT_PATH_SYNTAX_BAD,
2036 ("%#x\n", rcNt),
2037 *penmError = KFSLOOKUPERROR_DIR_OPEN_ERROR,
2038 NULL);
2039 }
2040 else
2041 {
2042 kHlpAssertFailed();
2043 *penmError = KFSLOOKUPERROR_OUT_OF_MEMORY;
2044 return NULL;
2045 }
2046
2047 /*
2048 * Maybe create a missing entry.
2049 */
2050 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2051 {
2052 PKFSOBJ pMissing = kFsCacheCreateObject(pCache, &pCache->RootDir, szTmp, 2, wszTmp, 2,
2053#ifdef KFSCACHE_CFG_SHORT_NAMES
2054 NULL, 0, NULL, 0,
2055#endif
2056 KFSOBJ_TYPE_MISSING, penmError);
2057 if (pMissing)
2058 {
2059 KBOOL fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, pMissing, penmError);
2060 kFsCacheObjRelease(pCache, pMissing);
2061 return fRc ? pMissing : NULL;
2062 }
2063 }
2064 else
2065 {
2066 /** @todo this isn't necessary correct for a root spec. */
2067 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2068 }
2069 return NULL;
2070}
2071
2072
2073/**
2074 * Look up a child node, ANSI version.
2075 *
2076 * @returns Pointer to the child if found, NULL if not.
2077 * @param pCache The cache.
2078 * @param pParent The parent directory to search.
2079 * @param pchName The child name to search for (not terminated).
2080 * @param cchName The length of the child name.
2081 */
2082static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
2083{
2084 /* Check for '.' first. */
2085 if (cchName != 1 || *pchName != '.')
2086 {
2087 KU32 cLeft;
2088 PKFSOBJ *ppCur;
2089
2090 if (pParent->paHashTab != NULL)
2091 {
2092 /** @todo directory hash table lookup. */
2093 }
2094
2095 /* Linear search. */
2096 cLeft = pParent->cChildren;
2097 ppCur = pParent->papChildren;
2098 while (cLeft-- > 0)
2099 {
2100 PKFSOBJ pCur = *ppCur++;
2101 if ( ( pCur->cchName == cchName
2102 && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
2103#ifdef KFSCACHE_CFG_SHORT_NAMES
2104 || ( pCur->cchShortName == cchName
2105 && pCur->pszShortName != pCur->pszName
2106 && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
2107#endif
2108 )
2109 return pCur;
2110 }
2111 return NULL;
2112 }
2113 return &pParent->Obj;
2114}
2115
2116
2117/**
2118 * Look up a child node, UTF-16 version.
2119 *
2120 * @returns Pointer to the child if found, NULL if not.
2121 * @param pCache The cache.
2122 * @param pParent The parent directory to search.
2123 * @param pwcName The child name to search for (not terminated).
2124 * @param cwcName The length of the child name (in wchar_t's).
2125 */
2126static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
2127{
2128 /* Check for '.' first. */
2129 if (cwcName != 1 || *pwcName != '.')
2130 {
2131 KU32 cLeft;
2132 PKFSOBJ *ppCur;
2133
2134 if (pParent->paHashTab != NULL)
2135 {
2136 /** @todo directory hash table lookup. */
2137 }
2138
2139 /* Linear search. */
2140 cLeft = pParent->cChildren;
2141 ppCur = pParent->papChildren;
2142 while (cLeft-- > 0)
2143 {
2144 PKFSOBJ pCur = *ppCur++;
2145 if ( ( pCur->cwcName == cwcName
2146 && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
2147#ifdef KFSCACHE_CFG_SHORT_NAMES
2148 || ( pCur->cwcShortName == cwcName
2149 && pCur->pwszShortName != pCur->pwszName
2150 && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
2151#endif
2152 )
2153 return pCur;
2154 }
2155 return NULL;
2156 }
2157 return &pParent->Obj;
2158}
2159
2160
2161/**
2162 * Looks up a UNC share, ANSI version.
2163 *
2164 * We keep both the server and share in the root directory entry. This means we
2165 * have to clean up the entry name before we can insert it.
2166 *
2167 * @returns Pointer to the share root directory or an update-to-date missing
2168 * node.
2169 * @param pCache The cache.
2170 * @param pszPath The path.
2171 * @param poff Where to return the root dire.
2172 * @param penmError Where to return details as to why the lookup
2173 * failed.
2174 */
2175static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
2176{
2177#if 0 /* later */
2178 KU32 offStartServer;
2179 KU32 offEndServer;
2180 KU32 offStartShare;
2181
2182 KU32 offEnd = 2;
2183 while (IS_SLASH(pszPath[offEnd]))
2184 offEnd++;
2185
2186 offStartServer = offEnd;
2187 while ( (ch = pszPath[offEnd]) != '\0'
2188 && !IS_SLASH(ch))
2189 offEnd++;
2190 offEndServer = offEnd;
2191
2192 if (ch != '\0')
2193 { /* likely */ }
2194 else
2195 {
2196 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2197 return NULL;
2198 }
2199
2200 while (IS_SLASH(pszPath[offEnd]))
2201 offEnd++;
2202 offStartServer = offEnd;
2203 while ( (ch = pszPath[offEnd]) != '\0'
2204 && !IS_SLASH(ch))
2205 offEnd++;
2206#endif
2207 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2208 return NULL;
2209}
2210
2211
2212/**
2213 * Looks up a UNC share, UTF-16 version.
2214 *
2215 * We keep both the server and share in the root directory entry. This means we
2216 * have to clean up the entry name before we can insert it.
2217 *
2218 * @returns Pointer to the share root directory or an update-to-date missing
2219 * node.
2220 * @param pCache The cache.
2221 * @param pwszPath The path.
2222 * @param poff Where to return the root dire.
2223 * @param penmError Where to return details as to why the lookup
2224 * failed.
2225 */
2226static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
2227{
2228#if 0 /* later */
2229 KU32 offStartServer;
2230 KU32 offEndServer;
2231 KU32 offStartShare;
2232
2233 KU32 offEnd = 2;
2234 while (IS_SLASH(pwszPath[offEnd]))
2235 offEnd++;
2236
2237 offStartServer = offEnd;
2238 while ( (ch = pwszPath[offEnd]) != '\0'
2239 && !IS_SLASH(ch))
2240 offEnd++;
2241 offEndServer = offEnd;
2242
2243 if (ch != '\0')
2244 { /* likely */ }
2245 else
2246 {
2247 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2248 return NULL;
2249 }
2250
2251 while (IS_SLASH(pwszPath[offEnd]))
2252 offEnd++;
2253 offStartServer = offEnd;
2254 while ( (ch = pwszPath[offEnd]) != '\0'
2255 && !IS_SLASH(ch))
2256 offEnd++;
2257#endif
2258 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2259 return NULL;
2260}
2261
2262
2263/**
2264 * Walks an full path relative to the given directory, ANSI version.
2265 *
2266 * This will create any missing nodes while walking.
2267 *
2268 * The caller will have to do the path hash table insertion of the result.
2269 *
2270 * @returns Pointer to the tree node corresponding to @a pszPath.
2271 * NULL on lookup failure, see @a penmError for details.
2272 * @param pCache The cache.
2273 * @param pParent The directory to start the lookup in.
2274 * @param pszPath The path to walk.
2275 * @param cchPath The length of the path.
2276 * @param penmError Where to return details as to why the lookup
2277 * failed.
2278 */
2279PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath,
2280 KFSLOOKUPERROR *penmError)
2281{
2282 /*
2283 * Walk loop.
2284 */
2285 KU32 off = 0;
2286 for (;;)
2287 {
2288 PKFSOBJ pChild;
2289
2290 /*
2291 * Find the end of the component, counting trailing slashes.
2292 */
2293 char ch;
2294 KU32 cchSlashes = 0;
2295 KU32 offEnd = off + 1;
2296 while ((ch = pszPath[offEnd]) != '\0')
2297 {
2298 if (!IS_SLASH(ch))
2299 offEnd++;
2300 else
2301 {
2302 do
2303 cchSlashes++;
2304 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2305 break;
2306 }
2307 }
2308
2309 /*
2310 * Do we need to populate or refresh this directory first?
2311 */
2312 if ( pParent->fPopulated
2313 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2314 || pParent->Obj.uCacheGen == pCache->uGeneration) )
2315 { /* likely */ }
2316 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2317 { /* likely */ }
2318 else
2319 return NULL;
2320
2321 /*
2322 * Search the current node for the name.
2323 *
2324 * If we don't find it, we may insert a missing node depending on
2325 * the cache configuration.
2326 */
2327 pChild = kFsCacheFindChildA(pCache, pParent, &pszPath[off], offEnd - off);
2328 if (pChild != NULL)
2329 { /* probably likely */ }
2330 else
2331 {
2332 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2333 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
2334 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
2335 {
2336 if (pChild)
2337 return kFsCacheObjRetainInternal(pChild);
2338 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2339 }
2340 else
2341 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2342 return NULL;
2343 }
2344
2345 /* Advance off and check if we're done already. */
2346 off = offEnd + cchSlashes;
2347 if ( cchSlashes == 0
2348 || off >= cchPath)
2349 {
2350 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2351 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2352 || pChild->uCacheGen == pCache->uGenerationMissing
2353 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2354 { /* likely */ }
2355 else
2356 return NULL;
2357 return kFsCacheObjRetainInternal(pChild);
2358 }
2359
2360 /*
2361 * Check that it's a directory. If a missing entry, we may have to
2362 * refresh it and re-examin it.
2363 */
2364 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2365 pParent = (PKFSDIR)pChild;
2366 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2367 {
2368 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2369 return NULL;
2370 }
2371 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2372 || pChild->uCacheGen == pCache->uGenerationMissing)
2373 {
2374 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2375 return NULL;
2376 }
2377 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2378 pParent = (PKFSDIR)pChild;
2379 else
2380 return NULL;
2381 }
2382
2383 return NULL;
2384
2385}
2386
2387
2388/**
2389 * Walks an full path relative to the given directory, UTF-16 version.
2390 *
2391 * This will create any missing nodes while walking.
2392 *
2393 * The caller will have to do the path hash table insertion of the result.
2394 *
2395 * @returns Pointer to the tree node corresponding to @a pszPath.
2396 * NULL on lookup failure, see @a penmError for details.
2397 * @param pCache The cache.
2398 * @param pParent The directory to start the lookup in.
2399 * @param pszPath The path to walk. No dot-dot bits allowed!
2400 * @param cchPath The length of the path.
2401 * @param penmError Where to return details as to why the lookup
2402 * failed.
2403 */
2404PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath,
2405 KFSLOOKUPERROR *penmError)
2406{
2407 /*
2408 * Walk loop.
2409 */
2410 KU32 off = 0;
2411 for (;;)
2412 {
2413 PKFSOBJ pChild;
2414
2415 /*
2416 * Find the end of the component, counting trailing slashes.
2417 */
2418 wchar_t wc;
2419 KU32 cwcSlashes = 0;
2420 KU32 offEnd = off + 1;
2421 while ((wc = pwszPath[offEnd]) != '\0')
2422 {
2423 if (!IS_SLASH(wc))
2424 offEnd++;
2425 else
2426 {
2427 do
2428 cwcSlashes++;
2429 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2430 break;
2431 }
2432 }
2433
2434 /*
2435 * Do we need to populate or refresh this directory first?
2436 */
2437 if ( pParent->fPopulated
2438 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2439 || pParent->Obj.uCacheGen == pCache->uGeneration) )
2440 { /* likely */ }
2441 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
2442 { /* likely */ }
2443 else
2444 return NULL;
2445
2446 /*
2447 * Search the current node for the name.
2448 *
2449 * If we don't find it, we may insert a missing node depending on
2450 * the cache configuration.
2451 */
2452 pChild = kFsCacheFindChildW(pCache, pParent, &pwszPath[off], offEnd - off);
2453 if (pChild != NULL)
2454 { /* probably likely */ }
2455 else
2456 {
2457 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
2458 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
2459 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
2460 {
2461 if (pChild)
2462 return kFsCacheObjRetainInternal(pChild);
2463 *penmError = KFSLOOKUPERROR_NOT_FOUND;
2464 }
2465 else
2466 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2467 return NULL;
2468 }
2469
2470 /* Advance off and check if we're done already. */
2471 off = offEnd + cwcSlashes;
2472 if ( cwcSlashes == 0
2473 || off >= cwcPath)
2474 {
2475 if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
2476 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2477 || pChild->uCacheGen == pCache->uGenerationMissing
2478 || kFsCacheRefreshMissing(pCache, pChild, penmError) )
2479 { /* likely */ }
2480 else
2481 return NULL;
2482 return kFsCacheObjRetainInternal(pChild);
2483 }
2484
2485 /*
2486 * Check that it's a directory. If a missing entry, we may have to
2487 * refresh it and re-examin it.
2488 */
2489 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2490 pParent = (PKFSDIR)pChild;
2491 else if (pChild->bObjType != KFSOBJ_TYPE_MISSING)
2492 {
2493 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_DIR;
2494 return NULL;
2495 }
2496 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2497 || pChild->uCacheGen == pCache->uGenerationMissing)
2498 {
2499 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
2500 return NULL;
2501 }
2502 else if (kFsCacheRefreshMissingIntermediateDir(pCache, pChild, penmError))
2503 pParent = (PKFSDIR)pChild;
2504 else
2505 return NULL;
2506 }
2507
2508 return NULL;
2509
2510}
2511
2512/**
2513 * Walk the file system tree for the given absolute path, entering it into the
2514 * hash table.
2515 *
2516 * This will create any missing nodes while walking.
2517 *
2518 * The caller will have to do the path hash table insertion of the result.
2519 *
2520 * @returns Pointer to the tree node corresponding to @a pszPath.
2521 * NULL on lookup failure, see @a penmError for details.
2522 * @param pCache The cache.
2523 * @param pszPath The path to walk. No dot-dot bits allowed!
2524 * @param cchPath The length of the path.
2525 * @param penmError Where to return details as to why the lookup
2526 * failed.
2527 */
2528static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
2529{
2530 PKFSOBJ pChild;
2531 KU32 cchSlashes;
2532 KU32 offEnd;
2533
2534 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteA(%s)\n", pszPath));
2535
2536 /*
2537 * The root "directory" needs special handling, so we keep it outside the
2538 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
2539 */
2540 cchSlashes = 0;
2541 if ( pszPath[1] == ':'
2542 && IS_ALPHA(pszPath[0]))
2543 {
2544 /* Drive letter. */
2545 offEnd = 2;
2546 kHlpAssert(IS_SLASH(pszPath[2]));
2547 pChild = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);
2548 }
2549 else if ( IS_SLASH(pszPath[0])
2550 && IS_SLASH(pszPath[1]) )
2551 pChild = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);
2552 else
2553 {
2554 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2555 return NULL;
2556 }
2557 if (pChild)
2558 { /* likely */ }
2559 else
2560 return NULL;
2561
2562 /* Count slashes trailing the root spec. */
2563 if (offEnd < cchPath)
2564 {
2565 kHlpAssert(IS_SLASH(pszPath[offEnd]));
2566 do
2567 cchSlashes++;
2568 while (IS_SLASH(pszPath[offEnd + cchSlashes]));
2569 }
2570
2571 /* Done already? */
2572 if (offEnd >= cchPath)
2573 {
2574 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2575 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
2576 || kFsCacheRefreshObj(pCache, pChild, penmError))
2577 return kFsCacheObjRetainInternal(pChild);
2578 return NULL;
2579 }
2580
2581 /* Check that we've got a valid result and not a cached negative one. */
2582 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2583 { /* likely */ }
2584 else
2585 {
2586 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
2587 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
2588 return pChild;
2589 }
2590
2591 /*
2592 * Now that we've found a valid root directory, lookup the
2593 * remainder of the path starting with it.
2594 */
2595 return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pChild, &pszPath[offEnd + cchSlashes],
2596 cchPath - offEnd - cchSlashes, penmError);
2597}
2598
2599
2600/**
2601 * Walk the file system tree for the given absolute path, UTF-16 version.
2602 *
2603 * This will create any missing nodes while walking.
2604 *
2605 * The caller will have to do the path hash table insertion of the result.
2606 *
2607 * @returns Pointer to the tree node corresponding to @a pszPath.
2608 * NULL on lookup failure, see @a penmError for details.
2609 * @param pCache The cache.
2610 * @param pwszPath The path to walk.
2611 * @param cwcPath The length of the path (in wchar_t's).
2612 * @param penmError Where to return details as to why the lookup
2613 * failed.
2614 */
2615static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KFSLOOKUPERROR *penmError)
2616{
2617 PKFSDIR pParent = &pCache->RootDir;
2618 PKFSOBJ pChild;
2619 KU32 off;
2620 KU32 cwcSlashes;
2621 KU32 offEnd;
2622
2623 KFSCACHE_LOG2(("kFsCacheLookupAbsoluteW(%ls)\n", pwszPath));
2624
2625 /*
2626 * The root "directory" needs special handling, so we keep it outside the
2627 * main search loop. (Special: Cannot enumerate it, UNCs, ++.)
2628 */
2629 cwcSlashes = 0;
2630 off = 0;
2631 if ( pwszPath[1] == ':'
2632 && IS_ALPHA(pwszPath[0]))
2633 {
2634 /* Drive letter. */
2635 offEnd = 2;
2636 kHlpAssert(IS_SLASH(pwszPath[2]));
2637 pChild = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError);
2638 }
2639 else if ( IS_SLASH(pwszPath[0])
2640 && IS_SLASH(pwszPath[1]) )
2641 pChild = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError);
2642 else
2643 {
2644 *penmError = KFSLOOKUPERROR_UNSUPPORTED;
2645 return NULL;
2646 }
2647 if (pChild)
2648 { /* likely */ }
2649 else
2650 return NULL;
2651
2652 /* Count slashes trailing the root spec. */
2653 if (offEnd < cwcPath)
2654 {
2655 kHlpAssert(IS_SLASH(pwszPath[offEnd]));
2656 do
2657 cwcSlashes++;
2658 while (IS_SLASH(pwszPath[offEnd + cwcSlashes]));
2659 }
2660
2661 /* Done already? */
2662 if (offEnd >= cwcPath)
2663 {
2664 if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2665 || pChild->uCacheGen == (pChild->bObjType != KFSOBJ_TYPE_MISSING ? pCache->uGeneration : pCache->uGenerationMissing)
2666 || kFsCacheRefreshObj(pCache, pChild, penmError))
2667 return kFsCacheObjRetainInternal(pChild);
2668 return NULL;
2669 }
2670
2671 /* Check that we've got a valid result and not a cached negative one. */
2672 if (pChild->bObjType == KFSOBJ_TYPE_DIR)
2673 { /* likely */ }
2674 else
2675 {
2676 kHlpAssert(pChild->bObjType == KFSOBJ_TYPE_MISSING);
2677 kHlpAssert(pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE || pChild->uCacheGen == pCache->uGenerationMissing);
2678 return pChild;
2679 }
2680
2681 /*
2682 * Now that we've found a valid root directory, lookup the
2683 * remainder of the path starting with it.
2684 */
2685 return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pChild, &pwszPath[offEnd + cwcSlashes],
2686 cwcPath - offEnd - cwcSlashes, penmError);
2687}
2688
2689
2690/**
2691 * This deals with paths that are relative and paths that contains '..'
2692 * elements, ANSI version.
2693 *
2694 * @returns Pointer to object corresponding to @a pszPath on success.
2695 * NULL if this isn't a path we care to cache.
2696 *
2697 * @param pCache The cache.
2698 * @param pszPath The path.
2699 * @param cchPath The length of the path.
2700 * @param penmError Where to return details as to why the lookup
2701 * failed.
2702 */
2703static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KFSLOOKUPERROR *penmError)
2704{
2705 /*
2706 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2707 * ends up calling it anyway.
2708 */
2709 char szFull[KFSCACHE_CFG_MAX_PATH];
2710 UINT cchFull = GetFullPathNameA(pszPath, sizeof(szFull), szFull, NULL);
2711 if ( cchFull >= 3
2712 && cchFull < sizeof(szFull))
2713 {
2714 PKFSOBJ pFsObj;
2715 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath));
2716 pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError);
2717
2718#if 0 /* No need to do this until it's actually queried. */
2719 /* Cache the resulting path. */
2720 if ( pFsObj
2721 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2722 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2723 {
2724 KU32 uHashPath = kFsCacheStrHash(szFull);
2725 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2726 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2727 }
2728#endif
2729 return pFsObj;
2730 }
2731
2732 /* The path is too long! */
2733 kHlpAssertMsgFailed(("'%s' -> cchFull=%u\n", pszPath, cchFull));
2734 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2735 return NULL;
2736}
2737
2738
2739/**
2740 * This deals with paths that are relative and paths that contains '..'
2741 * elements, UTF-16 version.
2742 *
2743 * @returns Pointer to object corresponding to @a pszPath on success.
2744 * NULL if this isn't a path we care to cache.
2745 *
2746 * @param pCache The cache.
2747 * @param pwszPath The path.
2748 * @param cwcPath The length of the path (in wchar_t's).
2749 * @param penmError Where to return details as to why the lookup
2750 * failed.
2751 */
2752static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KFSLOOKUPERROR *penmError)
2753{
2754 /*
2755 * We just call GetFullPathNameA here to do the job as getcwd and _getdcwd
2756 * ends up calling it anyway.
2757 */
2758 wchar_t wszFull[KFSCACHE_CFG_MAX_PATH];
2759 UINT cwcFull = GetFullPathNameW(pwszPath, KFSCACHE_CFG_MAX_PATH, wszFull, NULL);
2760 if ( cwcFull >= 3
2761 && cwcFull < KFSCACHE_CFG_MAX_PATH)
2762 {
2763 PKFSOBJ pFsObj;
2764 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
2765 pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError);
2766
2767#if 0 /* No need to do this until it's actually queried. */
2768 /* Cache the resulting path. */
2769 if ( pFsObj
2770 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2771 || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
2772 {
2773 KU32 uHashPath = kFsCacheStrHash(szFull);
2774 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
2775 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
2776 }
2777#endif
2778 return pFsObj;
2779 }
2780
2781 /* The path is too long! */
2782 kHlpAssertMsgFailed(("'%ls' -> cwcFull=%u\n", pwszPath, cwcFull));
2783 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2784 return NULL;
2785}
2786
2787
2788/**
2789 * Refreshes a path hash that has expired, ANSI version.
2790 *
2791 * @returns pHash on success, NULL if removed.
2792 * @param pCache The cache.
2793 * @param pHashEntry The path hash.
2794 * @param idxHashTab The hash table entry.
2795 */
2796static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU32 idxHashTab)
2797{
2798 if (!pHashEntry->pFsObj)
2799 {
2800 if (pHashEntry->fAbsolute)
2801 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, &pHashEntry->enmError);
2802 else
2803 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, &pHashEntry->enmError);
2804 }
2805 else
2806 {
2807 KU8 bOldType = pHashEntry->pFsObj->bObjType;
2808 KFSLOOKUPERROR enmError;
2809 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
2810 {
2811 if (pHashEntry->pFsObj->bObjType == bOldType)
2812 { }
2813 else
2814 {
2815 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
2816 if (pHashEntry->fAbsolute)
2817 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
2818 &pHashEntry->enmError);
2819 else
2820 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
2821 &pHashEntry->enmError);
2822 }
2823 }
2824 else
2825 {
2826 fprintf(stderr, "kFsCacheRefreshPathA - refresh failure handling not implemented!\n");
2827 __debugbreak();
2828 /** @todo just remove this entry. */
2829 return NULL;
2830 }
2831 }
2832 pHashEntry->uCacheGen = pCache->uGenerationMissing;
2833 return pHashEntry;
2834}
2835
2836
2837/**
2838 * Refreshes a path hash that has expired, UTF-16 version.
2839 *
2840 * @returns pHash on success, NULL if removed.
2841 * @param pCache The cache.
2842 * @param pHashEntry The path hash.
2843 * @param idxHashTab The hash table entry.
2844 */
2845static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU32 idxHashTab)
2846{
2847 if (!pHashEntry->pFsObj)
2848 {
2849 if (pHashEntry->fAbsolute)
2850 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, &pHashEntry->enmError);
2851 else
2852 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, &pHashEntry->enmError);
2853 }
2854 else
2855 {
2856 KU8 bOldType = pHashEntry->pFsObj->bObjType;
2857 KFSLOOKUPERROR enmError;
2858 if (kFsCacheRefreshObj(pCache, pHashEntry->pFsObj, &enmError))
2859 {
2860 if (pHashEntry->pFsObj->bObjType == bOldType)
2861 { }
2862 else
2863 {
2864 kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
2865 if (pHashEntry->fAbsolute)
2866 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
2867 &pHashEntry->enmError);
2868 else
2869 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
2870 &pHashEntry->enmError);
2871 }
2872 }
2873 else
2874 {
2875 fprintf(stderr, "kFsCacheRefreshPathW - refresh failure handling not implemented!\n");
2876 __debugbreak();
2877 /** @todo just remove this entry. */
2878 return NULL;
2879 }
2880 }
2881 pHashEntry->uCacheGen = pCache->uGenerationMissing;
2882 return pHashEntry;
2883}
2884
2885
2886/**
2887 * Internal lookup worker that looks up a KFSOBJ for the given ANSI path with
2888 * length and hash.
2889 *
2890 * This will first try the hash table. If not in the hash table, the file
2891 * system cache tree is walked, missing bits filled in and finally a hash table
2892 * entry is created.
2893 *
2894 * Only drive letter paths are cachable. We don't do any UNC paths at this
2895 * point.
2896 *
2897 * @returns Reference to object corresponding to @a pszPath on success, this
2898 * must be released by kFsCacheObjRelease.
2899 * NULL if not a path we care to cache.
2900 * @param pCache The cache.
2901 * @param pchPath The path to lookup.
2902 * @param cchPath The path length.
2903 * @param uHashPath The hash of the path.
2904 * @param penmError Where to return details as to why the lookup
2905 * failed.
2906 */
2907static PKFSOBJ kFsCacheLookupHashedA(PKFSCACHE pCache, const char *pchPath, KU32 cchPath, KU32 uHashPath,
2908 KFSLOOKUPERROR *penmError)
2909{
2910 /*
2911 * Do hash table lookup of the path.
2912 */
2913 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
2914 PKFSHASHA pHashEntry = pCache->apAnsiPaths[idxHashTab];
2915 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
2916 if (pHashEntry)
2917 {
2918 do
2919 {
2920 if ( pHashEntry->uHashPath == uHashPath
2921 && pHashEntry->cchPath == cchPath
2922 && kHlpMemComp(pHashEntry->pszPath, pchPath, cchPath) == 0)
2923 {
2924 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
2925 || pHashEntry->uCacheGen == (pHashEntry->pFsObj ? pCache->uGeneration : pCache->uGenerationMissing)
2926 || (pHashEntry = kFsCacheRefreshPathA(pCache, pHashEntry, idxHashTab)) )
2927 {
2928 pCache->cLookups++;
2929 pCache->cPathHashHits++;
2930 KFSCACHE_LOG2(("kFsCacheLookupA(%*.*s) - hit %p\n", cchPath, cchPath, pchPath, pHashEntry->pFsObj));
2931 *penmError = pHashEntry->enmError;
2932 if (pHashEntry->pFsObj)
2933 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
2934 return NULL;
2935 }
2936 break;
2937 }
2938 pHashEntry = pHashEntry->pNext;
2939 } while (pHashEntry);
2940 }
2941
2942 /*
2943 * Create an entry for it by walking the file system cache and filling in the blanks.
2944 */
2945 if ( cchPath > 0
2946 && cchPath < KFSCACHE_CFG_MAX_PATH)
2947 {
2948 PKFSOBJ pFsObj;
2949 KBOOL fAbsolute;
2950
2951 /* Is absolute without any '..' bits? */
2952 if ( cchPath >= 3
2953 && ( ( pchPath[1] == ':' /* Drive letter */
2954 && IS_SLASH(pchPath[2])
2955 && IS_ALPHA(pchPath[0]) )
2956 || ( IS_SLASH(pchPath[0]) /* UNC */
2957 && IS_SLASH(pchPath[1]) ) )
2958 && !kFsCacheHasDotDotA(pchPath, cchPath) )
2959 {
2960 pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, penmError);
2961 fAbsolute = K_TRUE;
2962 }
2963 else
2964 {
2965 pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, penmError);
2966 fAbsolute = K_FALSE;
2967 }
2968 if ( pFsObj
2969 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
2970 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
2971 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
2972 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pchPath, cchPath, uHashPath, idxHashTab, fAbsolute, *penmError);
2973
2974 pCache->cLookups++;
2975 if (pFsObj)
2976 pCache->cWalkHits++;
2977 return pFsObj;
2978 }
2979
2980 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
2981 return NULL;
2982}
2983
2984
2985/**
2986 * Internal lookup worker that looks up a KFSOBJ for the given UTF-16 path with
2987 * length and hash.
2988 *
2989 * This will first try the hash table. If not in the hash table, the file
2990 * system cache tree is walked, missing bits filled in and finally a hash table
2991 * entry is created.
2992 *
2993 * Only drive letter paths are cachable. We don't do any UNC paths at this
2994 * point.
2995 *
2996 * @returns Reference to object corresponding to @a pwcPath on success, this
2997 * must be released by kFsCacheObjRelease.
2998 * NULL if not a path we care to cache.
2999 * @param pCache The cache.
3000 * @param pwcPath The path to lookup.
3001 * @param cwcPath The length of the path (in wchar_t's).
3002 * @param uHashPath The hash of the path.
3003 * @param penmError Where to return details as to why the lookup
3004 * failed.
3005 */
3006static PKFSOBJ kFsCacheLookupHashedW(PKFSCACHE pCache, const wchar_t *pwcPath, KU32 cwcPath, KU32 uHashPath,
3007 KFSLOOKUPERROR *penmError)
3008{
3009 /*
3010 * Do hash table lookup of the path.
3011 */
3012 KU32 idxHashTab = uHashPath % K_ELEMENTS(pCache->apAnsiPaths);
3013 PKFSHASHW pHashEntry = pCache->apUtf16Paths[idxHashTab];
3014 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3015 if (pHashEntry)
3016 {
3017 do
3018 {
3019 if ( pHashEntry->uHashPath == uHashPath
3020 && pHashEntry->cwcPath == cwcPath
3021 && kHlpMemComp(pHashEntry->pwszPath, pwcPath, cwcPath) == 0)
3022 {
3023 if ( pHashEntry->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
3024 || pHashEntry->uCacheGen == (pHashEntry->pFsObj ? pCache->uGeneration : pCache->uGenerationMissing)
3025 || (pHashEntry = kFsCacheRefreshPathW(pCache, pHashEntry, idxHashTab)) )
3026 {
3027 pCache->cLookups++;
3028 pCache->cPathHashHits++;
3029 KFSCACHE_LOG2(("kFsCacheLookupW(%*.*ls) - hit %p\n", cwcPath, cwcPath, pwcPath, pHashEntry->pFsObj));
3030 *penmError = pHashEntry->enmError;
3031 if (pHashEntry->pFsObj)
3032 return kFsCacheObjRetainInternal(pHashEntry->pFsObj);
3033 return NULL;
3034 }
3035 break;
3036 }
3037 pHashEntry = pHashEntry->pNext;
3038 } while (pHashEntry);
3039 }
3040
3041 /*
3042 * Create an entry for it by walking the file system cache and filling in the blanks.
3043 */
3044 if ( cwcPath > 0
3045 && cwcPath < KFSCACHE_CFG_MAX_PATH)
3046 {
3047 PKFSOBJ pFsObj;
3048 KBOOL fAbsolute;
3049
3050 /* Is absolute without any '..' bits? */
3051 if ( cwcPath >= 3
3052 && ( ( pwcPath[1] == ':' /* Drive letter */
3053 && IS_SLASH(pwcPath[2])
3054 && IS_ALPHA(pwcPath[0]) )
3055 || ( IS_SLASH(pwcPath[0]) /* UNC */
3056 && IS_SLASH(pwcPath[1]) ) )
3057 && !kFsCacheHasDotDotW(pwcPath, cwcPath) )
3058 {
3059 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, penmError);
3060 fAbsolute = K_TRUE;
3061 }
3062 else
3063 {
3064 pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, penmError);
3065 fAbsolute = K_FALSE;
3066 }
3067 if ( pFsObj
3068 || ( (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
3069 && *penmError != KFSLOOKUPERROR_PATH_TOO_LONG)
3070 || *penmError == KFSLOOKUPERROR_UNSUPPORTED )
3071 kFsCacheCreatePathHashTabEntryW(pCache, pFsObj, pwcPath, cwcPath, uHashPath, idxHashTab, fAbsolute, *penmError);
3072
3073 pCache->cLookups++;
3074 if (pFsObj)
3075 pCache->cWalkHits++;
3076 return pFsObj;
3077 }
3078
3079 *penmError = KFSLOOKUPERROR_PATH_TOO_LONG;
3080 return NULL;
3081}
3082
3083
3084
3085/**
3086 * Looks up a KFSOBJ for the given ANSI path.
3087 *
3088 * This will first try the hash table. If not in the hash table, the file
3089 * system cache tree is walked, missing bits filled in and finally a hash table
3090 * entry is created.
3091 *
3092 * Only drive letter paths are cachable. We don't do any UNC paths at this
3093 * point.
3094 *
3095 * @returns Reference to object corresponding to @a pszPath on success, this
3096 * must be released by kFsCacheObjRelease.
3097 * NULL if not a path we care to cache.
3098 * @param pCache The cache.
3099 * @param pszPath The path to lookup.
3100 * @param penmError Where to return details as to why the lookup
3101 * failed.
3102 */
3103PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3104{
3105 KU32 uHashPath;
3106 KU32 cchPath = (KU32)kFsCacheStrHashEx(pszPath, &uHashPath);
3107 return kFsCacheLookupHashedA(pCache, pszPath, cchPath, uHashPath, penmError);
3108}
3109
3110
3111/**
3112 * Looks up a KFSOBJ for the given UTF-16 path.
3113 *
3114 * This will first try the hash table. If not in the hash table, the file
3115 * system cache tree is walked, missing bits filled in and finally a hash table
3116 * entry is created.
3117 *
3118 * Only drive letter paths are cachable. We don't do any UNC paths at this
3119 * point.
3120 *
3121 * @returns Reference to object corresponding to @a pwszPath on success, this
3122 * must be released by kFsCacheObjRelease.
3123 * NULL if not a path we care to cache.
3124 * @param pCache The cache.
3125 * @param pwszPath The path to lookup.
3126 * @param penmError Where to return details as to why the lookup
3127 * failed.
3128 */
3129PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3130{
3131 KU32 uHashPath;
3132 KU32 cwcPath = (KU32)kFsCacheUtf16HashEx(pwszPath, &uHashPath);
3133 return kFsCacheLookupHashedW(pCache, pwszPath, cwcPath, uHashPath, penmError);
3134}
3135
3136
3137/**
3138 * Looks up a KFSOBJ for the given ANSI path.
3139 *
3140 * This will first try the hash table. If not in the hash table, the file
3141 * system cache tree is walked, missing bits filled in and finally a hash table
3142 * entry is created.
3143 *
3144 * Only drive letter paths are cachable. We don't do any UNC paths at this
3145 * point.
3146 *
3147 * @returns Reference to object corresponding to @a pchPath on success, this
3148 * must be released by kFsCacheObjRelease.
3149 * NULL if not a path we care to cache.
3150 * @param pCache The cache.
3151 * @param pchPath The path to lookup (does not need to be nul
3152 * terminated).
3153 * @param cchPath The path length.
3154 * @param penmError Where to return details as to why the lookup
3155 * failed.
3156 */
3157PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError)
3158{
3159 return kFsCacheLookupHashedA(pCache, pchPath, (KU32)cchPath, kFsCacheStrHashN(pchPath, cchPath), penmError);
3160}
3161
3162
3163/**
3164 * Looks up a KFSOBJ for the given UTF-16 path.
3165 *
3166 * This will first try the hash table. If not in the hash table, the file
3167 * system cache tree is walked, missing bits filled in and finally a hash table
3168 * entry is created.
3169 *
3170 * Only drive letter paths are cachable. We don't do any UNC paths at this
3171 * point.
3172 *
3173 * @returns Reference to object corresponding to @a pwchPath on success, this
3174 * must be released by kFsCacheObjRelease.
3175 * NULL if not a path we care to cache.
3176 * @param pCache The cache.
3177 * @param pwcPath The path to lookup (does not need to be nul
3178 * terminated).
3179 * @param cwcPath The path length (in wchar_t's).
3180 * @param penmError Where to return details as to why the lookup
3181 * failed.
3182 */
3183PKFSOBJ kFsCacheLookupWithLengthW(PKFSCACHE pCache, const wchar_t *pwcPath, KSIZE cwcPath, KFSLOOKUPERROR *penmError)
3184{
3185 return kFsCacheLookupHashedW(pCache, pwcPath, (KU32)cwcPath, kFsCacheUtf16HashN(pwcPath, cwcPath), penmError);
3186}
3187
3188
3189/**
3190 * Wrapper around kFsCacheLookupA that drops KFSOBJ_TYPE_MISSING and returns
3191 * KFSLOOKUPERROR_NOT_FOUND instead.
3192 *
3193 * @returns Reference to object corresponding to @a pszPath on success, this
3194 * must be released by kFsCacheObjRelease.
3195 * NULL if not a path we care to cache.
3196 * @param pCache The cache.
3197 * @param pszPath The path to lookup.
3198 * @param penmError Where to return details as to why the lookup
3199 * failed.
3200 */
3201PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError)
3202{
3203 PKFSOBJ pObj = kFsCacheLookupA(pCache, pszPath, penmError);
3204 if (pObj)
3205 {
3206 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3207 return pObj;
3208
3209 kFsCacheObjRelease(pCache, pObj);
3210 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3211 }
3212 return NULL;
3213}
3214
3215
3216/**
3217 * Wrapper around kFsCacheLookupW that drops KFSOBJ_TYPE_MISSING and returns
3218 * KFSLOOKUPERROR_NOT_FOUND instead.
3219 *
3220 * @returns Reference to object corresponding to @a pszPath on success, this
3221 * must be released by kFsCacheObjRelease.
3222 * NULL if not a path we care to cache.
3223 * @param pCache The cache.
3224 * @param pwszPath The path to lookup.
3225 * @param penmError Where to return details as to why the lookup
3226 * failed.
3227 */
3228PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError)
3229{
3230 PKFSOBJ pObj = kFsCacheLookupW(pCache, pwszPath, penmError);
3231 if (pObj)
3232 {
3233 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
3234 return pObj;
3235
3236 kFsCacheObjRelease(pCache, pObj);
3237 *penmError = KFSLOOKUPERROR_NOT_FOUND;
3238 }
3239 return NULL;
3240}
3241
3242
3243/**
3244 * Destroys a cache object which has a zero reference count.
3245 *
3246 * @returns 0
3247 * @param pCache The cache.
3248 * @param pObj The object.
3249 */
3250KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
3251{
3252 kHlpAssert(pObj->cRefs == 0);
3253 kHlpAssert(pObj->pParent == NULL);
3254 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3255
3256 KFSCACHE_LOG(("Destroying %s/%s, type=%d\n", pObj->pParent ? pObj->pParent->Obj.pszName : "", pObj->pszName, pObj->bObjType));
3257
3258 /*
3259 * Invalidate the structure.
3260 */
3261 pObj->u32Magic = ~KFSOBJ_MAGIC;
3262
3263 /*
3264 * Destroy any user data first.
3265 */
3266 while (pObj->pUserDataHead != NULL)
3267 {
3268 PKFSUSERDATA pUserData = pObj->pUserDataHead;
3269 pObj->pUserDataHead = pUserData->pNext;
3270 if (pUserData->pfnDestructor)
3271 pUserData->pfnDestructor(pCache, pObj, pUserData);
3272 kHlpFree(pUserData);
3273 }
3274
3275 /*
3276 * Do type specific destruction
3277 */
3278 switch (pObj->bObjType)
3279 {
3280 case KFSOBJ_TYPE_MISSING:
3281 /* nothing else to do here */
3282 pCache->cbObjects -= sizeof(KFSDIR);
3283 break;
3284
3285 case KFSOBJ_TYPE_DIR:
3286 {
3287 PKFSDIR pDir = (PKFSDIR)pObj;
3288 KU32 cChildren = pDir->cChildren;
3289 pCache->cbObjects -= sizeof(*pDir)
3290 + K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
3291 + pDir->cHashTab * sizeof(pDir->paHashTab);
3292
3293 pDir->cChildren = 0;
3294 while (cChildren-- > 0)
3295 kFsCacheObjRelease(pCache, pDir->papChildren[cChildren]);
3296 kHlpFree(pDir->papChildren);
3297 pDir->papChildren = NULL;
3298
3299 kHlpFree(pDir->paHashTab);
3300 pDir->paHashTab = NULL;
3301 break;
3302 }
3303
3304 case KFSOBJ_TYPE_FILE:
3305 case KFSOBJ_TYPE_OTHER:
3306 pCache->cbObjects -= sizeof(*pObj);
3307 break;
3308
3309 default:
3310 return 0;
3311 }
3312
3313 /*
3314 * Common bits.
3315 */
3316 pCache->cbObjects -= pObj->cchName + 1;
3317#ifdef KFSCACHE_CFG_UTF16
3318 pCache->cbObjects -= (pObj->cwcName + 1) * sizeof(wchar_t);
3319#endif
3320#ifdef KFSCACHE_CFG_SHORT_NAMES
3321 if (pObj->pszName != pObj->pszShortName)
3322 {
3323 pCache->cbObjects -= pObj->cchShortName + 1;
3324# ifdef KFSCACHE_CFG_UTF16
3325 pCache->cbObjects -= (pObj->cwcShortName + 1) * sizeof(wchar_t);
3326# endif
3327 }
3328#endif
3329 pCache->cObjects--;
3330
3331 kHlpFree(pObj);
3332 return 0;
3333}
3334
3335
3336/**
3337 * Releases a reference to a cache object.
3338 *
3339 * @returns New reference count.
3340 * @param pCache The cache.
3341 * @param pObj The object.
3342 */
3343KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
3344{
3345 if (pObj)
3346 {
3347 KU32 cRefs;
3348 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3349 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3350
3351 cRefs = --pObj->cRefs;
3352 if (cRefs)
3353 return cRefs;
3354 return kFsCacheObjDestroy(pCache, pObj);
3355 }
3356 return 0;
3357}
3358
3359
3360/**
3361 * Retains a reference to a cahce object.
3362 *
3363 * @returns New reference count.
3364 * @param pObj The object.
3365 */
3366KU32 kFsCacheObjRetain(PKFSOBJ pObj)
3367{
3368 KU32 cRefs;
3369 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3370 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3371
3372 cRefs = ++pObj->cRefs;
3373 kHlpAssert(cRefs < 16384);
3374 return cRefs;
3375}
3376
3377
3378/**
3379 * Associates an item of user data with the given object.
3380 *
3381 * If the data needs cleaning up before being free, set the
3382 * PKFSUSERDATA::pfnDestructor member of the returned structure.
3383 *
3384 * @returns Pointer to the user data on success.
3385 * NULL if out of memory or key already in use.
3386 *
3387 * @param pCache The cache.
3388 * @param pObj The object.
3389 * @param uKey The user data key.
3390 * @param cbUserData The size of the user data.
3391 */
3392PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData)
3393{
3394 kHlpAssert(cbUserData >= sizeof(*pNew));
3395 if (kFsCacheObjGetUserData(pCache, pObj, uKey) == NULL)
3396 {
3397 PKFSUSERDATA pNew = (PKFSUSERDATA)kHlpAllocZ(cbUserData);
3398 if (pNew)
3399 {
3400 pNew->uKey = uKey;
3401 pNew->pfnDestructor = NULL;
3402 pNew->pNext = pObj->pUserDataHead;
3403 pObj->pUserDataHead = pNew;
3404 return pNew;
3405 }
3406 }
3407
3408 return NULL;
3409}
3410
3411
3412/**
3413 * Retrieves an item of user data associated with the given object.
3414 *
3415 * @returns Pointer to the associated user data if found, otherwise NULL.
3416 * @param pCache The cache.
3417 * @param pObj The object.
3418 * @param uKey The user data key.
3419 */
3420PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey)
3421{
3422 PKFSUSERDATA pCur;
3423
3424 kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
3425 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3426
3427 for (pCur = pObj->pUserDataHead; pCur; pCur = pCur->pNext)
3428 if (pCur->uKey == uKey)
3429 return pCur;
3430 return NULL;
3431}
3432
3433
3434/**
3435 * Gets the full path to @a pObj, ANSI version.
3436 *
3437 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3438 * @param pObj The object to get the full path to.
3439 * @param pszPath Where to return the path
3440 * @param cbPath The size of the output buffer.
3441 * @param chSlash The slash to use.
3442 */
3443KBOOL kFsCacheObjGetFullPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
3444{
3445 KSIZE off = pObj->cchParent;
3446 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3447 if (off > 0)
3448 {
3449 KSIZE offEnd = off + pObj->cchName;
3450 if (offEnd < cbPath)
3451 {
3452 PKFSDIR pAncestor;
3453
3454 pszPath[off + pObj->cchName] = '\0';
3455 memcpy(&pszPath[off], pObj->pszName, pObj->cchName);
3456
3457 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3458 {
3459 kHlpAssert(off > 1);
3460 kHlpAssert(pAncestor != NULL);
3461 kHlpAssert(pAncestor->Obj.cchName > 0);
3462 pszPath[--off] = chSlash;
3463 off -= pAncestor->Obj.cchName;
3464 kHlpAssert(pAncestor->Obj.cchParent == off);
3465 memcpy(&pszPath[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3466 }
3467 return K_TRUE;
3468 }
3469 }
3470 else
3471 {
3472 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
3473 off = pObj->cchName;
3474 if (off + fDriveLetter < cbPath)
3475 {
3476 memcpy(pszPath, pObj->pszName, off);
3477 if (fDriveLetter)
3478 pszPath[off++] = chSlash;
3479 pszPath[off] = '\0';
3480 return K_TRUE;
3481 }
3482 }
3483
3484 return K_FALSE;
3485}
3486
3487
3488/**
3489 * Gets the full path to @a pObj, UTF-16 version.
3490 *
3491 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3492 * @param pObj The object to get the full path to.
3493 * @param pszPath Where to return the path
3494 * @param cbPath The size of the output buffer.
3495 * @param wcSlash The slash to use.
3496 */
3497KBOOL kFsCacheObjGetFullPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
3498{
3499 KSIZE off = pObj->cwcParent;
3500 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3501 if (off > 0)
3502 {
3503 KSIZE offEnd = off + pObj->cwcName;
3504 if (offEnd < cwcPath)
3505 {
3506 PKFSDIR pAncestor;
3507
3508 pwszPath[off + pObj->cwcName] = '\0';
3509 memcpy(&pwszPath[off], pObj->pwszName, pObj->cwcName * sizeof(wchar_t));
3510
3511 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3512 {
3513 kHlpAssert(off > 1);
3514 kHlpAssert(pAncestor != NULL);
3515 kHlpAssert(pAncestor->Obj.cwcName > 0);
3516 pwszPath[--off] = wcSlash;
3517 off -= pAncestor->Obj.cwcName;
3518 kHlpAssert(pAncestor->Obj.cwcParent == off);
3519 memcpy(&pwszPath[off], pAncestor->Obj.pwszName, pAncestor->Obj.cwcName * sizeof(wchar_t));
3520 }
3521 return K_TRUE;
3522 }
3523 }
3524 else
3525 {
3526 KBOOL const fDriveLetter = pObj->cchName == 2 && pObj->pszName[2] == ':';
3527 off = pObj->cwcName;
3528 if (off + fDriveLetter < cwcPath)
3529 {
3530 memcpy(pwszPath, pObj->pwszName, off * sizeof(wchar_t));
3531 if (fDriveLetter)
3532 pwszPath[off++] = wcSlash;
3533 pwszPath[off] = '\0';
3534 return K_TRUE;
3535 }
3536 }
3537
3538 return K_FALSE;
3539}
3540
3541
3542#ifdef KFSCACHE_CFG_SHORT_NAMES
3543
3544/**
3545 * Gets the full short path to @a pObj, ANSI version.
3546 *
3547 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3548 * @param pObj The object to get the full path to.
3549 * @param pszPath Where to return the path
3550 * @param cbPath The size of the output buffer.
3551 * @param chSlash The slash to use.
3552 */
3553KBOOL kFsCacheObjGetFullShortPathA(PKFSOBJ pObj, char *pszPath, KSIZE cbPath, char chSlash)
3554{
3555 KSIZE off = pObj->cchShortParent;
3556 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3557 if (off > 0)
3558 {
3559 KSIZE offEnd = off + pObj->cchShortName;
3560 if (offEnd < cbPath)
3561 {
3562 PKFSDIR pAncestor;
3563
3564 pszPath[off + pObj->cchShortName] = '\0';
3565 memcpy(&pszPath[off], pObj->pszShortName, pObj->cchShortName);
3566
3567 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3568 {
3569 kHlpAssert(off > 1);
3570 kHlpAssert(pAncestor != NULL);
3571 kHlpAssert(pAncestor->Obj.cchShortName > 0);
3572 pszPath[--off] = chSlash;
3573 off -= pAncestor->Obj.cchShortName;
3574 kHlpAssert(pAncestor->Obj.cchShortParent == off);
3575 memcpy(&pszPath[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
3576 }
3577 return K_TRUE;
3578 }
3579 }
3580 else
3581 {
3582 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
3583 off = pObj->cchShortName;
3584 if (off + fDriveLetter < cbPath)
3585 {
3586 memcpy(pszPath, pObj->pszShortName, off);
3587 if (fDriveLetter)
3588 pszPath[off++] = chSlash;
3589 pszPath[off] = '\0';
3590 return K_TRUE;
3591 }
3592 }
3593
3594 return K_FALSE;
3595}
3596
3597
3598/**
3599 * Gets the full short path to @a pObj, UTF-16 version.
3600 *
3601 * @returns K_TRUE on success, K_FALSE on buffer overflow (nothing stored).
3602 * @param pObj The object to get the full path to.
3603 * @param pszPath Where to return the path
3604 * @param cbPath The size of the output buffer.
3605 * @param wcSlash The slash to use.
3606 */
3607KBOOL kFsCacheObjGetFullShortPathW(PKFSOBJ pObj, wchar_t *pwszPath, KSIZE cwcPath, wchar_t wcSlash)
3608{
3609 KSIZE off = pObj->cwcShortParent;
3610 kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
3611 if (off > 0)
3612 {
3613 KSIZE offEnd = off + pObj->cwcShortName;
3614 if (offEnd < cwcPath)
3615 {
3616 PKFSDIR pAncestor;
3617
3618 pwszPath[off + pObj->cwcShortName] = '\0';
3619 memcpy(&pwszPath[off], pObj->pwszShortName, pObj->cwcShortName * sizeof(wchar_t));
3620
3621 for (pAncestor = pObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3622 {
3623 kHlpAssert(off > 1);
3624 kHlpAssert(pAncestor != NULL);
3625 kHlpAssert(pAncestor->Obj.cwcShortName > 0);
3626 pwszPath[--off] = wcSlash;
3627 off -= pAncestor->Obj.cwcShortName;
3628 kHlpAssert(pAncestor->Obj.cwcShortParent == off);
3629 memcpy(&pwszPath[off], pAncestor->Obj.pwszShortName, pAncestor->Obj.cwcShortName * sizeof(wchar_t));
3630 }
3631 return K_TRUE;
3632 }
3633 }
3634 else
3635 {
3636 KBOOL const fDriveLetter = pObj->cchShortName == 2 && pObj->pszShortName[2] == ':';
3637 off = pObj->cwcShortName;
3638 if (off + fDriveLetter < cwcPath)
3639 {
3640 memcpy(pwszPath, pObj->pwszShortName, off * sizeof(wchar_t));
3641 if (fDriveLetter)
3642 pwszPath[off++] = wcSlash;
3643 pwszPath[off] = '\0';
3644 return K_TRUE;
3645 }
3646 }
3647
3648 return K_FALSE;
3649}
3650
3651#endif /* KFSCACHE_CFG_SHORT_NAMES */
3652
3653
3654
3655/**
3656 * Read the specified bits from the files into the given buffer, simple version.
3657 *
3658 * @returns K_TRUE on success (all requested bytes read),
3659 * K_FALSE on any kind of failure.
3660 *
3661 * @param pCache The cache.
3662 * @param pFileObj The file object.
3663 * @param offStart Where to start reading.
3664 * @param pvBuf Where to store what we read.
3665 * @param cbToRead How much to read (exact).
3666 */
3667KBOOL kFsCacheFileSimpleOpenReadClose(PKFSCACHE pCache, PKFSOBJ pFileObj, KU64 offStart, void *pvBuf, KSIZE cbToRead)
3668{
3669 /*
3670 * Open the file relative to the parent directory.
3671 */
3672 MY_NTSTATUS rcNt;
3673 HANDLE hFile;
3674 MY_IO_STATUS_BLOCK Ios;
3675 MY_OBJECT_ATTRIBUTES ObjAttr;
3676 MY_UNICODE_STRING UniStr;
3677
3678 kHlpAssertReturn(pFileObj->bObjType == KFSOBJ_TYPE_FILE, K_FALSE);
3679 kHlpAssert(pFileObj->pParent);
3680 kHlpAssertReturn(pFileObj->pParent->hDir != INVALID_HANDLE_VALUE, K_FALSE);
3681 kHlpAssertReturn(offStart == 0, K_FALSE); /** @todo when needed */
3682
3683 Ios.Information = -1;
3684 Ios.u.Status = -1;
3685
3686 UniStr.Buffer = (wchar_t *)pFileObj->pwszName;
3687 UniStr.Length = (USHORT)(pFileObj->cwcName * sizeof(wchar_t));
3688 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
3689
3690 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFileObj->pParent->hDir, NULL /*pSecAttr*/);
3691
3692 rcNt = g_pfnNtCreateFile(&hFile,
3693 GENERIC_READ | SYNCHRONIZE,
3694 &ObjAttr,
3695 &Ios,
3696 NULL, /*cbFileInitialAlloc */
3697 FILE_ATTRIBUTE_NORMAL,
3698 FILE_SHARE_READ,
3699 FILE_OPEN,
3700 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
3701 NULL, /*pEaBuffer*/
3702 0); /*cbEaBuffer*/
3703 if (MY_NT_SUCCESS(rcNt))
3704 {
3705 LARGE_INTEGER offFile;
3706 offFile.QuadPart = offStart;
3707
3708 Ios.Information = -1;
3709 Ios.u.Status = -1;
3710 rcNt = g_pfnNtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApcComplete*/, NULL /*pvApcCtx*/, &Ios,
3711 pvBuf, (KU32)cbToRead, !offStart ? &offFile : NULL, NULL /*puKey*/);
3712 if (MY_NT_SUCCESS(rcNt))
3713 rcNt = Ios.u.Status;
3714 if (MY_NT_SUCCESS(rcNt))
3715 {
3716 if (Ios.Information == cbToRead)
3717 {
3718 g_pfnNtClose(hFile);
3719 return K_TRUE;
3720 }
3721 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': Information=%p\n", pFileObj->pwszName, Ios.Information));
3722 }
3723 else
3724 KFSCACHE_LOG(("Error reading %#x bytes from '%ls': %#x\n", pFileObj->pwszName, rcNt));
3725 g_pfnNtClose(hFile);
3726 }
3727 else
3728 KFSCACHE_LOG(("Error opening '%ls' for caching: %#x\n", pFileObj->pwszName, rcNt));
3729 return K_FALSE;
3730}
3731
3732
3733/**
3734 * Invalidate all cache entries of missing files.
3735 *
3736 * @param pCache The cache.
3737 */
3738void kFsCacheInvalidateMissing(PKFSCACHE pCache)
3739{
3740 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3741 pCache->uGenerationMissing++;
3742 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
3743 KFSCACHE_LOG(("Invalidate missing %#x\n", pCache->uGenerationMissing));
3744}
3745
3746
3747/**
3748 * Invalidate all cache entries of missing files.
3749 *
3750 * @param pCache The cache.
3751 */
3752void kFsCacheInvalidateAll(PKFSCACHE pCache)
3753{
3754 kHlpAssert(pCache->u32Magic == KFSOBJ_MAGIC);
3755 pCache->uGenerationMissing++;
3756 kHlpAssert(pCache->uGenerationMissing < KU32_MAX);
3757 pCache->uGeneration++;
3758 kHlpAssert(pCache->uGeneration < KU32_MAX);
3759 KFSCACHE_LOG(("Invalidate all %#x/%#x\n", pCache->uGenerationMissing, pCache->uGeneration));
3760
3761}
3762
3763
3764
3765PKFSCACHE kFsCacheCreate(KU32 fFlags)
3766{
3767 PKFSCACHE pCache;
3768 birdResolveImports();
3769
3770 pCache = (PKFSCACHE)kHlpAllocZ(sizeof(*pCache));
3771 if (pCache)
3772 {
3773 /* Dummy root dir entry. */
3774 pCache->RootDir.Obj.u32Magic = KFSOBJ_MAGIC;
3775 pCache->RootDir.Obj.cRefs = 1;
3776 pCache->RootDir.Obj.uCacheGen = KFSOBJ_CACHE_GEN_IGNORE;
3777 pCache->RootDir.Obj.bObjType = KFSOBJ_TYPE_DIR;
3778 pCache->RootDir.Obj.fHaveStats = K_FALSE;
3779 pCache->RootDir.Obj.pParent = NULL;
3780 pCache->RootDir.Obj.pszName = "";
3781 pCache->RootDir.Obj.cchName = 0;
3782 pCache->RootDir.Obj.cchParent = 0;
3783#ifdef KFSCACHE_CFG_UTF16
3784 pCache->RootDir.Obj.cwcName = 0;
3785 pCache->RootDir.Obj.cwcParent = 0;
3786 pCache->RootDir.Obj.pwszName = L"";
3787#endif
3788
3789#ifdef KFSCACHE_CFG_SHORT_NAMES
3790 pCache->RootDir.Obj.pszShortName = NULL;
3791 pCache->RootDir.Obj.cchShortName = 0;
3792 pCache->RootDir.Obj.cchShortParent = 0;
3793# ifdef KFSCACHE_CFG_UTF16
3794 pCache->RootDir.Obj.cwcShortName;
3795 pCache->RootDir.Obj.cwcShortParent;
3796 pCache->RootDir.Obj.pwszShortName;
3797# endif
3798#endif
3799 pCache->RootDir.cChildren = 0;
3800 pCache->RootDir.cChildrenAllocated = 0;
3801 pCache->RootDir.papChildren = NULL;
3802 pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
3803 pCache->RootDir.cHashTab = 251;
3804 pCache->RootDir.paHashTab = (PKFSOBJHASH)kHlpAllocZ( pCache->RootDir.cHashTab
3805 * sizeof(pCache->RootDir.paHashTab[0]));
3806 if (pCache->RootDir.paHashTab)
3807 {
3808 /* The cache itself. */
3809 pCache->u32Magic = KFSCACHE_MAGIC;
3810 pCache->fFlags = fFlags;
3811 pCache->uGeneration = KU32_MAX / 2;
3812 pCache->uGenerationMissing = 1;
3813 pCache->cObjects = 1;
3814 pCache->cbObjects = sizeof(pCache->RootDir) + pCache->RootDir.cHashTab * sizeof(pCache->RootDir.paHashTab[0]);
3815 pCache->cPathHashHits = 0;
3816 pCache->cWalkHits = 0;
3817 pCache->cAnsiPaths = 0;
3818 pCache->cAnsiPathCollisions = 0;
3819 pCache->cbAnsiPaths = 0;
3820#ifdef KFSCACHE_CFG_UTF16
3821 pCache->cUtf16Paths = 0;
3822 pCache->cUtf16PathCollisions = 0;
3823 pCache->cbUtf16Paths = 0;
3824#endif
3825 return pCache;
3826 }
3827
3828 kHlpFree(pCache);
3829 }
3830 return NULL;
3831}
3832
Note: See TracBrowser for help on using the repository browser.