1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-06-01 19:26:10 +02:00

Merge branch 'mh/ceiling' into maint

An element on GIT_CEILING_DIRECTORIES list that does not name the
real path to a directory (i.e. a symbolic link) could have caused
the GIT_DIR discovery logic to escape the ceiling.

* mh/ceiling:
  string_list_longest_prefix(): remove function
  setup_git_directory_gently_1(): resolve symlinks in ceiling paths
  longest_ancestor_length(): require prefix list entries to be normalized
  longest_ancestor_length(): take a string_list argument for prefixes
  longest_ancestor_length(): use string_list_split()
  Introduce new function real_path_if_valid()
  real_path_internal(): add comment explaining use of cwd
  Introduce new static function real_path_internal()
This commit is contained in:
Junio C Hamano 2013-01-28 11:07:18 -08:00
commit ce956fc48e
11 changed files with 202 additions and 165 deletions

View File

@ -82,14 +82,6 @@ Functions
call free() on the util members of any items that have to be call free() on the util members of any items that have to be
deleted. Preserve the order of the items that are retained. deleted. Preserve the order of the items that are retained.
`string_list_longest_prefix`::
Return the longest string within a string_list that is a
prefix (in the sense of prefixcmp()) of the specified string,
or NULL if no such prefix exists. This function does not
require the string_list to be sorted (it does a linear
search).
`print_string_list`:: `print_string_list`::
Dump a string_list to stdout, useful mainly for debugging purposes. It Dump a string_list to stdout, useful mainly for debugging purposes. It

105
abspath.c
View File

