Skip to content

Commit 9e2326c

Browse files
mhaggerpeff
authored andcommitted
longest_ancestor_length(): require prefix list entries to be normalized
Move the responsibility for normalizing prefixes from longest_ancestor_length() to its callers. Use slightly different normalizations at the two callers: In setup_git_directory_gently_1(), use the old normalization, which ignores paths that are not usable. In the next commit we will change this caller to also resolve symlinks in the paths from GIT_CEILING_DIRECTORIES as part of the normalization. In "test-path-utils longest_ancestor_length", use the old normalization, but die() if any paths are unusable. Also change t0060 to only pass normalized paths to the test program (no empty entries or non-absolute paths, strip trailing slashes from the paths, and remove tests that thereby become redundant). The point of this change is to reduce the scope of the ancestor_length tests in t0060 from testing normalization+longest_prefix to testing only mostly longest_prefix. This is necessary because when setup_git_directory_gently_1() starts resolving symlinks as part of its normalization, it will not be reasonable to do the same in the test suite, because that would make the test results depend on the contents of the root directory of the filesystem on which the test is run. HOWEVER: under Windows, bash mangles arguments that look like absolute POSIX paths into DOS paths. So we have to retain the level of normalization done by normalize_path_copy() to convert the bash-mangled DOS paths (which contain backslashes) into paths that use forward slashes. Signed-off-by: Michael Haggerty <[email protected]> Signed-off-by: Jeff King <[email protected]>
1 parent 31171d9 commit 9e2326c

File tree

4 files changed

+91
-44
lines changed

4 files changed

+91
-44
lines changed

path.c

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -570,20 +570,20 @@ int normalize_path_copy(char *dst, const char *src)
570570

571571
/*
572572
* path = Canonical absolute path
573-
* prefixes = string_list containing absolute paths
573+
* prefixes = string_list containing normalized, absolute paths without
574+
* trailing slashes (except for the root directory, which is denoted by "/").
574575
*
575-
* Determines, for each path in prefixes, whether the "prefix" really
576+
* Determines, for each path in prefixes, whether the "prefix"
576577
* is an ancestor directory of path. Returns the length of the longest
577578
* ancestor directory, excluding any trailing slashes, or -1 if no prefix
578579
* is an ancestor. (Note that this means 0 is returned if prefixes is
579580
* ["/"].) "/foo" is not considered an ancestor of "/foobar". Directories
580581
* are not considered to be their own ancestors. path must be in a
581582
* canonical form: empty components, or "." or ".." components are not
582-
* allowed. Empty strings in prefixes are ignored.
583+
* allowed.
583584
*/
584585
int longest_ancestor_length(const char *path, struct string_list *prefixes)
585586
{
586-
char buf[PATH_MAX+1];
587587
int i, max_len = -1;
588588

589589
if (!strcmp(path, "/"))
@@ -593,19 +593,15 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes)
593593
const char *ceil = prefixes->items[i].string;
594594
int len = strlen(ceil);
595595

596-
if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
597-
continue;
598-
if (normalize_path_copy(buf, ceil) < 0)
599-
continue;
600-
len = strlen(buf);
601-
if (len > 0 && buf[len-1] == '/')
602-
buf[--len] = '\0';
596+
if (len == 1 && ceil[0] == '/')
597+
len = 0; /* root matches anything, with length 0 */
598+
else if (!strncmp(path, ceil, len) && path[len] == '/')
599+
; /* match of length len */
600+
else
601+
continue; /* no match */
603602

604-
if (!strncmp(path, buf, len) &&
605-
path[len] == '/' &&
606-
len > max_len) {
603+
if (len > max_len)
607604
max_len = len;
608-
}
609605
}
610606

611607
return max_len;

setup.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,28 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
621621
return buf.st_dev;
622622
}
623623

