summaryrefslogtreecommitdiff
path: root/path.c
diff options
context:
space:
mode:
Diffstat (limited to 'path.c')
-rw-r--r--path.c257
1 files changed, 214 insertions, 43 deletions
diff --git a/path.c b/path.c
index 047fdb0a1f..b4c8d91722 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/";
@@ -156,6 +157,85 @@ int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
return mkstemps(path, suffix_len);
}
+/* Adapted from libiberty's mkstemp.c. */
+
+#undef TMP_MAX
+#define TMP_MAX 16384
+
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
+{
+ static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ static const int num_letters = 62;
+ uint64_t value;
+ struct timeval tv;
+ char *template;
+ size_t len;
+ int fd, count;
+
+ len = strlen(pattern);
+
+ if (len < 6 + suffix_len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Replace pattern's XXXXXX characters with randomness.
+ * Try TMP_MAX different filenames.
+ */
+ gettimeofday(&tv, NULL);
+ value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+ template = &pattern[len - 6 - suffix_len];
+ for (count = 0; count < TMP_MAX; ++count) {
+ uint64_t v = value;
+ /* Fill in the random bits. */
+ template[0] = letters[v % num_letters]; v /= num_letters;
+ template[1] = letters[v % num_letters]; v /= num_letters;
+ template[2] = letters[v % num_letters]; v /= num_letters;
+ template[3] = letters[v % num_letters]; v /= num_letters;
+ template[4] = letters[v % num_letters]; v /= num_letters;
+ template[5] = letters[v % num_letters]; v /= num_letters;
+
+ fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
+ if (fd > 0)
+ return fd;
+ /*
+ * Fatal error (EPERM, ENOSPC etc).
+ * It doesn't make sense to loop.
+ */
+ if (errno != EEXIST)
+ break;
+ /*
+ * This is a random value. It is only necessary that
+ * the next TMP_MAX values generated by adding 7777 to
+ * VALUE are different with (module 2^32).
+ */
+ value += 7777;
+ }
+ /* We return the null string if we can't find a unique file name. */
+ pattern[0] = '\0';
+ return -1;
+}
+
+int git_mkstemp_mode(char *pattern, int mode)
+{
+ /* mkstemp is just mkstemps with no suffix */
+ return git_mkstemps_mode(pattern, 0, mode);
+}
+
+int gitmkstemps(char *pattern, int suffix_len)
+{
+ return git_mkstemps_mode(pattern, suffix_len, 0600);
+}
+
int validate_headref(const char *path)
{
struct stat st;
@@ -207,43 +287,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;
}
/*
@@ -291,8 +377,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;
}
@@ -319,7 +415,7 @@ char *enter_repo(char *path, int strict)
if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
validate_headref("HEAD") == 0) {
- setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+ set_git_dir(".");
check_repository_format();
return path;
}
@@ -377,17 +473,38 @@ int set_shared_perm(const char *path, int mode)
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;
}
@@ -564,3 +681,57 @@ char *strip_path_suffix(const char *path, const char *suffix)
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 canonicalization.
+ *
+ * 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;
+ }
+ }
+}
+
+int offset_1st_component(const char *path)
+{
+ if (has_dos_drive_prefix(path))
+ return 2 + is_dir_sep(path[2]);
+ return is_dir_sep(path[0]);
+}