Skip to content

Commit 4d5c31c

Browse files
committed
Merge pull request #994 from jeffhostetler/jeffhostetler/fscache_nfd
fscache: add not-found directory cache to fscache
2 parents 1437d3f + ef9f9fa commit 4d5c31c

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
@@ -8,6 +8,7 @@ static int initialized;
88
static volatile long enabled;
99
static struct hashmap map;
1010
static CRITICAL_SECTION mutex;
11+
static struct trace_key trace_fscache = TRACE_KEY_INIT(FSCACHE);
1112

1213
/*
1314
* An entry in the file system cache. Used for both entire directory listings
@@ -178,7 +179,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
178179
* Dir should not contain trailing '/'. Use an empty string for the current
179180
* directory (not "."!).
180181
*/
181-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
182+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
183+
int *dir_not_found)
182184
{
183185
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
184186
WIN32_FIND_DATAW fdata;
@@ -187,6 +189,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
187189
struct fsentry *list, **phead;
188190
DWORD err;
189191

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

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

215224
/* walk directory and build linked list of fsentry structures */
216225
phead = &list->next;
@@ -295,12 +304,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
295304
static struct fsentry *fscache_get(struct fsentry *key)
296305
{
297306
struct fsentry *fse, *future, *waiter;
307+
int dir_not_found;
298308

299309
EnterCriticalSection(&mutex);
300310
/* check if entry is in cache */
301311
fse = fscache_get_wait(key);
302312
if (fse) {
303-
fsentry_addref(fse);
313+
if (fse->st_mode)
314+
fsentry_addref(fse);
315+
else
316+
fse = NULL; /* non-existing directory */
304317
LeaveCriticalSection(&mutex);
305318
return fse;
306319
}
@@ -309,7 +322,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
309322
fse = fscache_get_wait(key->list);
310323
if (fse) {
311324
LeaveCriticalSection(&mutex);
312-
/* dir entry without file entry -> file doesn't exist */
325+
/*
326+
* dir entry without file entry, or dir does not
327+
* exist -> file doesn't exist
328+
*/
313329
errno = ENOENT;
314330
return NULL;
315331
}
@@ -323,7 +339,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
323339

324340
/* create the directory listing (outside mutex!) */
325341
LeaveCriticalSection(&mutex);
326-
fse = fsentry_create_list(future);
342+
fse = fsentry_create_list(future, &dir_not_found);
327343
EnterCriticalSection(&mutex);
328344

329345
/* remove future entry and signal waiting threads */
@@ -337,6 +353,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
337353

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

379+
if (fse && !fse->st_mode)
380+
fse = NULL; /* non-existing directory */
381+
351382
/* return entry or ENOENT */
352383
if (fse)
353384
fsentry_addref(fse);
@@ -391,6 +422,7 @@ int fscache_enable(int enable)
391422
fscache_clear();
392423
LeaveCriticalSection(&mutex);
393424
}
425+
trace_printf_key(&trace_fscache, "fscache: enable(%d)\n", enable);
394426
return result;
395427
}
396428

t/t1090-sparse-checkout-scope.sh

Lines changed: 20 additions & 0 deletions
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)