624+
/*
625+
* A "string_list_each_func_t" function that normalizes an entry from
626+
* GIT_CEILING_DIRECTORIES or discards it if unusable.
627+
*/
628+
static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
629+
{
630+
const char *ceil = item->string;
631+
int len = strlen(ceil);
632+
char buf[PATH_MAX+1];
633+
634+
if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
635+
return 0;
636+
if (normalize_path_copy(buf, ceil) < 0)
637+
return 0;
638+
len = strlen(buf);
639+
if (len > 1 && buf[len-1] == '/')
640+
buf[--len] = '\0';
641+
free(item->string);
642+
item->string = xstrdup(buf);
643+
return 1;
644+
}
645+
624646
/*
625647
* We cannot decide in this function whether we are in the work tree or
626648
* not, since the config can only be read _after_ this function was called.
@@ -659,6 +681,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
659681

660682
if (env_ceiling_dirs) {
661683
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
684+
filter_string_list(&ceiling_dirs, 0, normalize_ceiling_entry, NULL);
662685
ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
663686
string_list_clear(&ceiling_dirs, 0);
664687
}

t/t0060-path-utils.sh

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -93,47 +93,32 @@ norm_path /d1/s1//../s2/../../d2 /d2 POSIX
9393
norm_path /d1/.../d2 /d1/.../d2 POSIX
9494
norm_path /d1/..././../d2 /d1/d2 POSIX
9595

96-
ancestor / "" -1
9796
ancestor / / -1
98-
ancestor /foo "" -1
99-
ancestor /foo : -1
100-
ancestor /foo ::. -1
101-
ancestor /foo ::..:: -1
10297
ancestor /foo / 0
10398
ancestor /foo /fo -1
10499
ancestor /foo /foo -1
105-
ancestor /foo /foo/ -1
106100
ancestor /foo /bar -1
107-
ancestor /foo /bar/ -1
108101
ancestor /foo /foo/bar -1
109-
ancestor /foo /foo:/bar/ -1
110-
ancestor /foo /foo/:/bar/ -1
111-
ancestor /foo /foo::/bar/ -1
112-
ancestor /foo /:/foo:/bar/ 0
113-
ancestor /foo /foo:/:/bar/ 0
114-
ancestor /foo /:/bar/:/foo 0
115-
ancestor /foo/bar "" -1
102+
ancestor /foo /foo:/bar -1
103+
ancestor /foo /:/foo:/bar 0
104+
ancestor /foo /foo:/:/bar 0
105+
ancestor /foo /:/bar:/foo 0
116106
ancestor /foo/bar / 0
117107
ancestor /foo/bar /fo -1
118-
ancestor /foo/bar foo -1
119108
ancestor /foo/bar /foo 4
120-
ancestor /foo/bar /foo/ 4
121109
ancestor /foo/bar /foo/ba -1
122110
ancestor /foo/bar /:/fo 0
123111
ancestor /foo/bar /foo:/foo/ba 4
124112
ancestor /foo/bar /bar -1
125-
ancestor /foo/bar /bar/ -1
126-
ancestor /foo/bar /fo: -1
127-
ancestor /foo/bar :/fo -1
128-
ancestor /foo/bar /foo:/bar/ 4
129-
ancestor /foo/bar /:/foo:/bar/ 4
130-
ancestor /foo/bar /foo:/:/bar/ 4
131-
ancestor /foo/bar /:/bar/:/fo 0
132-
ancestor /foo/bar /:/bar/ 0
133-
ancestor /foo/bar .:/foo/. 4
134-
ancestor /foo/bar .:/foo/.:.: 4
135-
ancestor /foo/bar /foo/./:.:/bar 4
136-
ancestor /foo/bar .:/bar -1
113+
ancestor /foo/bar /fo -1
114+
ancestor /foo/bar /foo:/bar 4
115+
ancestor /foo/bar /:/foo:/bar 4
116+
ancestor /foo/bar /foo:/:/bar 4
117+
ancestor /foo/bar /:/bar:/fo 0
118+
ancestor /foo/bar /:/bar 0
119+
ancestor /foo/bar /foo 4
120+
ancestor /foo/bar /foo:/bar 4
121+
ancestor /foo/bar /bar -1
137122

138123
test_expect_success 'strip_path_suffix' '
139124
test c:/msysgit = $(test-path-utils strip_path_suffix \

test-path-utils.c

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
#include "cache.h"
22
#include "string-list.h"
33

4+
/*
5+
* A "string_list_each_func_t" function that normalizes an entry from
6+
* GIT_CEILING_DIRECTORIES. If the path is unusable for some reason,
7+
* die with an explanation.
8+
*/
9+
static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
10+
{
11+
const char *ceil = item->string;
12+
int len = strlen(ceil);
13+
char buf[PATH_MAX+1];
14+
15+
if (len == 0)
16+
die("Empty path is not supported");
17+
if (len > PATH_MAX)
18+
die("Path \"%s\" is too long", ceil);
19+
if (!is_absolute_path(ceil))
20+
die("Path \"%s\" is not absolute", ceil);
21+
if (normalize_path_copy(buf, ceil) < 0)
22+
die("Path \"%s\" could not be normalized", ceil);
23+
len = strlen(buf);
24+
if (len > 1 && buf[len-1] == '/')
25+
die("Normalized path \"%s\" ended with slash", buf);
26+
free(item->string);
27+
item->string = xstrdup(buf);
28+
return 1;
29+
}
30+
431
int main(int argc, char **argv)
532
{
633
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@@ -33,10 +60,26 @@ int main(int argc, char **argv)
3360
if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
3461
int len;
3562
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
63+
char *path = xstrdup(argv[2]);
3664

65+
/*
66+
* We have to normalize the arguments because under
67+
* Windows, bash mangles arguments that look like
68+
* absolute POSIX paths or colon-separate lists of
69+
* absolute POSIX paths into DOS paths (e.g.,
70+
* "/foo:/foo/bar" might be converted to
71+
* "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"),
72+
* whereas longest_ancestor_length() requires paths
73+
* that use forward slashes.
74+
*/
75+
if (normalize_path_copy(path, path))
76+
die("Path \"%s\" could not be normalized", argv[2]);
3777
string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
38-
len = longest_ancestor_length(argv[2], &ceiling_dirs);
78+
filter_string_list(&ceiling_dirs, 0,
79+
normalize_ceiling_entry, NULL);
80+
len = longest_ancestor_length(path, &ceiling_dirs);
3981
string_list_clear(&ceiling_dirs, 0);
82+
free(path);
4083
printf("%d\n", len);
4184
return 0;
4285
}

0 commit comments

Comments
 (0)