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

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

kmk/win: Run kDepObj on worker thread.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 24.7 KB
Line 
1/* $Id: dir-nt-bird.c 3169 2018-03-21 11:27:47Z 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 if (GetCurrentThreadId() == g_idMainThread)
486 {
487 KFSLOOKUPERROR enmError;
488 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
489 if (pPathObj)
490 {
491 KSIZE off = pPathObj->cchParent;
492 if (off > 0)
493 {
494 KSIZE offEnd = off + pPathObj->cchName;
495 if (offEnd < cbFull)
496 {
497 PKFSDIR pAncestor;
498
499 pszFull[offEnd] = '\0';
500 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
501
502 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
503 {
504 kHlpAssert(off > 1);
505 kHlpAssert(pAncestor != NULL);
506 kHlpAssert(pAncestor->Obj.cchName > 0);
507 pszFull[--off] = '/';
508#ifdef KFSCACHE_CFG_SHORT_NAMES
509 if ( pAncestor->Obj.pszName == pAncestor->Obj.pszShortName
510 || memchr(pAncestor->Obj.pszName, ' ', pAncestor->Obj.cchName) == NULL)
511#endif
512 {
513 off -= pAncestor->Obj.cchName;
514 kHlpAssert(pAncestor->Obj.cchParent == off);
515 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
516 }
517#ifdef KFSCACHE_CFG_SHORT_NAMES
518 else
519 {
520 /*
521 * The long name constains a space, so use the alternative name instead.
522 * Most likely the alternative name differs in length, usually it's shorter,
523 * so we have to shift the part of the path we've already assembled
524 * accordingly.
525 */
526 KSSIZE cchDelta = (KSSIZE)pAncestor->Obj.cchShortName - (KSSIZE)pAncestor->Obj.cchName;
527 if (cchDelta != 0)
528 {
529 if ((KSIZE)(offEnd + cchDelta) >= cbFull)
530 goto l_fallback;
531 memmove(&pszFull[off + cchDelta], &pszFull[off], offEnd + 1 - off);
532 off += cchDelta;
533 offEnd += cchDelta;
534 }
535 off -= pAncestor->Obj.cchShortName;
536 kHlpAssert(pAncestor->Obj.cchParent == off);
537 memcpy(&pszFull[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
538 }
539#endif
540 }
541 kFsCacheObjRelease(g_pFsCache, pPathObj);
542 return;
543 }
544 }
545 else
546 {
547 if ((size_t)pPathObj->cchName + 1 < cbFull)
548 {
549 /* Assume no spaces here. */
550 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
551 pszFull[pPathObj->cchName] = '/';
552 pszFull[pPathObj->cchName + 1] = '\0';
553
554 kFsCacheObjRelease(g_pFsCache, pPathObj);
555 return;
556 }
557 }
558
559 /* do fallback. */
560#ifdef KFSCACHE_CFG_SHORT_NAMES
561l_fallback:
562#endif
563 kHlpAssertFailed();
564 kFsCacheObjRelease(g_pFsCache, pPathObj);
565 }
566 }
567
568 nt_fullpath(pszPath, pszFull, cbFull);
569}
570
571
572/**
573 * Special stat call used by remake.c
574 *
575 * @returns 0 on success, -1 + errno on failure.
576 * @param pszPath The path to stat.
577 * @param pStat Where to return the mtime field only.
578 */
579int stat_only_mtime(const char *pszPath, struct stat *pStat)
580{
581 /* Currently a little expensive, so just hit the file system once the
582 jobs starts comming in. */
583 assert(GetCurrentThreadId() == g_idMainThread);
584 if (g_cInvalidates == 0)
585 {
586 KFSLOOKUPERROR enmError;
587 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
588 if (pPathObj)
589 {
590 if (pPathObj->bObjType != KFSOBJ_TYPE_MISSING)
591 {
592 kHlpAssert(pPathObj->fHaveStats); /* currently always true. */
593 pStat->st_mtime = pPathObj->Stats.st_mtime;
594 kFsCacheObjRelease(g_pFsCache, pPathObj);
595 return 0;
596 }
597
598 kFsCacheObjRelease(g_pFsCache, pPathObj);
599 errno = ENOENT;
600 }
601 else
602 errno = enmError == KFSLOOKUPERROR_NOT_DIR
603 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR
604 ? ENOTDIR : ENOENT;
605 return -1;
606 }
607 return birdStatModTimeOnly(pszPath, &pStat->st_mtim, 1 /*fFollowLink*/);
608}
609
610/**
611 * Do cache invalidation after a job completes.
612 */
613void dir_cache_invalid_after_job(void)
614{
615 assert(GetCurrentThreadId() == g_idMainThread);
616 g_cInvalidates++;
617 if (g_fFsCacheIsUsingCustomRevision)
618 kFsCacheInvalidateCustomBoth(g_pFsCache);
619 else
620 kFsCacheInvalidateAll(g_pFsCache);
621}
622
623/**
624 * Invalidate the whole directory cache
625 *
626 * Used by $(dircache-ctl invalidate)
627 */
628void dir_cache_invalid_all(void)
629{
630 assert(GetCurrentThreadId() == g_idMainThread);
631 g_cInvalidates++;
632 kFsCacheInvalidateAll(g_pFsCache);
633}
634
635/**
636 * Invalidate missing bits of the directory cache.
637 *
638 * Used by $(dircache-ctl invalidate-missing)
639 */
640void dir_cache_invalid_missing(void)
641{
642 assert(GetCurrentThreadId() == g_idMainThread);
643 g_cInvalidates++;
644 kFsCacheInvalidateAll(g_pFsCache);
645}
646
647/**
648 * Invalidate the volatile bits of the directory cache.
649 *
650 * Used by $(dircache-ctl invalidate-missing)
651 */
652void dir_cache_invalid_volatile(void)
653{
654 assert(GetCurrentThreadId() == g_idMainThread);
655 g_cInvalidates++;
656 if (g_fFsCacheIsUsingCustomRevision)
657 kFsCacheInvalidateCustomBoth(g_pFsCache);
658 else
659 kFsCacheInvalidateAll(g_pFsCache);
660}
661
662/**
663 * Used by $(dircache-ctl ) to mark a directory subtree or file as volatile.
664 *
665 * The first call changes the rest of the cache to be considered non-volatile.
666 *
667 * @returns 0 on success, -1 on failure.
668 * @param pszDir The directory (or file for what that is worth).
669 */
670int dir_cache_volatile_dir(const char *pszDir)
671{
672 KFSLOOKUPERROR enmError;
673 PKFSOBJ pObj = kFsCacheLookupA(g_pFsCache, pszDir, &enmError);
674 assert(GetCurrentThreadId() == g_idMainThread);
675 if (pObj)
676 {
677 KBOOL fRc = kFsCacheSetupCustomRevisionForTree(g_pFsCache, pObj);
678 kFsCacheObjRelease(g_pFsCache, pObj);
679 if (fRc)
680 {
681 g_fFsCacheIsUsingCustomRevision = K_TRUE;
682 return 0;
683 }
684 OS(error, reading_file, "failed to mark '%s' as volatile", pszDir);
685 }
686 else
687 OS(error, reading_file, "failed to mark '%s' as volatile (not found)", pszDir);
688 return -1;
689}
690
691/**
692 * Invalidates a deleted directory so the cache can close handles to it.
693 *
694 * Used by kmk_builtin_rm and kmk_builtin_rmdir.
695 *
696 * @returns 0 on success, -1 on failure.
697 * @param pszDir The directory to invalidate as deleted.
698 */
699int dir_cache_deleted_directory(const char *pszDir)
700{
701 assert(GetCurrentThreadId() == g_idMainThread);
702 if (kFsCacheInvalidateDeletedDirectoryA(g_pFsCache, pszDir))
703 return 0;
704 return -1;
705}
706
707
708int kmk_builtin_dircache(int argc, char **argv, char **envp)
709{
710 assert(GetCurrentThreadId() == g_idMainThread);
711 if (argc >= 2)
712 {
713 const char *pszCmd = argv[1];
714 if (strcmp(pszCmd, "invalidate") == 0)
715 {
716 if (argc == 2)
717 {
718 dir_cache_invalid_all();
719 return 0;
720 }
721 fprintf(stderr, "kmk_builtin_dircache: the 'invalidate' command takes no arguments!\n");
722 }
723 else if (strcmp(pszCmd, "invalidate-missing") == 0)
724 {
725 if (argc == 2)
726 {
727 dir_cache_invalid_missing ();
728 return 0;
729 }
730 fprintf(stderr, "kmk_builtin_dircache: the 'invalidate-missing' command takes no arguments!\n");
731 }
732 else if (strcmp(pszCmd, "volatile") == 0)
733 {
734 int i;
735 for (i = 2; i < argc; i++)
736 dir_cache_volatile_dir(argv[i]);
737 return 0;
738 }
739 else if (strcmp(pszCmd, "deleted") == 0)
740 {
741 int i;
742 for (i = 2; i < argc; i++)
743 dir_cache_deleted_directory(argv[i]);
744 return 0;
745 }
746 else
747 fprintf(stderr, "kmk_builtin_dircache: Invalid command '%s'!\n", pszCmd);
748 }
749 else
750 fprintf(stderr, "kmk_builtin_dircache: No command given!\n");
751
752 K_NOREF(envp);
753 return 2;
754}
755
Note: See TracBrowser for help on using the repository browser.