@ -15,16 +15,34 @@ int is_directory(const char *path)
#define MAXDEPTH 5 #define MAXDEPTH 5
/* /*
* Use this to get the real path, i.e. resolve links. If you want an * Return the real path (i.e., absolute path, with symlinks resolved
* absolute path but don't mind links, use absolute_path. * and extra slashes removed) equivalent to the specified path. (If
* you want an absolute path but don't mind links, use
* absolute_path().) The return value is a pointer to a static
* buffer.
*
* The input and all intermediate paths must be shorter than MAX_PATH.
* The directory part of path (i.e., everything up to the last
* dir_sep) must denote a valid, existing directory, but the last
* component need not exist. If die_on_error is set, then die with an
* informative error message if there is a problem. Otherwise, return
* NULL on errors (without generating any output).
* *
* If path is our buffer, then return path, as it's already what the * If path is our buffer, then return path, as it's already what the
* user wants. * user wants.
*/ */
const char *real_path(const char *path) static const char *real_path_internal(const char *path, int die_on_error)
{ {
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
char *retval = NULL;
/*
* If we have to temporarily chdir(), store the original CWD
* here so that we can chdir() back to it at the end of the
* function:
*/
char cwd[1024] = ""; char cwd[1024] = "";
int buf_index = 1; int buf_index = 1;
int depth = MAXDEPTH; int depth = MAXDEPTH;
@ -35,11 +53,19 @@ const char *real_path(const char *path)
if (path == buf || path == next_buf) if (path == buf || path == next_buf)
return path; return path;
if (!*path) if (!*path) {
die("The empty string is not a valid path"); if (die_on_error)
die("The empty string is not a valid path");
else
goto error_out;
}
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) {
die ("Too long path: %.*s", 60, path); if (die_on_error)
die("Too long path: %.*s", 60, path);
else
goto error_out;
}
while (depth--) { while (depth--) {
if (!is_directory(buf)) { if (!is_directory(buf)) {
@ -54,20 +80,36 @@ const char *real_path(const char *path)
} }
if (*buf) { if (*buf) {
if (!*cwd && !getcwd(cwd, sizeof(cwd))) if (!*cwd && !getcwd(cwd, sizeof(cwd))) {
die_errno ("Could not get current working directory"); if (die_on_error)
die_errno("Could not get current working directory");
else
goto error_out;
}
if (chdir(buf)) if (chdir(buf)) {
die_errno ("Could not switch to '%s'", buf); if (die_on_error)
die_errno("Could not switch to '%s'", buf);
else
goto error_out;
}
}
if (!getcwd(buf, PATH_MAX)) {
if (die_on_error)
die_errno("Could not get current working directory");
else
goto error_out;
} }
if (!getcwd(buf, PATH_MAX))
die_errno ("Could not get current working directory");
if (last_elem) { if (last_elem) {
size_t len = strlen(buf); size_t len = strlen(buf);
if (len + strlen(last_elem) + 2 > PATH_MAX) if (len + strlen(last_elem) + 2 > PATH_MAX) {
die ("Too long path name: '%s/%s'", if (die_on_error)
buf, last_elem); die("Too long path name: '%s/%s'",
buf, last_elem);
else
goto error_out;
}
if (len && !is_dir_sep(buf[len-1])) if (len && !is_dir_sep(buf[len-1]))
buf[len++] = '/'; buf[len++] = '/';
strcpy(buf + len, last_elem); strcpy(buf + len, last_elem);
@ -77,10 +119,18 @@ const char *real_path(const char *path)
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
ssize_t len = readlink(buf, next_buf, PATH_MAX); ssize_t len = readlink(buf, next_buf, PATH_MAX);
if (len < 0) if (len < 0) {
die_errno ("Invalid symlink '%s'", buf); if (die_on_error)
if (PATH_MAX <= len) die_errno("Invalid symlink '%s'", buf);
die("symbolic link too long: %s", buf); else
goto error_out;
}
if (PATH_MAX <= len) {
if (die_on_error)
die("symbolic link too long: %s", buf);
else
goto error_out;
}
next_buf[len] = '\0'; next_buf[len] = '\0';
buf = next_buf; buf = next_buf;
buf_index = 1 - buf_index; buf_index = 1 - buf_index;
@ -89,10 +139,23 @@ const char *real_path(const char *path)
break; break;
} }
retval = buf;
error_out:
free(last_elem);
if (*cwd && chdir(cwd)) if (*cwd && chdir(cwd))
die_errno ("Could not change back to '%s'", cwd); die_errno ("Could not change back to '%s'", cwd);
return buf; return retval;
}
const char *real_path(const char *path)
{
return real_path_internal(path, 1);
}
const char *real_path_if_valid(const char *path)
{
return real_path_internal(path, 0);
} }
static const char *get_pwd_cwd(void) static const char *get_pwd_cwd(void)

View File

@ -714,10 +714,11 @@ static inline int is_absolute_path(const char *path)
} }
int is_directory(const char *); int is_directory(const char *);
const char *real_path(const char *path); const char *real_path(const char *path);
const char *real_path_if_valid(const char *path);
const char *absolute_path(const char *path); const char *absolute_path(const char *path);
const char *relative_path(const char *abs, const char *base); const char *relative_path(const char *abs, const char *base);
int normalize_path_copy(char *dst, const char *src); int normalize_path_copy(char *dst, const char *src);
int longest_ancestor_length(const char *path, const char *prefix_list); int longest_ancestor_length(const char *path, struct string_list *prefixes);
char *strip_path_suffix(const char *path, const char *suffix); char *strip_path_suffix(const char *path, const char *suffix);
int daemon_avoid_alias(const char *path); int daemon_avoid_alias(const char *path);
int offset_1st_component(const char *path); int offset_1st_component(const char *path);

46
path.c
View File

