X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=path.c;h=79aa104712364a8c18964feecd4c8079449a78cf;hb=9ee6bcd398057cb0cf3c52b9dc93b094d3d37235;hp=4b9107fed10c1f3551acf1f14d2ba5d1ba8a0b84;hpb=66fd74ea5db13e6f095f7c414e9ea4a1aa59cdc7;p=git.git diff --git a/path.c b/path.c index 4b9107fed..79aa10471 100644 --- a/path.c +++ b/path.c @@ -11,6 +11,7 @@ * which is what it's designed for. */ #include "cache.h" +#include "strbuf.h" static char bad_path[] = "/bad-path/"; @@ -139,6 +140,22 @@ int git_mkstemp(char *path, size_t len, const char *template) return mkstemp(path); } +/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */ +int git_mkstemps(char *path, size_t len, const char *template, int suffix_len) +{ + const char *tmp; + size_t n; + + tmp = getenv("TMPDIR"); + if (!tmp) + tmp = "/tmp"; + n = snprintf(path, len, "%s/%s", tmp, template); + if (len <= n) { + errno = ENAMETOOLONG; + return -1; + } + return mkstemps(path, suffix_len); +} int validate_headref(const char *path) { @@ -191,43 +208,49 @@ int validate_headref(const char *path) return -1; } -static char *user_path(char *buf, char *path, int sz) +static struct passwd *getpw_str(const char *username, size_t len) { struct passwd *pw; - char *slash; - int len, baselen; + char *username_z = xmalloc(len + 1); + memcpy(username_z, username, len); + username_z[len] = '\0'; + pw = getpwnam(username_z); + free(username_z); + return pw; +} - if (!path || path[0] != '~') - return NULL; - path++; - slash = strchr(path, '/'); - if (path[0] == '/' || !path[0]) { - pw = getpwuid(getuid()); - } - else { - if (slash) { - *slash = 0; - pw = getpwnam(path); - *slash = '/'; +/* + * Return a string with ~ and ~user expanded via getpw*. If buf != NULL, + * then it is a newly allocated string. Returns NULL on getpw failure or + * if path is NULL. + */ +char *expand_user_path(const char *path) +{ + struct strbuf user_path = STRBUF_INIT; + const char *first_slash = strchrnul(path, '/'); + const char *to_copy = path; + + if (path == NULL) + goto return_null; + if (path[0] == '~') { + const char *username = path + 1; + size_t username_len = first_slash - username; + if (username_len == 0) { + const char *home = getenv("HOME"); + strbuf_add(&user_path, home, strlen(home)); + } else { + struct passwd *pw = getpw_str(username, username_len); + if (!pw) + goto return_null; + strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir)); } - else - pw = getpwnam(path); - } - if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir)) - return NULL; - baselen = strlen(pw->pw_dir); - memcpy(buf, pw->pw_dir, baselen); - while ((1 < baselen) && (buf[baselen-1] == '/')) { - buf[baselen-1] = 0; - baselen--; - } - if (slash && slash[1]) { - len = strlen(slash); - if (sz <= baselen + len) - return NULL; - memcpy(buf + baselen, slash, len + 1); + to_copy = first_slash; } - return buf; + strbuf_add(&user_path, to_copy, strlen(to_copy)); + return strbuf_detach(&user_path, NULL); +return_null: + strbuf_release(&user_path); + return NULL; } /* @@ -275,8 +298,18 @@ char *enter_repo(char *path, int strict) if (PATH_MAX <= len) return NULL; if (path[0] == '~') { - if (!user_path(used_path, path, PATH_MAX)) + char *newpath = expand_user_path(path); + if (!newpath || (PATH_MAX - 10 < strlen(newpath))) { + free(newpath); return NULL; + } + /* + * Copy back into the static buffer. A pity + * since newpath was not bounded, but other + * branches of the if are limited by PATH_MAX + * anyway. + */ + strcpy(used_path, newpath); free(newpath); strcpy(validated_path, path); path = used_path; } @@ -311,36 +344,49 @@ char *enter_repo(char *path, int strict) return NULL; } -int adjust_shared_perm(const char *path) +int set_shared_perm(const char *path, int mode) { struct stat st; - int mode; + int tweak, shared, orig_mode; - if (!shared_repository) + if (!shared_repository) { + if (mode) + return chmod(path, mode & ~S_IFMT); return 0; - if (lstat(path, &st) < 0) - return -1; - mode = st.st_mode; - - if (shared_repository) { - int tweak = shared_repository; - if (!(mode & S_IWUSR)) - tweak &= ~0222; - mode |= tweak; - } else { - /* Preserve old PERM_UMASK behaviour */ - if (mode & S_IWUSR) - mode |= S_IWGRP; } + if (!mode) { + if (lstat(path, &st) < 0) + return -1; + mode = st.st_mode; + orig_mode = mode; + } else + orig_mode = 0; + if (shared_repository < 0) + shared = -shared_repository; + else + shared = shared_repository; + tweak = shared; + + if (!(mode & S_IWUSR)) + tweak &= ~0222; + if (mode & S_IXUSR) + /* Copy read bits to execute bits */ + tweak |= (tweak & 0444) >> 2; + if (shared_repository < 0) + mode = (mode & ~0777) | tweak; + else + mode |= tweak; if (S_ISDIR(mode)) { - mode |= FORCE_DIR_SET_GID; - /* Copy read bits to execute bits */ - mode |= (shared_repository & 0444) >> 2; + mode |= (shared & 0444) >> 2; + mode |= FORCE_DIR_SET_GID; } - if ((mode & st.st_mode) != mode && chmod(path, mode) < 0) + if (((shared_repository < 0 + ? (orig_mode & (FORCE_DIR_SET_GID | 0777)) + : (orig_mode & mode)) != mode) && + chmod(path, (mode & ~S_IFMT)) < 0) return -2; return 0; } @@ -348,17 +394,38 @@ int adjust_shared_perm(const char *path) const char *make_relative_path(const char *abs, const char *base) { static char buf[PATH_MAX + 1]; - int baselen; - if (!base) - return abs; - baselen = strlen(base); - if (prefixcmp(abs, base)) + int i = 0, j = 0; + + if (!base || !base[0]) return abs; - if (abs[baselen] == '/') - baselen++; - else if (base[baselen - 1] != '/') + while (base[i]) { + if (is_dir_sep(base[i])) { + if (!is_dir_sep(abs[j])) + return abs; + while (is_dir_sep(base[i])) + i++; + while (is_dir_sep(abs[j])) + j++; + continue; + } else if (abs[j] != base[i]) { + return abs; + } + i++; + j++; + } + if ( + /* "/foo" is a prefix of "/foo" */ + abs[j] && + /* "/foo" is not a prefix of "/foobar" */ + !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j]) + ) return abs; - strcpy(buf, abs + baselen); + while (is_dir_sep(abs[j])) + j++; + if (!abs[j]) + strcpy(buf, "."); + else + strcpy(buf, abs + j); return buf; } @@ -499,3 +566,86 @@ int longest_ancestor_length(const char *path, const char *prefix_list) return max_len; } + +/* strip arbitrary amount of directory separators at end of path */ +static inline int chomp_trailing_dir_sep(const char *path, int len) +{ + while (len && is_dir_sep(path[len - 1])) + len--; + return len; +} + +/* + * If path ends with suffix (complete path components), returns the + * part before suffix (sans trailing directory separators). + * Otherwise returns NULL. + */ +char *strip_path_suffix(const char *path, const char *suffix) +{ + int path_len = strlen(path), suffix_len = strlen(suffix); + + while (suffix_len) { + if (!path_len) + return NULL; + + if (is_dir_sep(path[path_len - 1])) { + if (!is_dir_sep(suffix[suffix_len - 1])) + return NULL; + path_len = chomp_trailing_dir_sep(path, path_len); + suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); + } + else if (path[--path_len] != suffix[--suffix_len]) + return NULL; + } + + if (path_len && !is_dir_sep(path[path_len - 1])) + return NULL; + return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); +} + +int daemon_avoid_alias(const char *p) +{ + int sl, ndot; + + /* + * This resurrects the belts and suspenders paranoia check by HPA + * done in <435560F7.4080006@zytor.com> thread, now enter_repo() + * does not do getcwd() based path canonicalizations. + * + * sl becomes true immediately after seeing '/' and continues to + * be true as long as dots continue after that without intervening + * non-dot character. + */ + if (!p || (*p != '/' && *p != '~')) + return -1; + sl = 1; ndot = 0; + p++; + + while (1) { + char ch = *p++; + if (sl) { + if (ch == '.') + ndot++; + else if (ch == '/') { + if (ndot < 3) + /* reject //, /./ and /../ */ + return -1; + ndot = 0; + } + else if (ch == 0) { + if (0 < ndot && ndot < 3) + /* reject /.$ and /..$ */ + return -1; + return 0; + } + else + sl = ndot = 0; + } + else if (ch == 0) + return 0; + else if (ch == '/') { + sl = 1; + ndot = 0; + } + } +}