VirtualBox

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

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

updates

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

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