X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=path.c;h=334b2bd195f56ac6aaf9f9b8a36fe59115cd758c;hb=fb6a9f93d39e4e5fdb83673a927f71a34e9fb7c0;hp=b85f087f4390f1235a6b9e9545b3f6dd8457fdd5;hpb=f10e0e0b18c8e2e69535e7380fb3c1f9b097cfda;p=git.git diff --git a/path.c b/path.c index b85f087f..334b2bd1 100644 --- a/path.c +++ b/path.c @@ -11,6 +11,7 @@ * which is what it's designed for. */ #include "cache.h" +#include static char pathname[PATH_MAX]; static char bad_path[] = "/bad-path/"; @@ -41,7 +42,7 @@ char *mkpath(const char *fmt, ...) char *git_path(const char *fmt, ...) { - const char *git_dir = gitenv(GIT_DIR_ENVIRONMENT) ? : DEFAULT_GIT_DIR_ENVIRONMENT; + const char *git_dir = get_git_dir(); va_list args; unsigned len; @@ -89,3 +90,163 @@ char *safe_strncpy(char *dest, const char *src, size_t n) return dest; } + +int validate_symref(const char *path) +{ + struct stat st; + char *buf, buffer[256]; + int len, fd; + + if (lstat(path, &st) < 0) + return -1; + + /* Make sure it is a "refs/.." symlink */ + if (S_ISLNK(st.st_mode)) { + len = readlink(path, buffer, sizeof(buffer)-1); + if (len >= 5 && !memcmp("refs/", buffer, 5)) + return 0; + return -1; + } + + /* + * Anything else, just open it and try to see if it is a symbolic ref. + */ + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + len = read(fd, buffer, sizeof(buffer)-1); + close(fd); + + /* + * Is it a symbolic ref? + */ + if (len < 4 || memcmp("ref:", buffer, 4)) + return -1; + buf = buffer + 4; + len -= 4; + while (len && isspace(*buf)) + buf++, len--; + if (len >= 5 && !memcmp("refs/", buf, 5)) + return 0; + return -1; +} + +static char *user_path(char *buf, char *path, int sz) +{ + struct passwd *pw; + char *slash; + int len, baselen; + + 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 = '/'; + } + 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); + } + 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) +{ + static char used_path[PATH_MAX]; + static char validated_path[PATH_MAX]; + + if (!path) + return NULL; + + 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; + 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; + 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) { + putenv("GIT_DIR=."); + check_repository_format(); + return path; + } + + return NULL; +}