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