Skip to content

Commit e56ca99

Browse files
committed
Merge pull request #994 from jeffhostetler/jeffhostetler/fscache_nfd
fscache: add not-found directory cache to fscache
2 parents fa9ea34 + edfdcf6 commit e56ca99

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
@@ -177,7 +178,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
177178
* Dir should not contain trailing '/'. Use an empty string for the current
178179
* directory (not "."!).
179180
*/
180-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
181+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
182+
int *dir_not_found)
181183
{
182184
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
183185
WIN32_FIND_DATAW fdata;
@@ -186,6 +188,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
186188
struct fsentry *list, **phead;
187189
DWORD err;
188190

191+
*dir_not_found = 0;
192+
189193
/* convert name to UTF-16 and check length < MAX_PATH */
190194
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
191195
dir->len)) < 0) {
@@ -204,12 +208,17 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
204208
h = FindFirstFileW(pattern, &fdata);
205209
if (h == INVALID_HANDLE_VALUE) {
206210
err = GetLastError();
211+
*dir_not_found = 1; /* or empty directory */
207212
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
213+
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
214+
errno, dir->dirent.d_name);
208215
return NULL;
209216
}
210217

211218
/* allocate object to hold directory listing */
212219
list = fsentry_alloc(NULL, dir->dirent.d_name, dir->len);
220+
list->st_mode = S_IFDIR;
221+
list->dirent.d_type = DT_DIR;
213222

214223
/* walk directory and build linked list of fsentry structures */
215224
phead = &list->next;
@@ -294,12 +303,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
294303
static struct fsentry *fscache_get(struct fsentry *key)
295304
{
296305
struct fsentry *fse, *future, *waiter;
306+
int dir_not_found;
297307

298308
EnterCriticalSection(&mutex);
299309
/* check if entry is in cache */
300310
fse = fscache_get_wait(key);
301311
if (fse) {
302-
fsentry_addref(fse);
312+
if (fse->st_mode)
313+
fsentry_addref(fse);
314+
else
315+
fse = NULL; /* non-existing directory */
303316
LeaveCriticalSection(&mutex);
304317
return fse;
305318
}
@@ -308,7 +321,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
308321
fse = fscache_get_wait(key->list);
309322
if (fse) {
310323
LeaveCriticalSection(&mutex);
311-
/* dir entry without file entry -> file doesn't exist */
324+
/*
325+
* dir entry without file entry, or dir does not
326+
* exist -> file doesn't exist
327+
*/
312328
errno = ENOENT;
313329
return NULL;
314330
}
@@ -322,7 +338,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
322338

323339
/* create the directory listing (outside mutex!) */
324340
LeaveCriticalSection(&mutex);
325-
fse = fsentry_create_list(future);
341+
fse = fsentry_create_list(future, &dir_not_found);
326342
EnterCriticalSection(&mutex);
327343

328344
/* remove future entry and signal waiting threads */
@@ -336,6 +352,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
336352

337353
/* leave on error (errno set by fsentry_create_list) */
338354
if (!fse) {
355+
if (dir_not_found && key->list) {
356+
/*
357+
* Record that the directory does not exist (or is
358+
* empty, which for all practical matters is the same
359+
* thing as far as fscache is concerned).
360+
*/
361+
fse = fsentry_alloc(key->list->list,
362+
key->list->dirent.d_name,
363+
key->list->len);
364+
fse->st_mode = 0;
365+
hashmap_add(&map, &fse->ent);
366+
}
339367
LeaveCriticalSection(&mutex);
340368
return NULL;
341369
}
@@ -347,6 +375,9 @@ static struct fsentry *fscache_get(struct fsentry *key)
347375
if (key->list)
348376
fse = hashmap_get_entry(&map, key, ent, NULL);
349377

378+
if (fse && !fse->st_mode)
379+
fse = NULL; /* non-existing directory */
380+
350381
/* return entry or ENOENT */
351382
if (fse)
352383
fsentry_addref(fse);
@@ -390,6 +421,7 @@ int fscache_enable(int enable)
390421
fscache_clear();
391422
LeaveCriticalSection(&mutex);
392423
}
424+
trace_printf_key(&trace_fscache, "fscache: enable(%d)\n", enable);
393425
return result;
394426
}
395427

t/t1090-sparse-checkout-scope.sh

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

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

0 commit comments

Comments
 (0)