X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=inline;f=path.c;h=c5d25a4b903bd92930df4004d30d4dffa6054456;hb=e861ce1692fa9809f3e7b898804f8ddaf7cd8975;hp=2c077c0c8f7066c5338050ce890c0846adedf6bd;hpb=36d277c72d90d32f99616072b64a2652248f5264;p=git.git diff --git a/path.c b/path.c index 2c077c0c8..c5d25a4b9 100644 --- a/path.c +++ b/path.c @@ -11,11 +11,16 @@ * which is what it's designed for. */ #include "cache.h" -#include -static char pathname[PATH_MAX]; static char bad_path[] = "/bad-path/"; +static char *get_pathname(void) +{ + static char pathname_array[4][PATH_MAX]; + static int index; + return pathname_array[3 & ++index]; +} + static char *cleanup_path(char *path) { /* Clean it up */ @@ -31,6 +36,7 @@ char *mkpath(const char *fmt, ...) { va_list args; unsigned len; + char *pathname = get_pathname(); va_start(args, fmt); len = vsnprintf(pathname, PATH_MAX, fmt, args); @@ -43,6 +49,7 @@ char *mkpath(const char *fmt, ...) char *git_path(const char *fmt, ...) { const char *git_dir = get_git_dir(); + char *pathname = get_pathname(); va_list args; unsigned len; @@ -77,24 +84,17 @@ int git_mkstemp(char *path, size_t len, const char *template) pch += n; } - safe_strncpy(pch, template, len); + strlcpy(pch, template, len); return mkstemp(path); } -char *safe_strncpy(char *dest, const char *src, size_t n) -{ - strncpy(dest, src, n); - dest[n - 1] = '\0'; - - return dest; -} - -int validate_symref(const char *path) +int validate_headref(const char *path) { struct stat st; char *buf, buffer[256]; + unsigned char sha1[20]; int len, fd; if (lstat(path, &st) < 0) @@ -114,94 +114,181 @@ int validate_symref(const char *path) fd = open(path, O_RDONLY); if (fd < 0) return -1; - len = read(fd, buffer, sizeof(buffer)-1); + len = read_in_full(fd, buffer, sizeof(buffer)-1); close(fd); /* * Is it a symbolic ref? */ - if (len < 4 || memcmp("ref:", buffer, 4)) + if (len < 4) return -1; - buf = buffer + 4; - len -= 4; - while (len && isspace(*buf)) - buf++, len--; - if (len >= 5 && !memcmp("refs/", buf, 5)) + if (!memcmp("ref:", buffer, 4)) { + buf = buffer + 4; + len -= 4; + while (len && isspace(*buf)) + buf++, len--; + if (len >= 5 && !memcmp("refs/", buf, 5)) + return 0; + } + + /* + * Is this a detached HEAD? + */ + if (!get_sha1_hex(buffer, sha1)) return 0; - return -1; -} -static char *current_dir(void) -{ - return getcwd(pathname, sizeof(pathname)); + return -1; } -static int user_chdir(char *path) +static char *user_path(char *buf, char *path, int sz) { - char *dir = path; + struct passwd *pw; + char *slash; + int len, baselen; - if(*dir == '~') { /* user-relative path */ - struct passwd *pw; - char *slash = strchr(dir, '/'); - - dir++; - /* '~/' and '~' (no slash) means users own home-dir */ - if(!*dir || *dir == '/') - pw = getpwuid(getuid()); - else { - if (slash) { - *slash = '\0'; - pw = getpwnam(dir); - *slash = '/'; - } - else - pw = getpwnam(dir); + 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 = '/'; } - - /* make sure we got something back that we can chdir() to */ - if(!pw || chdir(pw->pw_dir) < 0) - return -1; - - if(!slash || !slash[1]) /* no path following username */ - return 0; - - dir = slash + 1; + else + pw = getpwnam(path); } - - /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */ - if(chdir(dir) < 0) - return -1; - - return 0; + 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); + } + return buf; } +/* + * First, one directory to try is determined by the following algorithm. + * + * (0) If "strict" is given, the path is used as given and no DWIM is + * done. Otherwise: + * (1) "~/path" to mean path under the running user's home directory; + * (2) "~user/path" to mean path under named user's home directory; + * (3) "relative/path" to mean cwd relative directory; or + * (4) "/absolute/path" to mean absolute directory. + * + * Unless "strict" is given, we try access() for existence of "%s.git/.git", + * "%s/.git", "%s.git", "%s" in this order. The first one that exists is + * what we try. + * + * Second, we try chdir() to that. Upon failure, we return NULL. + * + * Then, we try if the current directory is a valid git repository. + * Upon failure, we return NULL. + * + * If all goes well, we return the directory we used to chdir() (but + * before ~user is expanded), avoiding getcwd() resolving symbolic + * links. User relative paths are also returned as they are given, + * except DWIM suffixing. + */ char *enter_repo(char *path, int strict) { - if(!path) + static char used_path[PATH_MAX]; + static char validated_path[PATH_MAX]; + + if (!path) return NULL; - if (strict) { - if (chdir(path) < 0) + if (!strict) { + static const char *suffix[] = { + ".git/.git", "/.git", ".git", "", NULL, + }; + int len = strlen(path); + int i; + while ((1 < len) && (path[len-1] == '/')) { + path[len-1] = 0; + len--; + } + if (PATH_MAX <= len) return NULL; - } - else { - if (!*path) - ; /* happy -- no chdir */ - else if (!user_chdir(path)) - ; /* happy -- as given */ - else if (!user_chdir(mkpath("%s.git", path))) - ; /* happy -- uemacs --> uemacs.git */ - else + if (path[0] == '~') { + if (!user_path(used_path, path, PATH_MAX)) + return NULL; + strcpy(validated_path, path); + path = used_path; + } + else if (PATH_MAX - 10 < len) return NULL; - (void)chdir(".git"); + else { + path = strcpy(used_path, path); + strcpy(validated_path, path); + } + len = strlen(path); + for (i = 0; suffix[i]; i++) { + strcpy(path + len, suffix[i]); + if (!access(path, F_OK)) { + strcat(validated_path, suffix[i]); + break; + } + } + if (!suffix[i] || chdir(path)) + return NULL; + path = validated_path; } + else if (chdir(path)) + return NULL; - if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 && - validate_symref("HEAD") == 0) { + if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 && + validate_headref("HEAD") == 0) { putenv("GIT_DIR=."); check_repository_format(); - return current_dir(); + return path; } return NULL; } + +int adjust_shared_perm(const char *path) +{ + struct stat st; + int mode; + + if (!shared_repository) + return 0; + if (lstat(path, &st) < 0) + return -1; + mode = st.st_mode; + if (mode & S_IRUSR) + mode |= (shared_repository == PERM_GROUP + ? S_IRGRP + : (shared_repository == PERM_EVERYBODY + ? (S_IRGRP|S_IROTH) + : 0)); + + if (mode & S_IWUSR) + mode |= S_IWGRP; + + if (mode & S_IXUSR) + mode |= (shared_repository == PERM_GROUP + ? S_IXGRP + : (shared_repository == PERM_EVERYBODY + ? (S_IXGRP|S_IXOTH) + : 0)); + if (S_ISDIR(mode)) + mode |= S_ISGID; + if ((mode & st.st_mode) != mode && chmod(path, mode) < 0) + return -2; + return 0; +}