X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=refs.c;h=a52b038eefdf22fce20d1a81180c97def0613cdb;hb=303958dc42d451aead0e1b9cf7b9836831a05f4b;hp=6aa6aec82dbd5510d43dea07362399f57669eeef;hpb=a876ed83be5467d6075da8a16306724cb1babc2a;p=git.git diff --git a/refs.c b/refs.c index 6aa6aec8..a52b038e 100644 --- a/refs.c +++ b/refs.c @@ -2,11 +2,54 @@ #include "cache.h" #include -#include /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 +#ifndef USE_SYMLINK_HEAD +#define USE_SYMLINK_HEAD 1 +#endif + +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; +} + const char *resolve_ref(const char *path, unsigned char *sha1, int reading) { int depth = MAXDEPTH, len; @@ -71,6 +114,41 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading) return path; } +int create_symref(const char *git_HEAD, const char *refs_heads_master) +{ + const char *lockpath; + char ref[1000]; + int fd, len, written; + +#if USE_SYMLINK_HEAD + unlink(git_HEAD); + if (!symlink(refs_heads_master, git_HEAD)) + return 0; + fprintf(stderr, "no symlink - falling back to symbolic ref\n"); +#endif + + len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master); + if (sizeof(ref) <= len) { + error("refname too long: %s", refs_heads_master); + return -1; + } + lockpath = mkpath("%s.lock", git_HEAD); + fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); + written = write(fd, ref, len); + close(fd); + if (written != len) { + unlink(lockpath); + error("Unable to write to %s", lockpath); + return -2; + } + if (rename(lockpath, git_HEAD) < 0) { + unlink(lockpath); + error("Unable to create %s", git_HEAD); + return -3; + } + return 0; +} + int read_ref(const char *filename, unsigned char *sha1) { if (resolve_ref(filename, sha1, 1)) @@ -258,17 +336,54 @@ int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1) return retval; } +/* + * Make sure "ref" is something reasonable to have under ".git/refs/"; + * We do not like it if: + * + * - any path component of it begins with ".", or + * - it has double dots "..", or + * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or + * - it ends with a "/". + */ + +static inline int bad_ref_char(int ch) +{ + return (((unsigned) ch) <= ' ' || + ch == '~' || ch == '^' || ch == ':'); +} + int check_ref_format(const char *ref) { - char *middle; - if (ref[0] == '.' || ref[0] == '/') - return -1; - middle = strchr(ref, '/'); - if (!middle || !middle[1]) - return -1; - if (strchr(middle + 1, '/')) - return -1; - return 0; + int ch, level; + const char *cp = ref; + + level = 0; + while (1) { + while ((ch = *cp++) == '/') + ; /* tolerate duplicated slashes */ + if (!ch) + return -1; /* should not end with slashes */ + + /* we are at the beginning of the path component */ + if (ch == '.' || bad_ref_char(ch)) + return -1; + + /* scan the rest of the path component */ + while ((ch = *cp++) != 0) { + if (bad_ref_char(ch)) + return -1; + if (ch == '/') + break; + if (ch == '.' && *cp == '.') + return -1; + } + level++; + if (!ch) { + if (level < 2) + return -1; /* at least of form "heads/blah" */ + return 0; + } + } } int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)