refs.con commit [PATCH] Allow reading "symbolic refs" that point to other refs (ca8db14)
   1#include "refs.h"
   2#include "cache.h"
   3
   4#include <errno.h>
   5#include <ctype.h>
   6
   7/* We allow "recursive" symbolic refs. Only within reason, though */
   8#define MAXDEPTH 5
   9
  10int read_ref(const char *filename, unsigned char *sha1)
  11{
  12        int depth = 0;
  13        int ret = -1, fd;
  14
  15        while ((fd = open(filename, O_RDONLY)) >= 0) {
  16                char buffer[256];
  17                int len = read(fd, buffer, sizeof(buffer)-1);
  18
  19                close(fd);
  20                if (len < 0)
  21                        break;
  22
  23                buffer[len] = 0;
  24                while (len && isspace(buffer[len-1]))
  25                        buffer[--len] = 0;
  26
  27                if (!strncmp(buffer, "ref:", 4)) {
  28                        char *buf;
  29                        if (depth > MAXDEPTH)
  30                                break;
  31                        depth++;
  32                        buf = buffer + 4;
  33                        len -= 4;
  34                        while (len && isspace(*buf))
  35                                buf++, len--;
  36                        filename = git_path("%.*s", len, buf);
  37                        continue;
  38                }
  39                if (len >= 40)
  40                        ret = get_sha1_hex(buffer, sha1);
  41                break;
  42        }
  43        return ret;
  44}
  45
  46static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1))
  47{
  48        int retval = 0;
  49        DIR *dir = opendir(git_path("%s", base));
  50
  51        if (dir) {
  52                struct dirent *de;
  53                int baselen = strlen(base);
  54                char *path = xmalloc(baselen + 257);
  55
  56                if (!strncmp(base, "./", 2)) {
  57                        base += 2;
  58                        baselen -= 2;
  59                }
  60                memcpy(path, base, baselen);
  61                if (baselen && base[baselen-1] != '/')
  62                        path[baselen++] = '/';
  63
  64                while ((de = readdir(dir)) != NULL) {
  65                        unsigned char sha1[20];
  66                        struct stat st;
  67                        int namelen;
  68
  69                        if (de->d_name[0] == '.')
  70                                continue;
  71                        namelen = strlen(de->d_name);
  72                        if (namelen > 255)
  73                                continue;
  74                        memcpy(path + baselen, de->d_name, namelen+1);
  75                        if (stat(git_path("%s", path), &st) < 0)
  76                                continue;
  77                        if (S_ISDIR(st.st_mode)) {
  78                                retval = do_for_each_ref(path, fn);
  79                                if (retval)
  80                                        break;
  81                                continue;
  82                        }
  83                        if (read_ref(git_path("%s", path), sha1) < 0)
  84                                continue;
  85                        if (!has_sha1_file(sha1))
  86                                continue;
  87                        retval = fn(path, sha1);
  88                        if (retval)
  89                                break;
  90                }
  91                free(path);
  92                closedir(dir);
  93        }
  94        return retval;
  95}
  96
  97int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
  98{
  99        unsigned char sha1[20];
 100        if (!read_ref(git_path("HEAD"), sha1))
 101                return fn("HEAD", sha1);
 102        return 0;
 103}
 104
 105int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
 106{
 107        return do_for_each_ref("refs", fn);
 108}
 109
 110static char *ref_file_name(const char *ref)
 111{
 112        char *base = get_refs_directory();
 113        int baselen = strlen(base);
 114        int reflen = strlen(ref);
 115        char *ret = xmalloc(baselen + 2 + reflen);
 116        sprintf(ret, "%s/%s", base, ref);
 117        return ret;
 118}
 119
 120static char *ref_lock_file_name(const char *ref)
 121{
 122        char *base = get_refs_directory();
 123        int baselen = strlen(base);
 124        int reflen = strlen(ref);
 125        char *ret = xmalloc(baselen + 7 + reflen);
 126        sprintf(ret, "%s/%s.lock", base, ref);
 127        return ret;
 128}
 129
 130int get_ref_sha1(const char *ref, unsigned char *sha1)
 131{
 132        const char *filename;
 133
 134        if (check_ref_format(ref))
 135                return -1;
 136        filename = git_path("refs/%s", ref);
 137        return read_ref(filename, sha1);
 138}
 139
 140static int lock_ref_file(const char *filename, const char *lock_filename,
 141                         const unsigned char *old_sha1)
 142{
 143        int fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
 144        unsigned char current_sha1[20];
 145        int retval;
 146        if (fd < 0) {
 147                return error("Couldn't open lock file for %s: %s",
 148                             filename, strerror(errno));
 149        }
 150        retval = read_ref(filename, current_sha1);
 151        if (old_sha1) {
 152                if (retval) {
 153                        close(fd);
 154                        unlink(lock_filename);
 155                        return error("Could not read the current value of %s",
 156                                     filename);
 157                }
 158                if (memcmp(current_sha1, old_sha1, 20)) {
 159                        close(fd);
 160                        unlink(lock_filename);
 161                        error("The current value of %s is %s",
 162                              filename, sha1_to_hex(current_sha1));
 163                        return error("Expected %s",
 164                                     sha1_to_hex(old_sha1));
 165                }
 166        } else {
 167                if (!retval) {
 168                        close(fd);
 169                        unlink(lock_filename);
 170                        return error("Unexpectedly found a value of %s for %s",
 171                                     sha1_to_hex(current_sha1), filename);
 172                }
 173        }
 174        return fd;
 175}
 176
 177int lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
 178{
 179        char *filename;
 180        char *lock_filename;
 181        int retval;
 182        if (check_ref_format(ref))
 183                return -1;
 184        filename = ref_file_name(ref);
 185        lock_filename = ref_lock_file_name(ref);
 186        retval = lock_ref_file(filename, lock_filename, old_sha1);
 187        free(filename);
 188        free(lock_filename);
 189        return retval;
 190}
 191
 192static int write_ref_file(const char *filename,
 193                          const char *lock_filename, int fd,
 194                          const unsigned char *sha1)
 195{
 196        char *hex = sha1_to_hex(sha1);
 197        char term = '\n';
 198        if (write(fd, hex, 40) < 40 ||
 199            write(fd, &term, 1) < 1) {
 200                error("Couldn't write %s\n", filename);
 201                close(fd);
 202                return -1;
 203        }
 204        close(fd);
 205        rename(lock_filename, filename);
 206        return 0;
 207}
 208
 209int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
 210{
 211        char *filename;
 212        char *lock_filename;
 213        int retval;
 214        if (fd < 0)
 215                return -1;
 216        if (check_ref_format(ref))
 217                return -1;
 218        filename = ref_file_name(ref);
 219        lock_filename = ref_lock_file_name(ref);
 220        retval = write_ref_file(filename, lock_filename, fd, sha1);
 221        free(filename);
 222        free(lock_filename);
 223        return retval;
 224}
 225
 226int check_ref_format(const char *ref)
 227{
 228        char *middle;
 229        if (ref[0] == '.' || ref[0] == '/')
 230                return -1;
 231        middle = strchr(ref, '/');
 232        if (!middle || !middle[1])
 233                return -1;
 234        if (strchr(middle + 1, '/'))
 235                return -1;
 236        return 0;
 237}
 238
 239int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
 240{
 241        char *filename;
 242        char *lock_filename;
 243        int fd;
 244        int retval;
 245        if (check_ref_format(ref))
 246                return -1;
 247        filename = ref_file_name(ref);
 248        lock_filename = ref_lock_file_name(ref);
 249        fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
 250        if (fd < 0) {
 251                error("Writing %s", lock_filename);
 252                perror("Open");
 253        }
 254        retval = write_ref_file(filename, lock_filename, fd, sha1);
 255        free(filename);
 256        free(lock_filename);
 257        return retval;
 258}