refs.con commit Use resolve_ref() to implement read_ref(). (a876ed8)
   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
  10const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
  11{
  12        int depth = MAXDEPTH, len;
  13        char buffer[256];
  14
  15        for (;;) {
  16                struct stat st;
  17                char *buf;
  18                int fd;
  19
  20                if (--depth < 0)
  21                        return NULL;
  22
  23                /* Special case: non-existing file.
  24                 * Not having the refs/heads/new-branch is OK
  25                 * if we are writing into it, so is .git/HEAD
  26                 * that points at refs/heads/master still to be
  27                 * born.  It is NOT OK if we are resolving for
  28                 * reading.
  29                 */
  30                if (lstat(path, &st) < 0) {
  31                        if (reading || errno != ENOENT)
  32                                return NULL;
  33                        memset(sha1, 0, 20);
  34                        return path;
  35                }
  36
  37                /* Follow "normalized" - ie "refs/.." symlinks by hand */
  38                if (S_ISLNK(st.st_mode)) {
  39                        len = readlink(path, buffer, sizeof(buffer)-1);
  40                        if (len >= 5 && !memcmp("refs/", buffer, 5)) {
  41                                path = git_path("%.*s", len, buffer);
  42                                continue;
  43                        }
  44                }
  45
  46                /*
  47                 * Anything else, just open it and try to use it as
  48                 * a ref
  49                 */
  50                fd = open(path, O_RDONLY);
  51                if (fd < 0)
  52                        return NULL;
  53                len = read(fd, buffer, sizeof(buffer)-1);
  54                close(fd);
  55
  56                /*
  57                 * Is it a symbolic ref?
  58                 */
  59                if (len < 4 || memcmp("ref:", buffer, 4))
  60                        break;
  61                buf = buffer + 4;
  62                len -= 4;
  63                while (len && isspace(*buf))
  64                        buf++, len--;
  65                while (len && isspace(buf[len-1]))
  66                        buf[--len] = 0;
  67                path = git_path("%.*s", len, buf);
  68        }
  69        if (len < 40 || get_sha1_hex(buffer, sha1))
  70                return NULL;
  71        return path;
  72}
  73
  74int read_ref(const char *filename, unsigned char *sha1)
  75{
  76        if (resolve_ref(filename, sha1, 1))
  77                return 0;
  78        return -1;
  79}
  80
  81static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1))
  82{
  83        int retval = 0;
  84        DIR *dir = opendir(git_path("%s", base));
  85
  86        if (dir) {
  87                struct dirent *de;
  88                int baselen = strlen(base);
  89                char *path = xmalloc(baselen + 257);
  90
  91                if (!strncmp(base, "./", 2)) {
  92                        base += 2;
  93                        baselen -= 2;
  94                }
  95                memcpy(path, base, baselen);
  96                if (baselen && base[baselen-1] != '/')
  97                        path[baselen++] = '/';
  98
  99                while ((de = readdir(dir)) != NULL) {
 100                        unsigned char sha1[20];
 101                        struct stat st;
 102                        int namelen;
 103
 104                        if (de->d_name[0] == '.')
 105                                continue;
 106                        namelen = strlen(de->d_name);
 107                        if (namelen > 255)
 108                                continue;
 109                        memcpy(path + baselen, de->d_name, namelen+1);
 110                        if (stat(git_path("%s", path), &st) < 0)
 111                                continue;
 112                        if (S_ISDIR(st.st_mode)) {
 113                                retval = do_for_each_ref(path, fn);
 114                                if (retval)
 115                                        break;
 116                                continue;
 117                        }
 118                        if (read_ref(git_path("%s", path), sha1) < 0)
 119                                continue;
 120                        if (!has_sha1_file(sha1))
 121                                continue;
 122                        retval = fn(path, sha1);
 123                        if (retval)
 124                                break;
 125                }
 126                free(path);
 127                closedir(dir);
 128        }
 129        return retval;
 130}
 131
 132int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
 133{
 134        unsigned char sha1[20];
 135        if (!read_ref(git_path("HEAD"), sha1))
 136                return fn("HEAD", sha1);
 137        return 0;
 138}
 139
 140int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
 141{
 142        return do_for_each_ref("refs", fn);
 143}
 144
 145static char *ref_file_name(const char *ref)
 146{
 147        char *base = get_refs_directory();
 148        int baselen = strlen(base);
 149        int reflen = strlen(ref);
 150        char *ret = xmalloc(baselen + 2 + reflen);
 151        sprintf(ret, "%s/%s", base, ref);
 152        return ret;
 153}
 154
 155static char *ref_lock_file_name(const char *ref)
 156{
 157        char *base = get_refs_directory();
 158        int baselen = strlen(base);
 159        int reflen = strlen(ref);
 160        char *ret = xmalloc(baselen + 7 + reflen);
 161        sprintf(ret, "%s/%s.lock", base, ref);
 162        return ret;
 163}
 164
 165int get_ref_sha1(const char *ref, unsigned char *sha1)
 166{
 167        const char *filename;
 168
 169        if (check_ref_format(ref))
 170                return -1;
 171        filename = git_path("refs/%s", ref);
 172        return read_ref(filename, sha1);
 173}
 174
 175static int lock_ref_file(const char *filename, const char *lock_filename,
 176                         const unsigned char *old_sha1)
 177{
 178        int fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
 179        unsigned char current_sha1[20];
 180        int retval;
 181        if (fd < 0) {
 182                return error("Couldn't open lock file for %s: %s",
 183                             filename, strerror(errno));
 184        }
 185        retval = read_ref(filename, current_sha1);
 186        if (old_sha1) {
 187                if (retval) {
 188                        close(fd);
 189                        unlink(lock_filename);
 190                        return error("Could not read the current value of %s",
 191                                     filename);
 192                }
 193                if (memcmp(current_sha1, old_sha1, 20)) {
 194                        close(fd);
 195                        unlink(lock_filename);
 196                        error("The current value of %s is %s",
 197                              filename, sha1_to_hex(current_sha1));
 198                        return error("Expected %s",
 199                                     sha1_to_hex(old_sha1));
 200                }
 201        } else {
 202                if (!retval) {
 203                        close(fd);
 204                        unlink(lock_filename);
 205                        return error("Unexpectedly found a value of %s for %s",
 206                                     sha1_to_hex(current_sha1), filename);
 207                }
 208        }
 209        return fd;
 210}
 211
 212int lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
 213{
 214        char *filename;
 215        char *lock_filename;
 216        int retval;
 217        if (check_ref_format(ref))
 218                return -1;
 219        filename = ref_file_name(ref);
 220        lock_filename = ref_lock_file_name(ref);
 221        retval = lock_ref_file(filename, lock_filename, old_sha1);
 222        free(filename);
 223        free(lock_filename);
 224        return retval;
 225}
 226
 227static int write_ref_file(const char *filename,
 228                          const char *lock_filename, int fd,
 229                          const unsigned char *sha1)
 230{
 231        char *hex = sha1_to_hex(sha1);
 232        char term = '\n';
 233        if (write(fd, hex, 40) < 40 ||
 234            write(fd, &term, 1) < 1) {
 235                error("Couldn't write %s\n", filename);
 236                close(fd);
 237                return -1;
 238        }
 239        close(fd);
 240        rename(lock_filename, filename);
 241        return 0;
 242}
 243
 244int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
 245{
 246        char *filename;
 247        char *lock_filename;
 248        int retval;
 249        if (fd < 0)
 250                return -1;
 251        if (check_ref_format(ref))
 252                return -1;
 253        filename = ref_file_name(ref);
 254        lock_filename = ref_lock_file_name(ref);
 255        retval = write_ref_file(filename, lock_filename, fd, sha1);
 256        free(filename);
 257        free(lock_filename);
 258        return retval;
 259}
 260
 261int check_ref_format(const char *ref)
 262{
 263        char *middle;
 264        if (ref[0] == '.' || ref[0] == '/')
 265                return -1;
 266        middle = strchr(ref, '/');
 267        if (!middle || !middle[1])
 268                return -1;
 269        if (strchr(middle + 1, '/'))
 270                return -1;
 271        return 0;
 272}
 273
 274int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
 275{
 276        char *filename;
 277        char *lock_filename;
 278        int fd;
 279        int retval;
 280        if (check_ref_format(ref))
 281                return -1;
 282        filename = ref_file_name(ref);
 283        lock_filename = ref_lock_file_name(ref);
 284        fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
 285        if (fd < 0) {
 286                error("Writing %s", lock_filename);
 287                perror("Open");
 288        }
 289        retval = write_ref_file(filename, lock_filename, fd, sha1);
 290        free(filename);
 291        free(lock_filename);
 292        return retval;
 293}