Skip to content

Commit a18bf07

Browse files
dschoGit for Windows Build Agent
authored and
Git for Windows Build Agent
committed
Merge pull request #994 from jeffhostetler/jeffhostetler/fscache_nfd
fscache: add not-found directory cache to fscache
2 parents d3614fd + b7f8418 commit a18bf07

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

compat/win32/fscache.c

+37-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
#include "fscache.h"
55
#include "../../dir.h"
66
#include "../../abspath.h"
7+
#include "../../trace.h"
78

89
static int initialized;
910
static volatile long enabled;
1011
static struct hashmap map;
1112
static CRITICAL_SECTION mutex;
13+
static struct trace_key trace_fscache = TRACE_KEY_INIT(FSCACHE);
1214

1315
/*
1416
* An entry in the file system cache. Used for both entire directory listings
@@ -179,7 +181,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
179181
* Dir should not contain trailing '/'. Use an empty string for the current
180182
* directory (not "."!).
181183
*/
182-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
184+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
185+
int *dir_not_found)
183186
{
184187
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
185188
WIN32_FIND_DATAW fdata;
@@ -188,6 +191,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
188191
struct fsentry *list, **phead;
189192
DWORD err;
190193

194+
*dir_not_found = 0;
195+
191196
/* convert name to UTF-16 and check length < MAX_PATH */
192197
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
193198
dir->len)) < 0) {
@@ -206,12 +211,17 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
206211
h = FindFirstFileW(pattern, &fdata);
207212
if (h == INVALID_HANDLE_VALUE) {
208213
err = GetLastError();
214+
*dir_not_found = 1; /* or empty directory */
209215
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
216+
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
217+
errno, dir->dirent.d_name);
210218
return NULL;
211219
}
212220

213221
/* allocate object to hold directory listing */
214222
list = fsentry_alloc(NULL, dir->dirent.d_name, dir->len);
223+
list->st_mode = S_IFDIR;
224+
list->dirent.d_type = DT_DIR;
215225

216226
/* walk directory and build linked list of fsentry structures */
217227
phead = &list->next;
@@ -296,12 +306,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
296306
static struct fsentry *fscache_get(struct fsentry *key)
297307
{
298308
struct fsentry *fse, *future, *waiter;
309+
int dir_not_found;
299310

300311
EnterCriticalSection(&mutex);
301312
/* check if entry is in cache */
302313
fse = fscache_get_wait(key);
303314
if (fse) {
304-
fsentry_addref(fse);
315+
if (fse->st_mode)
316+
fsentry_addref(fse);
317+
else
318+
fse = NULL; /* non-existing directory */
305319
LeaveCriticalSection(&mutex);
306320
return fse;
307321
}
@@ -310,7 +324,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
310324
fse = fscache_get_wait(key->list);
311325
if (fse) {
312326
LeaveCriticalSection(&mutex);
313-
/* dir entry without file entry -> file doesn't exist */
327+
/*
328+
* dir entry without file entry, or dir does not
329+
* exist -> file doesn't exist
330+
*/
314331
errno = ENOENT;
315332
return NULL;
316333
}
@@ -324,7 +341,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
324341

325342
/* create the directory listing (outside mutex!) */
326343
LeaveCriticalSection(&mutex);
327-
fse = fsentry_create_list(future);
344+
fse = fsentry_create_list(future, &dir_not_found);
328345
EnterCriticalSection(&mutex);
329346

330347
/* remove future entry and signal waiting threads */
@@ -338,6 +355,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
338355

339356
/* leave on error (errno set by fsentry_create_list) */
340357
if (!fse) {
358+
if (dir_not_found && key->list) {
359+
/*
360+
* Record that the directory does not exist (or is
361+
* empty, which for all practical matters is the same
362+
* thing as far as fscache is concerned).
363+
*/
364+
fse = fsentry_alloc(key->list->list,
365+
key->list->dirent.d_name,
366+
key->list->len);
367+
fse->st_mode = 0;
368+
hashmap_add(&map, &fse->ent);
369+
}
341370
LeaveCriticalSection(&mutex);
342371
return NULL;
343372
}
@@ -349,6 +378,9 @@ static struct fsentry *fscache_get(struct fsentry *key)
349378
if (key->list)
350379
fse = hashmap_get_entry(&map, key, ent, NULL);
351380

381+
if (fse && !fse->st_mode)
382+
fse = NULL; /* non-existing directory */
383+
352384
/* return entry or ENOENT */
353385
if (fse)
354386
fsentry_addref(fse);
@@ -392,6 +424,7 @@ int fscache_enable(int enable)
392424
fscache_clear();
393425
LeaveCriticalSection(&mutex);
394426
}
427+
trace_printf_key(&trace_fscache, "fscache: enable(%d)\n", enable);
395428
return result;
396429
}
397430

t/t1090-sparse-checkout-scope.sh

+20
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,24 @@ test_expect_success 'in partial clone, sparse checkout only fetches needed blobs
106106
test_cmp expect actual
107107
'
108108

109+
test_expect_success MINGW 'no unnecessary opendir() with fscache' '
110+
git clone . fscache-test &&
111+
(
112+
cd fscache-test &&
113+
git config core.fscache 1 &&
114+
echo "/excluded/*" >.git/info/sparse-checkout &&
115+
for f in $(test_seq 10)
116+
do
117+
sha1=$(echo $f | git hash-object -w --stdin) &&
118+
git update-index --add \
119+
--cacheinfo 100644,$sha1,excluded/$f || exit 1
120+
done &&
121+
test_tick &&
122+
git commit -m excluded &&
123+
GIT_TRACE_FSCACHE=1 git status >out 2>err &&
124+
grep excluded err >grep.out &&
125+
test_line_count = 1 grep.out
126+
)
127+
'
128+
109129
test_done

0 commit comments

Comments
 (0)