Skip to content

Commit a829866

Browse files
committed
Merge pull request #994 from jeffhostetler/jeffhostetler/fscache_nfd
fscache: add not-found directory cache to fscache
2 parents 73a6a8b + 9fb151e commit a829866

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

compat/win32/fscache.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ static int initialized;
77
static volatile long enabled;
88
static struct hashmap map;
99
static CRITICAL_SECTION mutex;
10+
static struct trace_key trace_fscache = TRACE_KEY_INIT(FSCACHE);
1011

1112
/*
1213
* An entry in the file system cache. Used for both entire directory listings
@@ -172,7 +173,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
172173
* Dir should not contain trailing '/'. Use an empty string for the current
173174
* directory (not "."!).
174175
*/
175-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
176+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
177+
int *dir_not_found)
176178
{
177179
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
178180
WIN32_FIND_DATAW fdata;
@@ -181,6 +183,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
181183
struct fsentry *list, **phead;
182184
DWORD err;
183185

186+
*dir_not_found = 0;
187+
184188
/* convert name to UTF-16 and check length < MAX_PATH */
185189
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
186190
dir->len)) < 0) {
@@ -199,12 +203,17 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
199203
h = FindFirstFileW(pattern, &fdata);
200204
if (h == INVALID_HANDLE_VALUE) {
201205
err = GetLastError();
206+
*dir_not_found = 1; /* or empty directory */
202207
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
208+
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
209+
errno, dir->dirent.d_name);
203210
return NULL;
204211
}
205212

206213
/* allocate object to hold directory listing */
207214
list = fsentry_alloc(NULL, dir->dirent.d_name, dir->len);
215+
list->st_mode = S_IFDIR;
216+
list->dirent.d_type = DT_DIR;
208217

209218
/* walk directory and build linked list of fsentry structures */
210219
phead = &list->next;
@@ -289,12 +298,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
289298
static struct fsentry *fscache_get(struct fsentry *key)
290299
{
291300
struct fsentry *fse, *future, *waiter;
301+
int dir_not_found;
292302

293303
EnterCriticalSection(&mutex);
294304
/* check if entry is in cache */
295305
fse = fscache_get_wait(key);
296306
if (fse) {
297-
fsentry_addref(fse);
307+
if (fse->st_mode)
308+
fsentry_addref(fse);
309+
else
310+
fse = NULL; /* non-existing directory */
298311
LeaveCriticalSection(&mutex);
299312
return fse;
300313
}
@@ -303,7 +316,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
303316
fse = fscache_get_wait(key->list);
304317
if (fse) {
305318
LeaveCriticalSection(&mutex);
306-
/* dir entry without file entry -> file doesn't exist */
319+
/*
320+
* dir entry without file entry, or dir does not
321+
* exist -> file doesn't exist
322+
*/
307323
errno = ENOENT;
308324
return NULL;
309325
}
@@ -317,7 +333,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
317333

318334
/* create the directory listing (outside mutex!) */
319335
LeaveCriticalSection(&mutex);
320-
fse = fsentry_create_list(future);
336+
fse = fsentry_create_list(future, &dir_not_found);
321337
EnterCriticalSection(&mutex);
322338

323339
/* remove future entry and signal waiting threads */
@@ -331,6 +347,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
331347

332348
/* leave on error (errno set by fsentry_create_list) */
333349
if (!fse) {
350+
if (dir_not_found && key->list) {
351+
/*
352+
* Record that the directory does not exist (or is
353+
* empty, which for all practical matters is the same
354+
* thing as far as fscache is concerned).
355+
*/
356+
fse = fsentry_alloc(key->list->list,
357+
key->list->dirent.d_name,
358+
key->list->len);
359+
fse->st_mode = 0;
360+
hashmap_add(&map, &fse->ent);
361+
}
334362
LeaveCriticalSection(&mutex);
335363
return NULL;
336364
}
@@ -342,6 +370,9 @@ static struct fsentry *fscache_get(struct fsentry *key)
342370
if (key->list)
343371
fse = hashmap_get_entry(&map, key, ent, NULL);
344372

373+
if (fse && !fse->st_mode)
374+
fse = NULL; /* non-existing directory */
375+
345376
/* return entry or ENOENT */
346377
if (fse)
347378
fsentry_addref(fse);
@@ -385,6 +416,7 @@ int fscache_enable(int enable)
385416
fscache_clear();
386417
LeaveCriticalSection(&mutex);
387418
}
419+
trace_printf_key(&trace_fscache, "fscache: enable(%d)\n", enable);
388420
return result;
389421
}
390422

t/t1090-sparse-checkout-scope.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,24 @@ test_expect_success 'in partial clone, sparse checkout only fetches needed blobs
8181
test_cmp expect actual
8282
'
8383

84+
test_expect_success MINGW 'no unnecessary opendir() with fscache' '
85+
git clone . fscache-test &&
86+
(
87+
cd fscache-test &&
88+
git config core.fscache 1 &&
89+
echo "/excluded/*" >.git/info/sparse-checkout &&
90+
for f in $(test_seq 10)
91+
do
92+
sha1=$(echo $f | git hash-object -w --stdin) &&
93+
git update-index --add \
94+
--cacheinfo 100644,$sha1,excluded/$f || break
95+
done &&
96+
test_tick &&
97+
git commit -m excluded &&
98+
GIT_TRACE_FSCACHE=1 git status >out 2>err &&
99+
grep excluded err >grep.out &&
100+
test_line_count = 1 grep.out
101+
)
102+
'
103+
84104
test_done

0 commit comments

Comments
 (0)