prep_exclude: remove the artificial PATH_MAX limit

This fixes a segfault in git-status with long paths on Windows,
where PATH_MAX is only 260.

This also fixes the problem of silently ignoring .gitignore if the
full path exceeds PATH_MAX. Now add_excludes_from_file() will report
if it gets ENAMETOOLONG.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Nguyễn Thái Ngọc Duy
2014-07-14 11:50:22 +02:00
committed by Junio C Hamano
parent 709359c85c
commit aceb9429b3
2 changed files with 29 additions and 20 deletions

47
dir.c
View File

@ -799,12 +799,12 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
*/ */
while ((stk = dir->exclude_stack) != NULL) { while ((stk = dir->exclude_stack) != NULL) {
if (stk->baselen <= baselen && if (stk->baselen <= baselen &&
!strncmp(dir->basebuf, base, stk->baselen)) !strncmp(dir->basebuf.buf, base, stk->baselen))
break; break;
el = &group->el[dir->exclude_stack->exclude_ix]; el = &group->el[dir->exclude_stack->exclude_ix];
dir->exclude_stack = stk->prev; dir->exclude_stack = stk->prev;
dir->exclude = NULL; dir->exclude = NULL;
free((char *)el->src); /* see strdup() below */ free((char *)el->src); /* see strbuf_detach() below */
clear_exclude_list(el); clear_exclude_list(el);
free(stk); free(stk);
group->nr--; group->nr--;
@ -814,8 +814,17 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
if (dir->exclude) if (dir->exclude)
return; return;
/*
* Lazy initialization. All call sites currently just
* memset(dir, 0, sizeof(*dir)) before use. Changing all of
* them seems lots of work for little benefit.
*/
if (!dir->basebuf.buf)
strbuf_init(&dir->basebuf, PATH_MAX);
/* Read from the parent directories and push them down. */ /* Read from the parent directories and push them down. */
current = stk ? stk->baselen : -1; current = stk ? stk->baselen : -1;
strbuf_setlen(&dir->basebuf, current < 0 ? 0 : current);
while (current < baselen) { while (current < baselen) {
struct exclude_stack *stk = xcalloc(1, sizeof(*stk)); struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
const char *cp; const char *cp;
@ -833,48 +842,47 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
stk->baselen = cp - base; stk->baselen = cp - base;
stk->exclude_ix = group->nr; stk->exclude_ix = group->nr;
el = add_exclude_list(dir, EXC_DIRS, NULL); el = add_exclude_list(dir, EXC_DIRS, NULL);
memcpy(dir->basebuf + current, base + current, strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
stk->baselen - current); assert(stk->baselen == dir->basebuf.len);
/* Abort if the directory is excluded */ /* Abort if the directory is excluded */
if (stk->baselen) { if (stk->baselen) {
int dt = DT_DIR; int dt = DT_DIR;
dir->basebuf[stk->baselen - 1] = 0; dir->basebuf.buf[stk->baselen - 1] = 0;
dir->exclude = last_exclude_matching_from_lists(dir, dir->exclude = last_exclude_matching_from_lists(dir,
dir->basebuf, stk->baselen - 1, dir->basebuf.buf, stk->baselen - 1,
dir->basebuf + current, &dt); dir->basebuf.buf + current, &dt);
dir->basebuf[stk->baselen - 1] = '/'; dir->basebuf.buf[stk->baselen - 1] = '/';
if (dir->exclude && if (dir->exclude &&
dir->exclude->flags & EXC_FLAG_NEGATIVE) dir->exclude->flags & EXC_FLAG_NEGATIVE)
dir->exclude = NULL; dir->exclude = NULL;
if (dir->exclude) { if (dir->exclude) {
dir->basebuf[stk->baselen] = 0;
dir->exclude_stack = stk; dir->exclude_stack = stk;
return; return;
} }
} }
/* Try to read per-directory file unless path is too long */ /* Try to read per-directory file */
if (dir->exclude_per_dir && if (dir->exclude_per_dir) {
stk->baselen + strlen(dir->exclude_per_dir) < PATH_MAX) {
strcpy(dir->basebuf + stk->baselen,
dir->exclude_per_dir);
/* /*
* dir->basebuf gets reused by the traversal, but we * dir->basebuf gets reused by the traversal, but we
* need fname to remain unchanged to ensure the src * need fname to remain unchanged to ensure the src
* member of each struct exclude correctly * member of each struct exclude correctly
* back-references its source file. Other invocations * back-references its source file. Other invocations
* of add_exclude_list provide stable strings, so we * of add_exclude_list provide stable strings, so we
* strdup() and free() here in the caller. * strbuf_detach() and free() here in the caller.
*/ */
el->src = strdup(dir->basebuf); struct strbuf sb = STRBUF_INIT;
add_excludes_from_file_to_list(dir->basebuf, strbuf_addbuf(&sb, &dir->basebuf);
dir->basebuf, stk->baselen, el, 1); strbuf_addstr(&sb, dir->exclude_per_dir);
el->src = strbuf_detach(&sb, NULL);
add_excludes_from_file_to_list(el->src, el->src,
stk->baselen, el, 1);
} }
dir->exclude_stack = stk; dir->exclude_stack = stk;
current = stk->baselen; current = stk->baselen;
} }
dir->basebuf[baselen] = '\0'; strbuf_setlen(&dir->basebuf, baselen);
} }
/* /*
@ -1671,4 +1679,5 @@ void clear_directory(struct dir_struct *dir)
free(stk); free(stk);
stk = prev; stk = prev;
} }
strbuf_release(&dir->basebuf);
} }

2
dir.h
View File

@ -119,7 +119,7 @@ struct dir_struct {
*/ */
struct exclude_stack *exclude_stack; struct exclude_stack *exclude_stack;
struct exclude *exclude; struct exclude *exclude;
char basebuf[PATH_MAX]; struct strbuf basebuf;
}; };
/* /*