VirtualBox

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

Last change on this file since 3387 was 3359, checked in by bird, 4 years ago

kmk,kFsCache: Added variant of kFsCacheInvalidateAll that also closes directory handles, we need to do this befor ere-executing kmk after having remake some include file we needed. It messes up fetching otherwise.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use