[PATCH 1/5] Library code for user-relative paths, take three.
From: Andreas Ericsson <hidden>
Date: 2016-06-15 22:42:12
Subsystem:
the rest · Maintainer:
Linus Torvalds
See the threads "User-relative paths", "[RFC] GIT paths" and "[PATCH 0/4] User-relative paths, take two" for previous discussions on this topic. This patch provides the work-horse of the user-relative paths feature, using Linus' idea of a blind chdir() and getcwd() which makes it remarkably simple. Signed-off-by: Andreas Ericsson <redacted> --- cache.h | 1 + path.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 0 deletions(-) applies-to: 8ff699dffc817e92fb2101f538f84c38d5ed0a0f 416ee0a4f47244471b52b9dc8aca3e984b20445f
diff --git a/cache.h b/cache.h
index 99afa2c..d8be06b 100644
--- a/cache.h
+++ b/cache.h@@ -192,6 +192,7 @@ extern int diff_rename_limit_default; /* Return a statically allocated filename matching the sha1 signature */ extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); +extern char *enter_repo(char *path, int strict); extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern char *sha1_file_name(const unsigned char *sha1); extern char *sha1_pack_name(const unsigned char *sha1);
diff --git a/path.c b/path.c
index 495d17c..5b61709 100644
--- a/path.c
+++ b/path.c@@ -11,6 +11,7 @@ * which is what it's designed for. */ #include "cache.h" +#include <pwd.h> static char pathname[PATH_MAX]; static char bad_path[] = "/bad-path/";
@@ -89,3 +90,74 @@ char *safe_strncpy(char *dest, const cha return dest; } + +static char *current_dir() +{ + return getcwd(pathname, sizeof(pathname)); +} + +/* Take a raw path from is_git_repo() and canonicalize it using Linus' + * idea of a blind chdir() and getcwd(). */ +static const char *canonical_path(char *path, int strict) +{ + char *dir = path; + + if(strict && *dir != '/') + return NULL; + + 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); + } + + /* make sure we got something back that we can chdir() to */ + if(!pw || chdir(pw->pw_dir) < 0) + return NULL; + + if(!slash || !slash[1]) /* no path following username */ + return current_dir(); + + dir = slash + 1; + } + + /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */ + if(chdir(dir) < 0) + return NULL; + + return current_dir(); +} + +char *enter_repo(char *path, int strict) +{ + if(!path) + return NULL; + + if(!canonical_path(path, strict)) { + if(strict || !canonical_path(mkpath("%s.git", path), strict)) + return NULL; + } + + /* This is perfectly safe, and people tend to think of the directory + * where they ran git-init-db as their repository, so humour them. */ + (void)chdir(".git"); + + if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0) { + putenv("GIT_DIR=."); + return current_dir(); + } + + return NULL; +}
--- 0.99.9.GIT