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

Last change on this file since 2882 was 2879, checked in by bird, 9 years ago

fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.6 KB
Line 
1/* $Id: dir-nt-bird.c 2879 2016-09-05 20:14:21Z 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 */
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
78
79void hash_init_directories(void)
80{
81 g_pFsCache = kFsCacheCreate(0);
82 if (g_pFsCache)
83 return;
84 fputs("kFsCacheCreate failed!", stderr);
85 exit(9);
86}
87
88
89/**
90 * Checks if @a pszName exists in directory @a pszDir.
91 *
92 * @returns 1 if it does, 0 if it doesn't.
93 *
94 * @param pszDir The directory.
95 * @param pszName The name.
96 *
97 * If empty string, just check if the directory exists.
98 *
99 * If NULL, just read the whole cache the directory into
100 * the cache (we always do that).
101 */
102int dir_file_exists_p(const char *pszDir, const char *pszName)
103{
104 int fRc = 0;
105 KFSLOOKUPERROR enmError;
106 PKFSOBJ pDirObj = kFsCacheLookupA(g_pFsCache, pszDir, &enmError);
107 if (pDirObj)
108 {
109 if (pDirObj->bObjType == KFSOBJ_TYPE_DIR)
110 {
111 if (pszName != 0)
112 {
113 /* Empty filename is just checking out the directory. */
114 if (*pszName == '\0')
115 fRc = 1;
116 else
117 {
118 PKFSOBJ pNameObj = kFsCacheLookupRelativeToDirA(g_pFsCache, (PKFSDIR)pDirObj,
119 pszName, strlen(pszName), &enmError, NULL);
120 if (pNameObj)
121 {
122 fRc = pNameObj->bObjType == KFSOBJ_TYPE_MISSING;
123 kFsCacheObjRelease(g_pFsCache, pNameObj);
124 }
125 }
126 }
127 }
128 kFsCacheObjRelease(g_pFsCache, pDirObj);
129 }
130 return fRc;
131}
132
133
134/**
135 * Checks if a file exists.
136 *
137 * @returns 1 if it does exist, 0 if it doesn't.
138 * @param pszPath The path to check out.
139 */
140int file_exists_p(const char *pszPath)
141{
142 int fRc;
143 KFSLOOKUPERROR enmError;
144 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
145 if (pPathObj)
146 {
147 fRc = pPathObj->bObjType != KFSOBJ_TYPE_MISSING;
148 kFsCacheObjRelease(g_pFsCache, pPathObj);
149 }
150 else
151 fRc = 0;
152 return fRc;
153}
154
155
156/**
157 * Just a way for vpath.c to get a correctly cased path, I think.
158 *
159 * @returns Directory path in string cache.
160 * @param pszDir The directory.
161 */
162const char *dir_name(const char *pszDir)
163{
164 char szTmp[MAX_PATH];
165 nt_fullpath(pszDir, szTmp, sizeof(szTmp));
166 return strcache_add(szTmp);
167}
168
169
170/**
171 * Makes future file_impossible_p calls return 1 for pszPath.
172 */
173void file_impossible(const char *pszPath)
174{
175 KFSLOOKUPERROR enmError;
176 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
177 if (pPathObj)
178 {
179 kFsCacheObjAddUserData(g_pFsCache, pPathObj, KMK_DIR_NT_IMPOSSIBLE_KEY, sizeof(KFSUSERDATA));
180 kFsCacheObjRelease(g_pFsCache, pPathObj);
181 }
182}
183
184/**
185 * Makes future file_impossible_p calls return 1 for pszPath.
186 */
187int file_impossible_p(const char *pszPath)
188{
189 int fRc;
190 KFSLOOKUPERROR enmError;
191 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
192 if (pPathObj)
193 {
194 fRc = kFsCacheObjGetUserData(g_pFsCache, pPathObj, KMK_DIR_NT_IMPOSSIBLE_KEY) != NULL;
195 kFsCacheObjRelease(g_pFsCache, pPathObj);
196 }
197 else
198 fRc = 0;
199 return fRc;
200}
201
202
203/**
204 * opendir for glob.
205 *
206 * @returns Pointer to DIR like handle, NULL if directory not found.
207 * @param pszDir The directory to enumerate.
208 */
209static __ptr_t dir_glob_opendir(const char *pszDir)
210{
211 KFSLOOKUPERROR enmError;
212 PKFSOBJ pDirObj = kFsCacheLookupA(g_pFsCache, pszDir, &enmError);
213 if (pDirObj)
214 {
215 if (pDirObj->bObjType == KFSOBJ_TYPE_DIR)
216 {
217 if (kFsCacheDirEnsurePopuplated(g_pFsCache, (PKFSDIR)pDirObj, NULL))
218 {
219 KMKNTOPENDIR *pDir = xmalloc(sizeof(*pDir));
220 pDir->pDir = (PKFSDIR)pDirObj;
221 pDir->idxNext = 0;
222 return pDir;
223 }
224 }
225 kFsCacheObjRelease(g_pFsCache, pDirObj);
226 }
227 return NULL;
228}
229
230
231/**
232 * readdir for glob.
233 *
234 * @returns Pointer to DIR like handle, NULL if directory not found.
235 * @param pDir Directory enum handle by dir_glob_opendir.
236 */
237static struct dirent *dir_glob_readdir(__ptr_t pvDir)
238{
239 KMKNTOPENDIR *pDir = (KMKNTOPENDIR *)pvDir;
240 KU32 const cChildren = pDir->pDir->cChildren;
241 while (pDir->idxNext < cChildren)
242 {
243 PKFSOBJ pEntry = pDir->pDir->papChildren[pDir->idxNext++];
244
245 /* Don't return missing objects. */
246 if (pEntry->bObjType != KFSOBJ_TYPE_MISSING)
247 {
248 /* Copy the name that fits. If neither fits, skip the name. */
249 if (pEntry->cchName < sizeof(pDir->DirEnt.d_name))
250 {
251 pDir->DirEnt.d_namlen = pEntry->cchName;
252 memcpy(pDir->DirEnt.d_name, pEntry->pszName, pEntry->cchName + 1);
253 }
254 else if (pEntry->cchShortName < sizeof(pDir->DirEnt.d_name))
255 {
256 pDir->DirEnt.d_namlen = pEntry->cchShortName;
257 memcpy(pDir->DirEnt.d_name, pEntry->pszShortName, pEntry->cchShortName + 1);
258 }
259 else
260 continue;
261
262 pDir->DirEnt.d_reclen = offsetof(struct dirent, d_name) + pDir->DirEnt.d_namlen;
263 if (pEntry->bObjType == KFSOBJ_TYPE_DIR)
264 pDir->DirEnt.d_type = DT_DIR;
265 else if (pEntry->bObjType == KFSOBJ_TYPE_FILE)
266 pDir->DirEnt.d_type = DT_REG;
267 else
268 pDir->DirEnt.d_type = DT_UNKNOWN;
269
270 return &pDir->DirEnt;
271 }
272 }
273
274 /*
275 * Fake the '.' and '..' directories because they're not part of papChildren above.
276 */
277 if (pDir->idxNext < cChildren + 2)
278 {
279 pDir->idxNext++;
280 pDir->DirEnt.d_type = DT_DIR;
281 pDir->DirEnt.d_namlen = pDir->idxNext - cChildren;
282 pDir->DirEnt.d_reclen = offsetof(struct dirent, d_name) + pDir->DirEnt.d_namlen;
283 pDir->DirEnt.d_name[0] = '.';
284 pDir->DirEnt.d_name[1] = '.';
285 pDir->DirEnt.d_name[pDir->DirEnt.d_namlen] = '\0';
286 return &pDir->DirEnt;
287 }
288
289 return NULL;
290}
291
292
293/**
294 * closedir for glob.
295 *
296 * @param pDir Directory enum handle by dir_glob_opendir.
297 */
298static void dir_glob_closedir(__ptr_t pvDir)
299{
300 KMKNTOPENDIR *pDir = (KMKNTOPENDIR *)pvDir;
301 kFsCacheObjRelease(g_pFsCache, &pDir->pDir->Obj);
302 pDir->pDir = NULL;
303 free(pDir);
304}
305
306
307/**
308 * stat for glob.
309 *
310 * @returns 0 on success, -1 + errno on failure.
311 * @param pszPath The path to stat.
312 * @param pStat Where to return the info.
313 */
314static int dir_glob_stat(const char *pszPath, struct stat *pStat)
315{
316 KFSLOOKUPERROR enmError;
317 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
318/** @todo follow symlinks vs. on symlink! */
319 if (pPathObj)
320 {
321 if (pPathObj->bObjType != KFSOBJ_TYPE_MISSING)
322 {
323 kHlpAssert(pPathObj->fHaveStats); /* currently always true. */
324 *pStat = pPathObj->Stats;
325 kFsCacheObjRelease(g_pFsCache, pPathObj);
326 return 0;
327 }
328 kFsCacheObjRelease(g_pFsCache, pPathObj);
329 }
330 errno = ENOENT;
331 return -1;
332}
333
334
335/**
336 * lstat for glob.
337 *
338 * @returns 0 on success, -1 + errno on failure.
339 * @param pszPath The path to stat.
340 * @param pStat Where to return the info.
341 */
342static int dir_glob_lstat(const char *pszPath, struct stat *pStat)
343{
344 KFSLOOKUPERROR enmError;
345 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
346 if (pPathObj)
347 {
348 if (pPathObj->bObjType != KFSOBJ_TYPE_MISSING)
349 {
350 kHlpAssert(pPathObj->fHaveStats); /* currently always true. */
351 *pStat = pPathObj->Stats;
352 kFsCacheObjRelease(g_pFsCache, pPathObj);
353 return 0;
354 }
355 kFsCacheObjRelease(g_pFsCache, pPathObj);
356 errno = ENOENT;
357 }
358 else
359 errno = enmError == KFSLOOKUPERROR_NOT_DIR
360 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR
361 ? ENOTDIR : ENOENT;
362
363 return -1;
364}
365
366
367/**
368 * Checks if @a pszDir exists and is a directory.
369 *
370 * @returns 1 if is directory, 0 if isn't or doesn't exists.
371 * @param pszDir The alleged directory.
372 */
373static int dir_globl_dir_exists_p(const char *pszDir)
374{
375 int fRc;
376 KFSLOOKUPERROR enmError;
377 PKFSOBJ pDirObj = kFsCacheLookupA(g_pFsCache, pszDir, &enmError);
378 if (pDirObj)
379 {
380 fRc = pDirObj->bObjType == KFSOBJ_TYPE_DIR;
381 kFsCacheObjRelease(g_pFsCache, pDirObj);
382 }
383 else
384 fRc = 0;
385 return fRc;
386
387}
388
389
390/**
391 * Sets up pGlob with the necessary callbacks.
392 *
393 * @param pGlob Structure to populate.
394 */
395void dir_setup_glob(glob_t *pGlob)
396{
397 pGlob->gl_opendir = dir_glob_opendir;
398 pGlob->gl_readdir = dir_glob_readdir;
399 pGlob->gl_closedir = dir_glob_closedir;
400 pGlob->gl_stat = dir_glob_stat;
401#ifdef __EMX__ /* The FreeBSD implementation actually uses gl_lstat!! */
402 pGlob->gl_lstat = dir_glob_lstat;
403#else
404 pGlob->gl_exists = file_exists_p;
405 pGlob->gl_isdir = dir_globl_dir_exists_p;
406#endif
407}
408
409
410void print_dir_data_base(void)
411{
412 /** @todo. */
413}
414
415
416
417void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
418{
419 KFSLOOKUPERROR enmError;
420 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
421 if (pPathObj)
422 {
423 KSIZE off = pPathObj->cchParent;
424 if (off > 0)
425 {
426 KSIZE offEnd = off + pPathObj->cchName;
427 if (offEnd < cbFull)
428 {
429 PKFSDIR pAncestor;
430
431 pszFull[off + pPathObj->cchName] = '\0';
432 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
433
434 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
435 {
436 kHlpAssert(off > 1);
437 kHlpAssert(pAncestor != NULL);
438 kHlpAssert(pAncestor->ObjcchName > 0);
439 pszFull[--off] = '/';
440 off -= pAncestor->Obj.cchName;
441 kHlpAssert(pAncestor->Obj.cchParent == off);
442 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
443 }
444 kFsCacheObjRelease(g_pFsCache, pPathObj);
445 return;
446 }
447 }
448 else
449 {
450 if ((size_t)pPathObj->cchName + 1 < cbFull)
451 {
452 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
453 pszFull[pPathObj->cchName] = '/';
454 pszFull[pPathObj->cchName + 1] = '\0';
455
456 kFsCacheObjRelease(g_pFsCache, pPathObj);
457 return;
458 }
459 }
460
461 /* do fallback. */
462 kHlpAssertFailed();
463 kFsCacheObjRelease(g_pFsCache, pPathObj);
464 }
465
466 nt_fullpath(pszPath, pszFull, cbFull);
467}
468
469
470/**
471 * Special stat call used by remake.c
472 *
473 * @returns 0 on success, -1 + errno on failure.
474 * @param pszPath The path to stat.
475 * @param pStat Where to return the mtime field only.
476 */
477int stat_only_mtime(const char *pszPath, struct stat *pStat)
478{
479 /* Currently a little expensive, so just hit the file system once the
480 jobs starts comming in. */
481 if (g_cInvalidates == 0)
482 {
483 KFSLOOKUPERROR enmError;
484 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
485 if (pPathObj)
486 {
487 if (pPathObj->bObjType != KFSOBJ_TYPE_MISSING)
488 {
489 kHlpAssert(pPathObj->fHaveStats); /* currently always true. */
490 pStat->st_mtime = pPathObj->Stats.st_mtime;
491 kFsCacheObjRelease(g_pFsCache, pPathObj);
492 return 0;
493 }
494
495 kFsCacheObjRelease(g_pFsCache, pPathObj);
496 errno = ENOENT;
497 }
498 else
499 errno = enmError == KFSLOOKUPERROR_NOT_DIR
500 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR
501 ? ENOTDIR : ENOENT;
502 return -1;
503 }
504 return birdStatModTimeOnly(pszPath, &pStat->st_mtim, 1 /*fFollowLink*/);
505}
506
507
508/**
509 * Invalidate missing bits of the directory cache.
510 *
511 * This is called each time a make job completes.
512 */
513void dir_cache_invalid_missing(void)
514{
515 g_cInvalidates++;
516 kFsCacheInvalidateAll(g_pFsCache);
517}
518
Note: See TracBrowser for help on using the repository browser.