X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=path.c;h=504eae061fe27e846f67f3e1c25d51e886420b62;hb=65360346309a550185363ef5b846a1202175a4e6;hp=496123ca552a3aad32b009c668962045ec78d218;hpb=8c992d16cb8f964e42a733dbc60f0e3f0b47270e;p=git.git diff --git a/path.c b/path.c index 496123ca5..504eae061 100644 --- a/path.c +++ b/path.c @@ -272,7 +272,7 @@ int adjust_shared_perm(const char *path) int tweak = shared_repository; if (!(mode & S_IWUSR)) tweak &= ~0222; - mode = (mode & ~0777) | tweak; + mode |= tweak; } else { /* Preserve old PERM_UMASK behaviour */ if (mode & S_IWUSR) @@ -343,3 +343,99 @@ const char *make_relative_path(const char *abs, const char *base) strcpy(buf, abs + baselen); return buf; } + +/* + * path = absolute path + * buf = buffer of at least max(2, strlen(path)+1) bytes + * It is okay if buf == path, but they should not overlap otherwise. + * + * Performs the following normalizations on path, storing the result in buf: + * - Removes trailing slashes. + * - Removes empty components. + * - Removes "." components. + * - Removes ".." components, and the components the precede them. + * "" and paths that contain only slashes are normalized to "/". + * Returns the length of the output. + * + * Note that this function is purely textual. It does not follow symlinks, + * verify the existence of the path, or make any system calls. + */ +int normalize_absolute_path(char *buf, const char *path) +{ + const char *comp_start = path, *comp_end = path; + char *dst = buf; + int comp_len; + assert(buf); + assert(path); + + while (*comp_start) { + assert(*comp_start == '/'); + while (*++comp_end && *comp_end != '/') + ; /* nothing */ + comp_len = comp_end - comp_start; + + if (!strncmp("/", comp_start, comp_len) || + !strncmp("/.", comp_start, comp_len)) + goto next; + + if (!strncmp("/..", comp_start, comp_len)) { + while (dst > buf && *--dst != '/') + ; /* nothing */ + goto next; + } + + memcpy(dst, comp_start, comp_len); + dst += comp_len; + next: + comp_start = comp_end; + } + + if (dst == buf) + *dst++ = '/'; + + *dst = '\0'; + return dst - buf; +} + +/* + * path = Canonical absolute path + * prefix_list = Colon-separated list of absolute paths + * + * Determines, for each path in parent_list, whether the "prefix" really + * is an ancestor directory of path. Returns the length of the longest + * 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 + * "/".) "/foo" is not considered an ancestor of "/foobar". Directories + * are not considered to be their own ancestors. path must be in a + * canonical form: empty components, or "." or ".." components are not + * allowed. prefix_list may be null, which is like "". + */ +int longest_ancestor_length(const char *path, const char *prefix_list) +{ + char buf[PATH_MAX+1]; + const char *ceil, *colon; + int len, max_len = -1; + + if (prefix_list == NULL || !strcmp(path, "/")) + return -1; + + for (colon = ceil = prefix_list; *colon; ceil = colon+1) { + for (colon = ceil; *colon && *colon != ':'; colon++); + len = colon - ceil; + if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) + continue; + strlcpy(buf, ceil, len+1); + len = normalize_absolute_path(buf, buf); + /* Strip "trailing slashes" from "/". */ + if (len == 1) + len = 0; + + if (!strncmp(path, buf, len) && + path[len] == '/' && + len > max_len) { + max_len = len; + } + } + + return max_len; +}