VirtualBox

source: kBuild/trunk/src/kmk/dir-nt-bird.c@ 3184

Last change on this file since 3184 was 3184, checked in by bird, 7 years ago

kFsCache,dir-nt-bird: Added locking to the cache to make it accessible from winchildren worker threads.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 25.1 KB
Line 
1/* $Id: dir-nt-bird.c 3184 2018-03-23 22:36:43Z bird $ */
2/** @file
3 * Reimplementation of dir.c for NT using kFsCache.
4 *
5 * This should perform better on NT, especially on machines "infected"
6 * by antivirus programs.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <[email protected]>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#include <Windows.h> /* locking */
34#include "nt/kFsCache.h"
35#include "makeint.h"
36#if defined(KMK) && !defined(__OS2__)
37# include "glob/glob.h"
38#else
39# include <glob.h>
40#endif
41#include <assert.h>
42
43#include "nt_fullpath.h" /* for the time being - will be implemented here later on. */
44
45
46/*********************************************************************************************************************************
47* Defined Constants And Macros *
48*********************************************************************************************************************************/
49/** User data key indicating that it's an impossible file to make.
50 * See file_impossible() and file_impossible_p(). */
51#define KMK_DIR_NT_IMPOSSIBLE_KEY (~(KUPTR)7)
52
53
54/*********************************************************************************************************************************
55* Structures and Typedefs *
56*********************************************************************************************************************************/
57/**
58 * glob directory stream.
59 */
60typedef struct KMKNTOPENDIR
61{
62 /** Reference to the directory. */
63 PKFSDIR pDir;
64 /** Index of the next directory entry (child) to return. */
65 KU32 idxNext;
66 /** The structure in which to return the directory entry. */
67 struct dirent DirEnt;
68} KMKNTOPENDIR;
69
70
71/*********************************************************************************************************************************
72* Global Variables *
73*********************************************************************************************************************************/
74/** The cache.*/
75PKFSCACHE g_pFsCache = NULL;
76/** Number of times dir_cache_invalid_missing was called. */
77static KU32 g_cInvalidates = 0;
78/** Set by dir_cache_volatile_dir to indicate that the user has marked the
79 * volatile parts of the file system with custom revisioning and we only need to
80 * flush these. This is very handy when using a separate output directory
81 * from the sources. */
82static KBOOL g_fFsCacheIsUsingCustomRevision = K_FALSE;
83/** The ID of the main thread. We currently only let it access the cache. */
84static DWORD g_idMainThread = 0;
85
86
87void hash_init_directories(void)
88{
89 g_idMainThread = GetCurrentThreadId();
90 g_pFsCache = kFsCacheCreate(0);
91 if (g_pFsCache)
92 return;
93 fputs("kFsCacheCreate failed!", stderr);
94 exit(9);
95}
96
97
98/**
99 * Checks if @a pszName exists in directory @a pszDir.
100 *
101 * @returns 1 if it does, 0 if it doesn't.
102 *
103 * @param pszDir The directory.
104 * @param pszName The name.
105 *
106 * If empty string, just check if the directory exists.
107 *
108 * If NULL, just read the whole cache the directory into
109 * the cache (we always do that).
110 */
111int dir_file_exists_p(const char *pszDir, const char *pszName)
112{
113 int fRc = 0;
114 KFSLOOKUPERROR enmError;
115 PKFSOBJ pDirObj = kFsCacheLookupA(g_pFsCache, pszDir, &enmError);
116 assert(GetCurrentThreadId() == g_idMainThread);
117 if (pDirObj)
118 {
119
120 if (pDirObj->bObjType == KFSOBJ_TYPE_DIR)
121 {
122 if (pszName != 0)
123 {
124 /* Empty filename is just checking out the directory. */
125 if (*pszName == '\0')
126 fRc = 1;
127 else
128 {
129 PKFSOBJ pNameObj = kFsCacheLookupRelativeToDirA(g_pFsCache, (PKFSDIR)pDirObj, pszName, strlen(pszName),
130 0/*fFlags*/, &enmError, NULL);
131 if (pNameObj)
132 {
133 fRc = pNameObj->bObjType == KFSOBJ_TYPE_MISSING;
134 kFsCacheObjRelease(g_pFsCache, pNameObj);
135 }
136 }
137 }
138 }
139 kFsCacheObjRelease(g_pFsCache, pDirObj);
140 }
141 return fRc;
142}
143
144
145/**
146 * Checks if a file exists.
147 *
148 * @returns 1 if it does exist, 0 if it doesn't.
149 * @param pszPath The path to check out.
150 * @note Multi-thread safe.
151 */
152int file_exists_p(const char *pszPath)
153{
154 int fRc;
155 KFSLOOKUPERROR enmError;
156 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
157 if (pPathObj)
158 {
159 fRc = pPathObj->bObjType != KFSOBJ_TYPE_MISSING;
160 kFsCacheObjRelease(g_pFsCache, pPathObj);
161 }
162 else
163 fRc = 0;
164 return fRc;
165}
166
167
168/**
169 * Checks if a file exists and is a regular file, given a UTF-16 string.
170 *
171 * @returns 1 if it regular file, 0 if doesn't exist or isn't a file
172 * @param pwszPath The UTF-16 path to check out.
173 * @note Multi-thread safe.
174 */
175int utf16_regular_file_p(const wchar_t *pwszPath)
176{
177 int fRc;
178 KFSLOOKUPERROR enmError;
179 PKFSOBJ pPathObj = kFsCacheLookupW(g_pFsCache, pwszPath, &enmError);
180 if (pPathObj)
181 {
182 fRc = pPathObj->bObjType == KFSOBJ_TYPE_FILE;
183 kFsCacheObjRelease(g_pFsCache, pPathObj);
184 }
185 else
186 fRc = 0;
187 return fRc;
188}
189
190
191/**
192 * Just a way for vpath.c to get a correctly cased path, I think.
193 *
194 * @returns Directory path in string cache.
195 * @param pszDir The directory.
196 */
197const char *dir_name(const char *pszDir)
198{
199 char szTmp[MAX_PATH];
200 assert(GetCurrentThreadId() == g_idMainThread);
201 nt_fullpath(pszDir, szTmp, sizeof(szTmp));
202 return strcache_add(szTmp);
203}
204
205
206/**
207 * Makes future file_impossible_p calls return 1 for pszPath.
208 */
209void file_impossible(const char *pszPath)
210{
211 KFSLOOKUPERROR enmError;
212 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
213 assert(GetCurrentThreadId() == g_idMainThread);
214 if (pPathObj)
215 {
216 kFsCacheObjAddUserData(g_pFsCache, pPathObj, KMK_DIR_NT_IMPOSSIBLE_KEY, sizeof(KFSUSERDATA));
217 kFsCacheObjRelease(g_pFsCache, pPathObj);
218 }
219}
220
221/**
222 * Makes future file_impossible_p calls return 1 for pszPath.
223 */
224int file_impossible_p(const char *pszPath)
225{
226 int fRc;
227 KFSLOOKUPERROR enmError;
228 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
229 assert(GetCurrentThreadId() == g_idMainThread);
230 if (pPathObj)
231 {
232 fRc = kFsCacheObjGetUserData(g_pFsCache, pPathObj, KMK_DIR_NT_IMPOSSIBLE_KEY) != NULL;
233 kFsCacheObjRelease(g_pFsCache, pPathObj);
234 }
235 else
236 fRc = 0;
237 return fRc;
238}
239
240
241/**
242 * opendir for glob.
243 *
244 * @returns Pointer to DIR like handle, NULL if directory not found.
245 * @param pszDir The directory to enumerate.
246 */
247static __ptr_t dir_glob_opendir(const char *pszDir)
248{
249 KFSLOOKUPERROR enmError;
250 PKFSOBJ pDirObj = kFsCacheLookupA(g_pFsCache, pszDir, &enmError);
251 assert(GetCurrentThreadId() == g_idMainThread);
252 if (pDirObj)
253 {
254 if (pDirObj->bObjType == KFSOBJ_TYPE_DIR)
255 {
256 if (kFsCacheDirEnsurePopuplated(g_pFsCache, (PKFSDIR)pDirObj, NULL))
257 {
258 KMKNTOPENDIR *pDir = xmalloc(sizeof(*pDir));
259 pDir->pDir = (PKFSDIR)pDirObj;
260 pDir->idxNext = 0;
261 return pDir;
262 }
263 }
264 kFsCacheObjRelease(g_pFsCache, pDirObj);
265 }
266 return NULL;
267}
268
269
270/**
271 * readdir for glob.
272 *
273 * @returns Pointer to DIR like handle, NULL if directory not found.
274 * @param pDir Directory enum handle by dir_glob_opendir.
275 */
276static struct dirent *dir_glob_readdir(__ptr_t pvDir)
277{
278 KMKNTOPENDIR *pDir = (KMKNTOPENDIR *)pvDir;
279 KU32 const cChildren = pDir->pDir->cChildren;
280 assert(GetCurrentThreadId() == g_idMainThread);
281 while (pDir->idxNext < cChildren)
282 {
283 PKFSOBJ pEntry = pDir->pDir->papChildren[pDir->idxNext++];
284
285 /* Don't return missing objects. */
286 if (pEntry->bObjType != KFSOBJ_TYPE_MISSING)
287 {
288 /* Copy the name that fits, trying to avoid names with spaces.
289 If neither fits, skip the name. */
290 if ( pEntry->cchName < sizeof(pDir->DirEnt.d_name)
291 && ( pEntry->pszShortName == pEntry->pszName
292 || memchr(pEntry->pszName, ' ', pEntry->cchName) == NULL))
293 {
294 pDir->DirEnt.d_namlen = pEntry->cchName;
295 memcpy(pDir->DirEnt.d_name, pEntry->pszName, pEntry->cchName + 1);
296 }
297 else if (pEntry->cchShortName < sizeof(pDir->DirEnt.d_name))
298 {
299 pDir->DirEnt.d_namlen = pEntry->cchShortName;
300 memcpy(pDir->DirEnt.d_name, pEntry->pszShortName, pEntry->cchShortName + 1);
301 }
302 else
303 continue;
304
305 pDir->DirEnt.d_reclen = offsetof(struct dirent, d_name) + pDir->DirEnt.d_namlen;
306 if (pEntry->bObjType == KFSOBJ_TYPE_DIR)
307 pDir->DirEnt.d_type = DT_DIR;
308 else if (pEntry->bObjType == KFSOBJ_TYPE_FILE)
309 pDir->DirEnt.d_type = DT_REG;
310 else
311 pDir->DirEnt.d_type = DT_UNKNOWN;
312
313 return &pDir->DirEnt;
314 }
315 }
316
317 /*
318 * Fake the '.' and '..' directories because they're not part of papChildren above.
319 */
320 if (pDir->idxNext < cChildren + 2)
321 {
322 pDir->idxNext++;
323 pDir->DirEnt.d_type = DT_DIR;
324 pDir->DirEnt.d_namlen = pDir->idxNext - cChildren;
325 pDir->DirEnt.d_reclen = offsetof(struct dirent, d_name) + pDir->DirEnt.d_namlen;
326 pDir->DirEnt.d_name[0] = '.';
327 pDir->DirEnt.d_name[1] = '.';
328 pDir->DirEnt.d_name[pDir->DirEnt.d_namlen] = '\0';
329 return &pDir->DirEnt;
330 }
331
332 return NULL;
333}
334
335
336/**
337 * closedir for glob.
338 *
339 * @param pDir Directory enum handle by dir_glob_opendir.
340 */
341static void dir_glob_closedir(__ptr_t pvDir)
342{
343 KMKNTOPENDIR *pDir = (KMKNTOPENDIR *)pvDir;
344 assert(GetCurrentThreadId() == g_idMainThread);
345 kFsCacheObjRelease(g_pFsCache, &pDir->pDir->Obj);
346 pDir->pDir = NULL;
347 free(pDir);
348}
349
350
351/**
352 * stat for glob.
353 *
354 * @returns 0 on success, -1 + errno on failure.
355 * @param pszPath The path to stat.
356 * @param pStat Where to return the info.
357 */
358static int dir_glob_stat(const char *pszPath, struct stat *pStat)
359{
360 KFSLOOKUPERROR enmError;
361 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
362 assert(GetCurrentThreadId() == g_idMainThread);
363/** @todo follow symlinks vs. on symlink! */
364 if (pPathObj)
365 {
366 if (pPathObj->bObjType != KFSOBJ_TYPE_MISSING)
367 {
368 kHlpAssert(pPathObj->fHaveStats); /* currently always true. */
369 *pStat = pPathObj->Stats;
370 kFsCacheObjRelease(g_pFsCache, pPathObj);
371 return 0;
372 }
373 kFsCacheObjRelease(g_pFsCache, pPathObj);
374 }
375 errno = ENOENT;
376 return -1;
377}
378
379
380/**
381 * lstat for glob.
382 *
383 * @returns 0 on success, -1 + errno on failure.
384 * @param pszPath The path to stat.
385 * @param pStat Where to return the info.
386 */
387static int dir_glob_lstat(const char *pszPath, struct stat *pStat)
388{
389 KFSLOOKUPERROR enmError;
390 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
391 assert(GetCurrentThreadId() == g_idMainThread);
392 if (pPathObj)
393 {
394 if (pPathObj->bObjType != KFSOBJ_TYPE_MISSING)
395 {
396 kHlpAssert(pPathObj->fHaveStats); /* currently always true. */
397 *pStat = pPathObj->Stats;
398 kFsCacheObjRelease(g_pFsCache, pPathObj);
399 return 0;
400 }
401 kFsCacheObjRelease(g_pFsCache, pPathObj);
402 errno = ENOENT;
403 }
404 else
405 errno = enmError == KFSLOOKUPERROR_NOT_DIR
406 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR
407 ? ENOTDIR : ENOENT;
408
409 return -1;
410}
411
412
413/**
414 * Checks if @a pszDir exists and is a directory.
415 *
416 * @returns 1 if is directory, 0 if isn't or doesn't exists.
417 * @param pszDir The alleged directory.
418 */
419static int dir_globl_dir_exists_p(const char *pszDir)
420{
421 int fRc;
422 KFSLOOKUPERROR enmError;
423 PKFSOBJ pDirObj = kFsCacheLookupA(g_pFsCache, pszDir, &enmError);
424 assert(GetCurrentThreadId() == g_idMainThread);
425 if (pDirObj)
426 {
427 fRc = pDirObj->bObjType == KFSOBJ_TYPE_DIR;
428 kFsCacheObjRelease(g_pFsCache, pDirObj);
429 }
430 else
431 fRc = 0;
432 return fRc;
433
434}
435
436
437/**
438 * Sets up pGlob with the necessary callbacks.
439 *
440 * @param pGlob Structure to populate.
441 */
442void dir_setup_glob(glob_t *pGlob)
443{
444 assert(GetCurrentThreadId() == g_idMainThread);
445 pGlob->gl_opendir = dir_glob_opendir;
446 pGlob->gl_readdir = dir_glob_readdir;
447 pGlob->gl_closedir = dir_glob_closedir;
448 pGlob->gl_stat = dir_glob_stat;
449#ifdef __EMX__ /* The FreeBSD implementation actually uses gl_lstat!! */
450 pGlob->gl_lstat = dir_glob_lstat;
451#else
452 pGlob->gl_exists = file_exists_p;
453 pGlob->gl_isdir = dir_globl_dir_exists_p;
454#endif
455}
456
457
458/**
459 * Print statitstics.
460 */
461void print_dir_stats(void)
462{
463 FILE *pOut = stdout;
464 KU32 cMisses;
465
466 fputs("\n"
467 "# NT dir cache stats:\n", pOut);
468 fprintf(pOut, "# %u objects, taking up %u (%#x) bytes, avg %u bytes\n",
469 g_pFsCache->cObjects, g_pFsCache->cbObjects, g_pFsCache->cbObjects, g_pFsCache->cbObjects / g_pFsCache->cObjects);
470 fprintf(pOut, "# %u A path hashes, taking up %u (%#x) bytes, avg %u bytes, %u collision\n",
471 g_pFsCache->cAnsiPaths, g_pFsCache->cbAnsiPaths, g_pFsCache->cbAnsiPaths,
472 g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1), g_pFsCache->cAnsiPathCollisions);
473#ifdef KFSCACHE_CFG_UTF16
474 fprintf(pOut, "# %u W path hashes, taking up %u (%#x) bytes, avg %u bytes, %u collisions\n",
475 g_pFsCache->cUtf16Paths, g_pFsCache->cbUtf16Paths, g_pFsCache->cbUtf16Paths,
476 g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1), g_pFsCache->cUtf16PathCollisions);
477#endif
478 fprintf(pOut, "# %u child hash tables, total of %u entries, %u children inserted, %u collisions\n",
479 g_pFsCache->cChildHashTabs, g_pFsCache->cChildHashEntriesTotal,
480 g_pFsCache->cChildHashed, g_pFsCache->cChildHashCollisions);
481
482 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
483 fprintf(pOut, "# %u lookups: %u (%" KU64_PRI " %%) path hash hits, %u (%" KU64_PRI "%%) walks hits, %u (%" KU64_PRI "%%) misses\n",
484 g_pFsCache->cLookups,
485 g_pFsCache->cPathHashHits, g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1),
486 g_pFsCache->cWalkHits, g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1),
487 cMisses, cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1));
488 fprintf(pOut, "# %u child searches, %u (%" KU64_PRI "%%) hash hits\n",
489 g_pFsCache->cChildSearches,
490 g_pFsCache->cChildHashHits, g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1));
491}
492
493
494void print_dir_data_base(void)
495{
496 /** @todo. */
497
498}
499
500
501/* duplicated in kWorker.c
502 * Note! Tries avoid to produce a result with spaces since they aren't supported by makefiles. */
503void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
504{
505 KFSLOOKUPERROR enmError;
506 PKFSOBJ pPathObj;
507
508 KFSCACHE_LOCK(g_pFsCache); /* let's start out being careful. */
509
510 pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
511 if (pPathObj)
512 {
513 KSIZE off = pPathObj->cchParent;
514 if (off > 0)
515 {
516 KSIZE offEnd = off + pPathObj->cchName;
517 if (offEnd < cbFull)
518 {
519 PKFSDIR pAncestor;
520
521 pszFull[offEnd] = '\0';
522 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
523
524 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
525 {
526 kHlpAssert(off > 1);
527 kHlpAssert(pAncestor != NULL);
528 kHlpAssert(pAncestor->Obj.cchName > 0);
529 pszFull[--off] = '/';
530#ifdef KFSCACHE_CFG_SHORT_NAMES
531 if ( pAncestor->Obj.pszName == pAncestor->Obj.pszShortName
532 || memchr(pAncestor->Obj.pszName, ' ', pAncestor->Obj.cchName) == NULL)
533#endif
534 {
535 off -= pAncestor->Obj.cchName;
536 kHlpAssert(pAncestor->Obj.cchParent == off);
537 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
538 }
539#ifdef KFSCACHE_CFG_SHORT_NAMES
540 else
541 {
542 /*
543 * The long name constains a space, so use the alternative name instead.
544 * Most likely the alternative name differs in length, usually it's shorter,
545 * so we have to shift the part of the path we've already assembled
546 * accordingly.
547 */
548 KSSIZE cchDelta = (KSSIZE)pAncestor->Obj.cchShortName - (KSSIZE)pAncestor->Obj.cchName;
549 if (cchDelta != 0)
550 {
551 if ((KSIZE)(offEnd + cchDelta) >= cbFull)
552 goto l_fallback;
553 memmove(&pszFull[off + cchDelta], &pszFull[off], offEnd + 1 - off);
554 off += cchDelta;
555 offEnd += cchDelta;
556 }
557 off -= pAncestor->Obj.cchShortName;
558 kHlpAssert(pAncestor->Obj.cchParent == off);
559 memcpy(&pszFull[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
560 }
561#endif
562 }
563 kFsCacheObjRelease(g_pFsCache, pPathObj);
564 KFSCACHE_UNLOCK(g_pFsCache);
565 return;
566 }
567 }
568 else
569 {
570 if ((size_t)pPathObj->cchName + 1 < cbFull)
571 {
572 /* Assume no spaces here. */
573 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
574 pszFull[pPathObj->cchName] = '/';
575 pszFull[pPathObj->cchName + 1] = '\0';
576
577 kFsCacheObjRelease(g_pFsCache, pPathObj);
578 KFSCACHE_UNLOCK(g_pFsCache);
579 return;
580 }
581 }
582
583 /* do fallback. */
584#ifdef KFSCACHE_CFG_SHORT_NAMES
585l_fallback:
586#endif
587 kHlpAssertFailed();
588 kFsCacheObjRelease(g_pFsCache, pPathObj);
589 }
590 KFSCACHE_UNLOCK(g_pFsCache);
591
592 nt_fullpath(pszPath, pszFull, cbFull);
593}
594
595
596/**
597 * Special stat call used by remake.c
598 *
599 * @returns 0 on success, -1 + errno on failure.
600 * @param pszPath The path to stat.
601 * @param pStat Where to return the mtime field only.
602 */
603int stat_only_mtime(const char *pszPath, struct stat *pStat)
604{
605 /* Currently a little expensive, so just hit the file system once the
606 jobs starts comming in. */
607 assert(GetCurrentThreadId() == g_idMainThread);
608 if (g_cInvalidates == 0)
609 {
610 KFSLOOKUPERROR enmError;
611 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
612 if (pPathObj)
613 {
614 if (pPathObj->bObjType != KFSOBJ_TYPE_MISSING)
615 {
616 kHlpAssert(pPathObj->fHaveStats); /* currently always true. */
617 pStat->st_mtime = pPathObj->Stats.st_mtime;
618 kFsCacheObjRelease(g_pFsCache, pPathObj);
619 return 0;
620 }
621
622 kFsCacheObjRelease(g_pFsCache, pPathObj);
623 errno = ENOENT;
624 }
625 else
626 errno = enmError == KFSLOOKUPERROR_NOT_DIR
627 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR
628 ? ENOTDIR : ENOENT;
629 return -1;
630 }
631 return birdStatModTimeOnly(pszPath, &pStat->st_mtim, 1 /*fFollowLink*/);
632}
633
634/**
635 * Do cache invalidation after a job completes.
636 */
637void dir_cache_invalid_after_job(void)
638{
639 assert(GetCurrentThreadId() == g_idMainThread);
640 g_cInvalidates++;
641 if (g_fFsCacheIsUsingCustomRevision)
642 kFsCacheInvalidateCustomBoth(g_pFsCache);
643 else
644 kFsCacheInvalidateAll(g_pFsCache);
645}
646
647/**
648 * Invalidate the whole directory cache
649 *
650 * Used by $(dircache-ctl invalidate)
651 */
652void dir_cache_invalid_all(void)
653{
654 assert(GetCurrentThreadId() == g_idMainThread);
655 g_cInvalidates++;
656 kFsCacheInvalidateAll(g_pFsCache);
657}
658
659/**
660 * Invalidate missing bits of the directory cache.
661 *
662 * Used by $(dircache-ctl invalidate-missing)
663 */
664void dir_cache_invalid_missing(void)
665{
666 assert(GetCurrentThreadId() == g_idMainThread);
667 g_cInvalidates++;
668 kFsCacheInvalidateAll(g_pFsCache);
669}
670
671/**
672 * Invalidate the volatile bits of the directory cache.
673 *
674 * Used by $(dircache-ctl invalidate-missing)
675 */
676void dir_cache_invalid_volatile(void)
677{
678 assert(GetCurrentThreadId() == g_idMainThread);
679 g_cInvalidates++;
680 if (g_fFsCacheIsUsingCustomRevision)
681 kFsCacheInvalidateCustomBoth(g_pFsCache);
682 else
683 kFsCacheInvalidateAll(g_pFsCache);
684}
685
686/**
687 * Used by $(dircache-ctl ) to mark a directory subtree or file as volatile.
688 *
689 * The first call changes the rest of the cache to be considered non-volatile.
690 *
691 * @returns 0 on success, -1 on failure.
692 * @param pszDir The directory (or file for what that is worth).
693 */
694int dir_cache_volatile_dir(const char *pszDir)
695{
696 KFSLOOKUPERROR enmError;
697 PKFSOBJ pObj = kFsCacheLookupA(g_pFsCache, pszDir, &enmError);
698 assert(GetCurrentThreadId() == g_idMainThread);
699 if (pObj)
700 {
701 KBOOL fRc = kFsCacheSetupCustomRevisionForTree(g_pFsCache, pObj);
702 kFsCacheObjRelease(g_pFsCache, pObj);
703 if (fRc)
704 {
705 g_fFsCacheIsUsingCustomRevision = K_TRUE;
706 return 0;
707 }
708 OS(error, reading_file, "failed to mark '%s' as volatile", pszDir);
709 }
710 else
711 OS(error, reading_file, "failed to mark '%s' as volatile (not found)", pszDir);
712 return -1;
713}
714
715/**
716 * Invalidates a deleted directory so the cache can close handles to it.
717 *
718 * Used by kmk_builtin_rm and kmk_builtin_rmdir.
719 *
720 * @returns 0 on success, -1 on failure.
721 * @param pszDir The directory to invalidate as deleted.
722 */
723int dir_cache_deleted_directory(const char *pszDir)
724{
725 assert(GetCurrentThreadId() == g_idMainThread);
726 if (kFsCacheInvalidateDeletedDirectoryA(g_pFsCache, pszDir))
727 return 0;
728 return -1;
729}
730
731
732int kmk_builtin_dircache(int argc, char **argv, char **envp)
733{
734 assert(GetCurrentThreadId() == g_idMainThread);
735 if (argc >= 2)
736 {
737 const char *pszCmd = argv[1];
738 if (strcmp(pszCmd, "invalidate") == 0)
739 {
740 if (argc == 2)
741 {
742 dir_cache_invalid_all();
743 return 0;
744 }
745 fprintf(stderr, "kmk_builtin_dircache: the 'invalidate' command takes no arguments!\n");
746 }
747 else if (strcmp(pszCmd, "invalidate-missing") == 0)
748 {
749 if (argc == 2)
750 {
751 dir_cache_invalid_missing ();
752 return 0;
753 }
754 fprintf(stderr, "kmk_builtin_dircache: the 'invalidate-missing' command takes no arguments!\n");
755 }
756 else if (strcmp(pszCmd, "volatile") == 0)
757 {
758 int i;
759 for (i = 2; i < argc; i++)
760 dir_cache_volatile_dir(argv[i]);
761 return 0;
762 }
763 else if (strcmp(pszCmd, "deleted") == 0)
764 {
765 int i;
766 for (i = 2; i < argc; i++)
767 dir_cache_deleted_directory(argv[i]);
768 return 0;
769 }
770 else
771 fprintf(stderr, "kmk_builtin_dircache: Invalid command '%s'!\n", pszCmd);
772 }
773 else
774 fprintf(stderr, "kmk_builtin_dircache: No command given!\n");
775
776 K_NOREF(envp);
777 return 2;
778}
779
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