@ -12,6 +12,7 @@
*/ */
#include "cache.h" #include "cache.h"
#include "strbuf.h" #include "strbuf.h"
#include "string-list.h"
static char bad_path[] = "/bad-path/"; static char bad_path[] = "/bad-path/";
@ -569,43 +570,38 @@ int normalize_path_copy(char *dst, const char *src)
/* /*
* path = Canonical absolute path * path = Canonical absolute path
* prefix_list = Colon-separated list of absolute paths * prefixes = string_list containing normalized, absolute paths without
* trailing slashes (except for the root directory, which is denoted by "/").
* *
* Determines, for each path in prefix_list, whether the "prefix" really * Determines, for each path in prefixes, whether the "prefix"
* is an ancestor directory of path. Returns the length of the longest * is an ancestor directory of path. Returns the length of the longest
* ancestor directory, excluding any trailing slashes, or -1 if no prefix * ancestor directory, excluding any trailing slashes, or -1 if no prefix
* is an ancestor. (Note that this means 0 is returned if prefix_list is * is an ancestor. (Note that this means 0 is returned if prefixes is
* "/".) "/foo" is not considered an ancestor of "/foobar". Directories * ["/"].) "/foo" is not considered an ancestor of "/foobar". Directories
* are not considered to be their own ancestors. path must be in a * are not considered to be their own ancestors. path must be in a
* canonical form: empty components, or "." or ".." components are not * canonical form: empty components, or "." or ".." components are not
* allowed. prefix_list may be null, which is like "". * allowed.
*/ */
int longest_ancestor_length(const char *path, const char *prefix_list) int longest_ancestor_length(const char *path, struct string_list *prefixes)
{ {
char buf[PATH_MAX+1]; int i, max_len = -1;
const char *ceil, *colon;
int len, max_len = -1;
if (prefix_list == NULL || !strcmp(path, "/")) if (!strcmp(path, "/"))
return -1; return -1;
for (colon = ceil = prefix_list; *colon; ceil = colon+1) { for (i = 0; i < prefixes->nr; i++) {
for (colon = ceil; *colon && *colon != PATH_SEP; colon++); const char *ceil = prefixes->items[i].string;
len = colon - ceil; int len = strlen(ceil);
if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
continue;
strlcpy(buf, ceil, len+1);
if (normalize_path_copy(buf, buf) < 0)
continue;
len = strlen(buf);
if (len > 0 && buf[len-1] == '/')
buf[--len] = '\0';
if (!strncmp(path, buf, len) && if (len == 1 && ceil[0] == '/')
path[len] == '/' && len = 0; /* root matches anything, with length 0 */
len > max_len) { else if (!strncmp(path, ceil, len) && path[len] == '/')
; /* match of length len */
else
continue; /* no match */
if (len > max_len)
max_len = len; max_len = len;
}
} }
return max_len; return max_len;

34
setup.c
View File

@ -1,5 +1,6 @@
#include "cache.h" #include "cache.h"
#include "dir.h" #include "dir.h"
#include "string-list.h"
static int inside_git_dir = -1; static int inside_git_dir = -1;
static int inside_work_tree = -1; static int inside_work_tree = -1;
@ -620,6 +621,27 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
return buf.st_dev; return buf.st_dev;
} }
/*
* A "string_list_each_func_t" function that canonicalizes an entry
* from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or
* discards it if unusable.
*/
static int canonicalize_ceiling_entry(struct string_list_item *item,
void *unused)
{
char *ceil = item->string;
const char *real_path;
if (!*ceil || !is_absolute_path(ceil))
return 0;
real_path = real_path_if_valid(ceil);
if (!real_path)
return 0;
free(item->string);
item->string = xstrdup(real_path);
return 1;
}
/* /*
* We cannot decide in this function whether we are in the work tree or * We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called. * not, since the config can only be read _after_ this function was called.
@ -627,10 +649,11 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
static const char *setup_git_directory_gently_1(int *nongit_ok) static const char *setup_git_directory_gently_1(int *nongit_ok)
{ {
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
static char cwd[PATH_MAX+1]; static char cwd[PATH_MAX+1];
const char *gitdirenv, *ret; const char *gitdirenv, *ret;
char *gitfile; char *gitfile;
int len, offset, offset_parent, ceil_offset; int len, offset, offset_parent, ceil_offset = -1;
dev_t current_device = 0; dev_t current_device = 0;
int one_filesystem = 1; int one_filesystem = 1;
@ -655,7 +678,14 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
if (gitdirenv) if (gitdirenv)
return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok); return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok);
ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs); if (env_ceiling_dirs) {
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
filter_string_list(&ceiling_dirs, 0,
canonicalize_ceiling_entry, NULL);
ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
string_list_clear(&ceiling_dirs, 0);
}
if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
ceil_offset = 1; ceil_offset = 1;

View File

@ -145,26 +145,6 @@ void string_list_remove_empty_items(struct string_list *list, int free_util) {
filter_string_list(list, free_util, item_is_not_empty, NULL); filter_string_list(list, free_util, item_is_not_empty, NULL);
} }
char *string_list_longest_prefix(const struct string_list *prefixes,
const char *string)
{
int i, max_len = -1;
char *retval = NULL;
for (i = 0; i < prefixes->nr; i++) {
char *prefix = prefixes->items[i].string;
if (!prefixcmp(string, prefix)) {
int len = strlen(prefix);
if (len > max_len) {
retval = prefix;
max_len = len;
}
}
}
return retval;
}
void string_list_clear(struct string_list *list, int free_util) void string_list_clear(struct string_list *list, int free_util)
{ {
if (list->items) { if (list->items) {

View File

@ -45,15 +45,6 @@ void filter_string_list(struct string_list *list, int free_util,
*/ */
void string_list_remove_empty_items(struct string_list *list, int free_util); void string_list_remove_empty_items(struct string_list *list, int free_util);
/*
* Return the longest string in prefixes that is a prefix (in the
* sense of prefixcmp()) of string, or NULL if no such prefix exists.
* This function does not require the string_list to be sorted (it
* does a linear search).
*/
char *string_list_longest_prefix(const struct string_list *prefixes, const char *string);
/* Use these functions only on sorted lists: */ /* Use these functions only on sorted lists: */
int string_list_has_string(const struct string_list *list, const char *string); int string_list_has_string(const struct string_list *list, const char *string);
int string_list_find_insert_index(const struct string_list *list, const char *string, int string_list_find_insert_index(const struct string_list *list, const char *string,

View File

@ -93,47 +93,32 @@ norm_path /d1/s1//../s2/../../d2 /d2 POSIX
norm_path /d1/.../d2 /d1/.../d2 POSIX norm_path /d1/.../d2 /d1/.../d2 POSIX
norm_path /d1/..././../d2 /d1/d2 POSIX norm_path /d1/..././../d2 /d1/d2 POSIX
ancestor / "" -1
ancestor / / -1 ancestor / / -1
ancestor /foo "" -1
ancestor /foo : -1
ancestor /foo ::. -1
ancestor /foo ::..:: -1
ancestor /foo / 0 ancestor /foo / 0
ancestor /foo /fo -1 ancestor /foo /fo -1
ancestor /foo /foo -1 ancestor /foo /foo -1
ancestor /foo /foo/ -1
ancestor /foo /bar -1 ancestor /foo /bar -1
ancestor /foo /bar/ -1
ancestor /foo /foo/bar -1 ancestor /foo /foo/bar -1
ancestor /foo /foo:/bar/ -1 ancestor /foo /foo:/bar -1
ancestor /foo /foo/:/bar/ -1 ancestor /foo /:/foo:/bar 0
ancestor /foo /foo::/bar/ -1 ancestor /foo /foo:/:/bar 0
ancestor /foo /:/foo:/bar/ 0 ancestor /foo /:/bar:/foo 0
ancestor /foo /foo:/:/bar/ 0
ancestor /foo /:/bar/:/foo 0
ancestor /foo/bar "" -1
ancestor /foo/bar / 0 ancestor /foo/bar / 0
ancestor /foo/bar /fo -1 ancestor /foo/bar /fo -1
ancestor /foo/bar foo -1
ancestor /foo/bar /foo 4 ancestor /foo/bar /foo 4
ancestor /foo/bar /foo/ 4
ancestor /foo/bar /foo/ba -1 ancestor /foo/bar /foo/ba -1
ancestor /foo/bar /:/fo 0 ancestor /foo/bar /:/fo 0
ancestor /foo/bar /foo:/foo/ba 4 ancestor /foo/bar /foo:/foo/ba 4
ancestor /foo/bar /bar -1 ancestor /foo/bar /bar -1
ancestor /foo/bar /bar/ -1 ancestor /foo/bar /fo -1
ancestor /foo/bar /fo: -1 ancestor /foo/bar /foo:/bar 4
ancestor /foo/bar :/fo -1 ancestor /foo/bar /:/foo:/bar 4
ancestor /foo/bar /foo:/bar/ 4 ancestor /foo/bar /foo:/:/bar 4
ancestor /foo/bar /:/foo:/bar/ 4 ancestor /foo/bar /:/bar:/fo 0
ancestor /foo/bar /foo:/:/bar/ 4 ancestor /foo/bar /:/bar 0
ancestor /foo/bar /:/bar/:/fo 0 ancestor /foo/bar /foo 4
ancestor /foo/bar /:/bar/ 0 ancestor /foo/bar /foo:/bar 4
ancestor /foo/bar .:/foo/. 4 ancestor /foo/bar /bar -1
ancestor /foo/bar .:/foo/.:.: 4
ancestor /foo/bar /foo/./:.:/bar 4
ancestor /foo/bar .:/bar -1
test_expect_success 'strip_path_suffix' ' test_expect_success 'strip_path_suffix' '
test c:/msysgit = $(test-path-utils strip_path_suffix \ test c:/msysgit = $(test-path-utils strip_path_suffix \

View File

@ -17,14 +17,6 @@ test_split () {
" "
} }
test_longest_prefix () {
test "$(test-string-list longest_prefix "$1" "$2")" = "$3"
}
test_no_longest_prefix () {
test_must_fail test-string-list longest_prefix "$1" "$2"
}
test_split "foo:bar:baz" ":" "-1" <<EOF test_split "foo:bar:baz" ":" "-1" <<EOF
3 3
[0]: "foo" [0]: "foo"
@ -96,26 +88,4 @@ test_expect_success "test remove_duplicates" '
test a:b:c = "$(test-string-list remove_duplicates a:a:a:b:b:b:c:c:c)" test a:b:c = "$(test-string-list remove_duplicates a:a:a:b:b:b:c:c:c)"
' '
test_expect_success "test longest_prefix" '
test_no_longest_prefix - '' &&
test_no_longest_prefix - x &&
test_longest_prefix "" x "" &&
test_longest_prefix x x x &&
test_longest_prefix "" foo "" &&
test_longest_prefix : foo "" &&
test_longest_prefix f foo f &&
test_longest_prefix foo foobar foo &&
test_longest_prefix foo foo foo &&
test_no_longest_prefix bar foo &&
test_no_longest_prefix bar:bar foo &&
test_no_longest_prefix foobar foo &&
test_longest_prefix foo:bar foo foo &&
test_longest_prefix foo:bar bar bar &&
test_longest_prefix foo::bar foo foo &&
test_longest_prefix foo:foobar foo foo &&
test_longest_prefix foobar:foo foo foo &&
test_longest_prefix foo: bar "" &&
test_longest_prefix :foo bar ""
'
test_done test_done

View File

@ -1,4 +1,32 @@
#include "cache.h" #include "cache.h"
#include "string-list.h"
/*
* A "string_list_each_func_t" function that normalizes an entry from
* GIT_CEILING_DIRECTORIES. If the path is unusable for some reason,
* die with an explanation.
*/
static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
{
const char *ceil = item->string;
int len = strlen(ceil);
char buf[PATH_MAX+1];
if (len == 0)
die("Empty path is not supported");
if (len > PATH_MAX)
die("Path \"%s\" is too long", ceil);
if (!is_absolute_path(ceil))
die("Path \"%s\" is not absolute", ceil);
if (normalize_path_copy(buf, ceil) < 0)
die("Path \"%s\" could not be normalized", ceil);
len = strlen(buf);
if (len > 1 && buf[len-1] == '/')
die("Normalized path \"%s\" ended with slash", buf);
free(item->string);
item->string = xstrdup(buf);
return 1;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -30,7 +58,28 @@ int main(int argc, char **argv)
} }
if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
int len = longest_ancestor_length(argv[2], argv[3]); int len;
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
char *path = xstrdup(argv[2]);
/*
* We have to normalize the arguments because under
* Windows, bash mangles arguments that look like
* absolute POSIX paths or colon-separate lists of
* absolute POSIX paths into DOS paths (e.g.,
* "/foo:/foo/bar" might be converted to
* "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"),
* whereas longest_ancestor_length() requires paths
* that use forward slashes.
*/
if (normalize_path_copy(path, path))
die("Path \"%s\" could not be normalized", argv[2]);
string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
filter_string_list(&ceiling_dirs, 0,
normalize_ceiling_entry, NULL);
len = longest_ancestor_length(path, &ceiling_dirs);
string_list_clear(&ceiling_dirs, 0);
free(path);
printf("%d\n", len); printf("%d\n", len);
return 0; return 0;
} }

View File

@ -97,26 +97,6 @@ int main(int argc, char **argv)
return 0; return 0;
} }
if (argc == 4 && !strcmp(argv[1], "longest_prefix")) {
/* arguments: <colon-separated-prefixes>|- <string> */
struct string_list prefixes = STRING_LIST_INIT_DUP;
int retval;
const char *prefix_string = argv[2];
const char *string = argv[3];
const char *match;
parse_string_list(&prefixes, prefix_string);
match = string_list_longest_prefix(&prefixes, string);
if (match) {
printf("%s\n", match);
retval = 0;
}
else
retval = 1;
string_list_clear(&prefixes, 0);
return retval;
}
fprintf(stderr, "%s: unknown function name: %s\n", argv[0], fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)"); argv[1] ? argv[1] : "(there was none)");
return 1; return 1;