1/*2* I'm tired of doing "vsnprintf()" etc just to open a3* file, so here's a "return static buffer with printf"4* interface for paths.5*6* It's obviously not thread-safe. Sue me. But it's quite7* useful for doing things like8*9* f = open(mkpath("%s/%s.git", base, name), O_RDONLY);10*11* which is what it's designed for.12*/13#include "cache.h"1415static char bad_path[] = "/bad-path/";1617static char *get_pathname(void)18{19static char pathname_array[4][PATH_MAX];20static int index;21return pathname_array[3 & ++index];22}2324static char *cleanup_path(char *path)25{26/* Clean it up */27if (!memcmp(path, "./", 2)) {28path += 2;29while (*path == '/')30path++;31}32return path;33}3435char *mkpath(const char *fmt, ...)36{37va_list args;38unsigned len;39char *pathname = get_pathname();4041va_start(args, fmt);42len = vsnprintf(pathname, PATH_MAX, fmt, args);43va_end(args);44if (len >= PATH_MAX)45return bad_path;46return cleanup_path(pathname);47}4849char *git_path(const char *fmt, ...)50{51const char *git_dir = get_git_dir();52char *pathname = get_pathname();53va_list args;54unsigned len;5556len = strlen(git_dir);57if (len > PATH_MAX-100)58return bad_path;59memcpy(pathname, git_dir, len);60if (len && git_dir[len-1] != '/')61pathname[len++] = '/';62va_start(args, fmt);63len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);64va_end(args);65if (len >= PATH_MAX)66return bad_path;67return cleanup_path(pathname);68}697071/* git_mkstemp() - create tmp file honoring TMPDIR variable */72int git_mkstemp(char *path, size_t len, const char *template)73{74const char *tmp;75size_t n;7677tmp = getenv("TMPDIR");78if (!tmp)79tmp = "/tmp";80n = snprintf(path, len, "%s/%s", tmp, template);81if (len <= n) {82errno = ENAMETOOLONG;83return -1;84}85return mkstemp(path);86}878889int validate_headref(const char *path)90{91struct stat st;92char *buf, buffer[256];93unsigned char sha1[20];94int fd;95ssize_t len;9697if (lstat(path, &st) < 0)98return -1;99100/* Make sure it is a "refs/.." symlink */101if (S_ISLNK(st.st_mode)) {102len = readlink(path, buffer, sizeof(buffer)-1);103if (len >= 5 && !memcmp("refs/", buffer, 5))104return 0;105return -1;106}107108/*109* Anything else, just open it and try to see if it is a symbolic ref.110*/111fd = open(path, O_RDONLY);112if (fd < 0)113return -1;114len = read_in_full(fd, buffer, sizeof(buffer)-1);115close(fd);116117/*118* Is it a symbolic ref?119*/120if (len < 4)121return -1;122if (!memcmp("ref:", buffer, 4)) {123buf = buffer + 4;124len -= 4;125while (len && isspace(*buf))126buf++, len--;127if (len >= 5 && !memcmp("refs/", buf, 5))128return 0;129}130131/*132* Is this a detached HEAD?133*/134if (!get_sha1_hex(buffer, sha1))135return 0;136137return -1;138}139140static char *user_path(char *buf, char *path, int sz)141{142struct passwd *pw;143char *slash;144int len, baselen;145146if (!path || path[0] != '~')147return NULL;148path++;149slash = strchr(path, '/');150if (path[0] == '/' || !path[0]) {151pw = getpwuid(getuid());152}153else {154if (slash) {155*slash = 0;156pw = getpwnam(path);157*slash = '/';158}159else160pw = getpwnam(path);161}162if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))163return NULL;164baselen = strlen(pw->pw_dir);165memcpy(buf, pw->pw_dir, baselen);166while ((1 < baselen) && (buf[baselen-1] == '/')) {167buf[baselen-1] = 0;168baselen--;169}170if (slash && slash[1]) {171len = strlen(slash);172if (sz <= baselen + len)173return NULL;174memcpy(buf + baselen, slash, len + 1);175}176return buf;177}178179/*180* First, one directory to try is determined by the following algorithm.181*182* (0) If "strict" is given, the path is used as given and no DWIM is183* done. Otherwise:184* (1) "~/path" to mean path under the running user's home directory;185* (2) "~user/path" to mean path under named user's home directory;186* (3) "relative/path" to mean cwd relative directory; or187* (4) "/absolute/path" to mean absolute directory.188*189* Unless "strict" is given, we try access() for existence of "%s.git/.git",190* "%s/.git", "%s.git", "%s" in this order. The first one that exists is191* what we try.192*193* Second, we try chdir() to that. Upon failure, we return NULL.194*195* Then, we try if the current directory is a valid git repository.196* Upon failure, we return NULL.197*198* If all goes well, we return the directory we used to chdir() (but199* before ~user is expanded), avoiding getcwd() resolving symbolic200* links. User relative paths are also returned as they are given,201* except DWIM suffixing.202*/203char *enter_repo(char *path, int strict)204{205static char used_path[PATH_MAX];206static char validated_path[PATH_MAX];207208if (!path)209return NULL;210211if (!strict) {212static const char *suffix[] = {213".git/.git", "/.git", ".git", "", NULL,214};215int len = strlen(path);216int i;217while ((1 < len) && (path[len-1] == '/')) {218path[len-1] = 0;219len--;220}221if (PATH_MAX <= len)222return NULL;223if (path[0] == '~') {224if (!user_path(used_path, path, PATH_MAX))225return NULL;226strcpy(validated_path, path);227path = used_path;228}229else if (PATH_MAX - 10 < len)230return NULL;231else {232path = strcpy(used_path, path);233strcpy(validated_path, path);234}235len = strlen(path);236for (i = 0; suffix[i]; i++) {237strcpy(path + len, suffix[i]);238if (!access(path, F_OK)) {239strcat(validated_path, suffix[i]);240break;241}242}243if (!suffix[i] || chdir(path))244return NULL;245path = validated_path;246}247else if (chdir(path))248return NULL;249250if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&251validate_headref("HEAD") == 0) {252setenv(GIT_DIR_ENVIRONMENT, ".", 1);253check_repository_format();254return path;255}256257return NULL;258}259260int adjust_shared_perm(const char *path)261{262struct stat st;263int mode;264265if (!shared_repository)266return 0;267if (lstat(path, &st) < 0)268return -1;269mode = st.st_mode;270271if (shared_repository) {272int tweak = shared_repository;273if (!(mode & S_IWUSR))274tweak &= ~0222;275mode = (mode & ~0777) | tweak;276} else {277/* Preserve old PERM_UMASK behaviour */278if (mode & S_IWUSR)279mode |= S_IWGRP;280}281282if (S_ISDIR(mode)) {283mode |= FORCE_DIR_SET_GID;284285/* Copy read bits to execute bits */286mode |= (shared_repository & 0444) >> 2;287}288289if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)290return -2;291return 0;292}293294/* We allow "recursive" symbolic links. Only within reason, though. */295#define MAXDEPTH 5296297const char *make_absolute_path(const char *path)298{299static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];300char cwd[1024] = "";301int buf_index = 1, len;302303int depth = MAXDEPTH;304char *last_elem = NULL;305struct stat st;306307if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)308die ("Too long path: %.*s", 60, path);309310while (depth--) {311if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {312char *last_slash = strrchr(buf, '/');313if (last_slash) {314*last_slash = '\0';315last_elem = xstrdup(last_slash + 1);316} else {317last_elem = xstrdup(buf);318*buf = '\0';319}320}321322if (*buf) {323if (!*cwd && !getcwd(cwd, sizeof(cwd)))324die ("Could not get current working directory");325326if (chdir(buf))327die ("Could not switch to '%s'", buf);328}329if (!getcwd(buf, PATH_MAX))330die ("Could not get current working directory");331332if (last_elem) {333int len = strlen(buf);334if (len + strlen(last_elem) + 2 > PATH_MAX)335die ("Too long path name: '%s/%s'",336buf, last_elem);337buf[len] = '/';338strcpy(buf + len + 1, last_elem);339free(last_elem);340last_elem = NULL;341}342343if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {344len = readlink(buf, next_buf, PATH_MAX);345if (len < 0)346die ("Invalid symlink: %s", buf);347next_buf[len] = '\0';348buf = next_buf;349buf_index = 1 - buf_index;350next_buf = bufs[buf_index];351} else352break;353}354355if (*cwd && chdir(cwd))356die ("Could not change back to '%s'", cwd);357358return buf;359}360361/*362* path = absolute path363* buf = buffer of at least max(2, strlen(path)+1) bytes364* It is okay if buf == path, but they should not overlap otherwise.365*366* Performs the following normalizations on path, storing the result in buf:367* - Removes trailing slashes.368* - Removes empty components.369* - Removes "." components.370* - Removes ".." components, and the components the precede them.371* "" and paths that contain only slashes are normalized to "/".372* Returns the length of the output.373*374* Note that this function is purely textual. It does not follow symlinks,375* verify the existence of the path, or make any system calls.376*/377int normalize_absolute_path(char *buf, const char *path)378{379const char *comp_start = path, *comp_end = path;380char *dst = buf;381int comp_len;382assert(buf);383assert(path);384385while (*comp_start) {386assert(*comp_start == '/');387while (*++comp_end && *comp_end != '/')388; /* nothing */389comp_len = comp_end - comp_start;390391if (!strncmp("/", comp_start, comp_len) ||392!strncmp("/.", comp_start, comp_len))393goto next;394395if (!strncmp("/..", comp_start, comp_len)) {396while (dst > buf && *--dst != '/')397; /* nothing */398goto next;399}400401memcpy(dst, comp_start, comp_len);402dst += comp_len;403next:404comp_start = comp_end;405}406407if (dst == buf)408*dst++ = '/';409410*dst = '\0';411return dst - buf;412}