X-Git-Url: https://git.octo.it/?a=blobdiff_plain;ds=sidebyside;f=refs.c;h=5a8cbd4ef386da60d3f7a2cac4563fc993e38b94;hb=a7a8d3786e6b60a472d0f88f778cacaead122a6c;hp=d4f3612487c0b9775d1baa382549b019beeef262;hpb=ca8db1424d1808a1f78bc9905efd267f7c154d8e;p=git.git diff --git a/refs.c b/refs.c index d4f36124..5a8cbd4e 100644 --- a/refs.c +++ b/refs.c @@ -7,40 +7,152 @@ /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 -int read_ref(const char *filename, unsigned char *sha1) +#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 = 0; - int ret = -1, fd; + int depth = MAXDEPTH, len; + char buffer[256]; + + for (;;) { + struct stat st; + char *buf; + int fd; - while ((fd = open(filename, O_RDONLY)) >= 0) { - char buffer[256]; - int len = read(fd, buffer, sizeof(buffer)-1); + if (--depth < 0) + return NULL; + /* Special case: non-existing file. + * Not having the refs/heads/new-branch is OK + * if we are writing into it, so is .git/HEAD + * that points at refs/heads/master still to be + * born. It is NOT OK if we are resolving for + * reading. + */ + if (lstat(path, &st) < 0) { + if (reading || errno != ENOENT) + return NULL; + memset(sha1, 0, 20); + return path; + } + + /* Follow "normalized" - ie "refs/.." symlinks by hand */ + if (S_ISLNK(st.st_mode)) { + len = readlink(path, buffer, sizeof(buffer)-1); + if (len >= 5 && !memcmp("refs/", buffer, 5)) { + path = git_path("%.*s", len, buffer); + continue; + } + } + + /* + * Anything else, just open it and try to use it as + * a ref + */ + fd = open(path, O_RDONLY); + if (fd < 0) + return NULL; + len = read(fd, buffer, sizeof(buffer)-1); close(fd); - if (len < 0) + + /* + * Is it a symbolic ref? + */ + if (len < 4 || memcmp("ref:", buffer, 4)) break; + buf = buffer + 4; + len -= 4; + while (len && isspace(*buf)) + buf++, len--; + while (len && isspace(buf[len-1])) + buf[--len] = 0; + path = git_path("%.*s", len, buf); + } + if (len < 40 || get_sha1_hex(buffer, sha1)) + return NULL; + return path; +} - buffer[len] = 0; - while (len && isspace(buffer[len-1])) - buffer[--len] = 0; +int create_symref(const char *git_HEAD, const char *refs_heads_master) +{ +#if USE_SYMLINK_HEAD + unlink(git_HEAD); + return symlink(refs_heads_master, git_HEAD); +#else + const char *lockpath; + char ref[1000]; + int fd, len, written; - if (!strncmp(buffer, "ref:", 4)) { - char *buf; - if (depth > MAXDEPTH) - break; - depth++; - buf = buffer + 4; - len -= 4; - while (len && isspace(*buf)) - buf++, len--; - filename = git_path("%.*s", len, buf); - continue; - } - if (len >= 40) - ret = get_sha1_hex(buffer, sha1); - break; + 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; } - return ret; + 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; +#endif +} + +int read_ref(const char *filename, unsigned char *sha1) +{ + if (resolve_ref(filename, sha1, 1)) + return 0; + return -1; } static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1))