From ddc2a6281595fd24ea01497c496f88c40a59562f Mon Sep 17 00:00:00 2001 From: Martin Erik Werner Date: Tue, 4 Feb 2014 15:25:19 +0100 Subject: setup: add abspath_part_inside_repo() function In order to extract the part of an absolute path which lies inside the repo, it is not possible to directly use real_path, since that would dereference symlinks both outside and inside the work tree. Add an abspath_part_inside_repo() function which first checks if the work tree is already the prefix, then incrementally checks each path level by temporarily NUL-terminating at each '/' and comparing against the work tree path. If a match is found, it overwrites the input path with the remainder past the work tree (which will be the part inside the work tree). This function is currently only intended for use in 'prefix_path_gently'. Signed-off-by: Martin Erik Werner Reviewed-by: Duy Nguyen Signed-off-by: Junio C Hamano --- setup.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'setup.c') diff --git a/setup.c b/setup.c index 5432a31b62..bb8b53190e 100644 --- a/setup.c +++ b/setup.c @@ -5,6 +5,70 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; +/* + * The input parameter must contain an absolute path, and it must already be + * normalized. + * + * Find the part of an absolute path that lies inside the work tree by + * dereferencing symlinks outside the work tree, for example: + * /dir1/repo/dir2/file (work tree is /dir1/repo) -> dir2/file + * /dir/file (work tree is /) -> dir/file + * /dir/symlink1/symlink2 (symlink1 points to work tree) -> symlink2 + * /dir/repolink/file (repolink points to /dir/repo) -> file + * /dir/repo (exactly equal to work tree) -> (empty string) + */ +static int abspath_part_inside_repo(char *path) +{ + size_t len; + size_t wtlen; + char *path0; + int off; + const char *work_tree = get_git_work_tree(); + + if (!work_tree) + return -1; + wtlen = strlen(work_tree); + len = strlen(path); + off = 0; + + /* check if work tree is already the prefix */ + if (wtlen <= len && !strncmp(path, work_tree, wtlen)) { + if (path[wtlen] == '/') { + memmove(path, path + wtlen + 1, len - wtlen); + return 0; + } else if (path[wtlen - 1] == '/' || path[wtlen] == '\0') { + /* work tree is the root, or the whole path */ + memmove(path, path + wtlen, len - wtlen + 1); + return 0; + } + /* work tree might match beginning of a symlink to work tree */ + off = wtlen; + } + path0 = path; + path += offset_1st_component(path) + off; + + /* check each '/'-terminated level */ + while (*path) { + path++; + if (*path == '/') { + *path = '\0'; + if (strcmp(real_path(path0), work_tree) == 0) { + memmove(path0, path + 1, len - (path - path0)); + return 0; + } + *path = '/'; + } + } + + /* check whole path */ + if (strcmp(real_path(path0), work_tree) == 0) { + *path0 = '\0'; + return 0; + } + + return -1; +} + /* * Normalize "path", prepending the "prefix" for relative paths. If * remaining_prefix is not NULL, return the actual prefix still -- cgit v1.2.3 From 655ee9ea3e6c0af57d320e84723ec3bf656cdbf7 Mon Sep 17 00:00:00 2001 From: Martin Erik Werner Date: Tue, 4 Feb 2014 15:25:20 +0100 Subject: setup: don't dereference in-tree symlinks for absolute paths The prefix_path_gently() function currently applies real_path to everything if given an absolute path, dereferencing symlinks both outside and inside the work tree. This causes most high-level functions to misbehave when acting on symlinks given via absolute paths. For example $ git add /dir/repo/symlink attempts to add the target of the symlink rather than the symlink itself, which is usually not what the user intends to do. In order to manipulate symlinks in the work tree using absolute paths, symlinks should only be dereferenced outside the work tree. Modify the prefix_path_gently() to first normalize the path in order to make sure path levels are separated by '/', then pass the result to 'abspath_part_inside_repo' to find the part inside the work tree (without dereferencing any symlinks inside the work tree). For absolute paths, prefix_path_gently() did not, nor does now do, any actual prefixing, hence the result from abspath_part_in_repo() is returned as-is. Fixes t0060-82 and t3004-5. Signed-off-by: Martin Erik Werner Reviewed-by: Duy Nguyen Signed-off-by: Junio C Hamano --- setup.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) (limited to 'setup.c') diff --git a/setup.c b/setup.c index bb8b53190e..8295ba6bd8 100644 --- a/setup.c +++ b/setup.c @@ -86,11 +86,17 @@ char *prefix_path_gently(const char *prefix, int len, const char *orig = path; char *sanitized; if (is_absolute_path(orig)) { - const char *temp = real_path(path); - sanitized = xmalloc(len + strlen(temp) + 1); - strcpy(sanitized, temp); + sanitized = xmalloc(strlen(path) + 1); if (remaining_prefix) *remaining_prefix = 0; + if (normalize_path_copy_len(sanitized, path, remaining_prefix)) { + free(sanitized); + return NULL; + } + if (abspath_part_inside_repo(sanitized)) { + free(sanitized); + return NULL; + } } else { sanitized = xmalloc(len + strlen(path) + 1); if (len) @@ -98,26 +104,10 @@ char *prefix_path_gently(const char *prefix, int len, strcpy(sanitized + len, path); if (remaining_prefix) *remaining_prefix = len; - } - if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) - goto error_out; - if (is_absolute_path(orig)) { - size_t root_len, len, total; - const char *work_tree = get_git_work_tree(); - if (!work_tree) - goto error_out; - len = strlen(work_tree); - root_len = offset_1st_component(work_tree); - total = strlen(sanitized) + 1; - if (strncmp(sanitized, work_tree, len) || - (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) { - error_out: + if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) { free(sanitized); return NULL; } - if (sanitized[len] == '/') - len++; - memmove(sanitized, sanitized + len, total - len); } return sanitized; } -- cgit v1.2.3