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

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

kDep*: no globals; dir-nt-bird.c: only main thread

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