From: Nguyễn Thái Ngọc Duy Date: Tue, 15 Jan 2013 13:35:24 +0000 (+0700) Subject: attr: fix off-by-one directory component length calculation X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=711536bd4ba791adfd506583927a8f6c8f821e24;p=git.git attr: fix off-by-one directory component length calculation 94bc671 (Add directory pattern matching to attributes - 2012-12-08) uses find_basename() to calculate the length of directory part in prepare_attr_stack. This function expects the directory without the trailing slash (as "origin" field in match_attr struct is without the trailing slash). find_basename() includes the trailing slash and confuses push/pop algorithm. Consider path = "abc/def" and the push down code: while (1) { len = strlen(attr_stack->origin); if (dirlen <= len) break; cp = memchr(path + len + 1, '/', dirlen - len - 1); if (!cp) cp = path + dirlen; dirlen is 4, not 3, without this patch. So when attr_stack->origin is "abc", it'll miss the exit condition because 4 <= 3 is wrong. It'll then try to push "abc/" down the attr stack (because "cp" would be NULL). So we have both "abc" and "abc/" in the stack. Next time when "abc/ghi" is checked, "abc/" is popped out because of the off-by-one dirlen, only to be pushed back in again by the above code. This repeats for all files in the same directory. Which means at least one failed open syscall per file, or more if .gitattributes exists. This is the perf result with 10 runs on git.git: Test 94bc671^ 94bc671 HEAD ---------------------------------------------------------------------------------------------------------- 7810.1: grep worktree, cheap regex 0.02(0.01+0.04) 0.05(0.03+0.05) +150.0% 0.02(0.01+0.04) +0.0% 7810.2: grep worktree, expensive regex 0.25(0.94+0.01) 0.26(0.94+0.02) +4.0% 0.25(0.93+0.02) +0.0% 7810.3: grep --cached, cheap regex 0.11(0.10+0.00) 0.12(0.10+0.02) +9.1% 0.10(0.10+0.00) -9.1% 7810.4: grep --cached, expensive regex 0.61(0.60+0.01) 0.62(0.61+0.01) +1.6% 0.61(0.60+0.00) +0.0% Reported-by: Ross Lagerwall Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- diff --git a/attr.c b/attr.c index 466c93fa5..bb9a470c8 100644 --- a/attr.c +++ b/attr.c @@ -583,6 +583,13 @@ static void prepare_attr_stack(const char *path) dirlen = find_basename(path) - path; + /* + * find_basename() includes the trailing slash, but we do + * _not_ want it. + */ + if (dirlen) + dirlen--; + /* * At the bottom of the attribute stack is the built-in * set of attribute definitions, followed by the contents