update-ref.con commit [PATCH] HTTP partial transfer support fix. (4fa2197)
   1#include "cache.h"
   2#include "refs.h"
   3#include <ctype.h>
   4
   5static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]";
   6
   7#define MAXDEPTH 5
   8
   9static const char *resolve_ref(const char *path, unsigned char *sha1)
  10{
  11        int depth = MAXDEPTH, len;
  12        char buffer[256];
  13
  14        for (;;) {
  15                struct stat st;
  16                int fd;
  17
  18                if (--depth < 0)
  19                        return NULL;
  20
  21                /* Special case: non-existing file */
  22                if (lstat(path, &st) < 0) {
  23                        if (errno != ENOENT)
  24                                return NULL;
  25                        memset(sha1, 0, 20);
  26                        return path;
  27                }
  28
  29                /* Follow "normalized" - ie "refs/.." symlinks by hand */
  30                if (S_ISLNK(st.st_mode)) {
  31                        len = readlink(path, buffer, sizeof(buffer)-1);
  32                        if (len >= 5 && !memcmp("refs/", buffer, 5)) {
  33                                path = git_path("%.*s", len, buffer);
  34                                continue;
  35                        }
  36                }
  37
  38                /*
  39                 * Anything else, just open it and try to use it as
  40                 * a ref
  41                 */
  42                fd = open(path, O_RDONLY);
  43                if (fd < 0)
  44                        return NULL;
  45                len = read(fd, buffer, sizeof(buffer)-1);
  46                close(fd);
  47                break;
  48        }
  49        if (len < 40 || get_sha1_hex(buffer, sha1))
  50                return NULL;
  51        return path;
  52}
  53
  54static int re_verify(const char *path, unsigned char *oldsha1, unsigned char *currsha1)
  55{
  56        char buf[40];
  57        int fd = open(path, O_RDONLY), nr;
  58        if (fd < 0)
  59                return -1;
  60        nr = read(fd, buf, 40);
  61        close(fd);
  62        if (nr != 40 || get_sha1_hex(buf, currsha1) < 0)
  63                return -1;
  64        return memcmp(oldsha1, currsha1, 20) ? -1 : 0;
  65}
  66
  67int main(int argc, char **argv)
  68{
  69        char *hex;
  70        const char *refname, *value, *oldval, *path, *lockpath;
  71        unsigned char sha1[20], oldsha1[20], currsha1[20];
  72        int fd, written;
  73
  74        setup_git_directory();
  75        if (argc < 3 || argc > 4)
  76                usage(git_update_ref_usage);
  77
  78        refname = argv[1];
  79        value = argv[2];
  80        oldval = argv[3];
  81        if (get_sha1(value, sha1) < 0)
  82                die("%s: not a valid SHA1", value);
  83        memset(oldsha1, 0, 20);
  84        if (oldval && get_sha1(oldval, oldsha1) < 0)
  85                die("%s: not a valid old SHA1", oldval);
  86
  87        path = resolve_ref(git_path("%s", refname), currsha1);
  88        if (!path)
  89                die("No such ref: %s", refname);
  90
  91        if (oldval) {
  92                if (memcmp(currsha1, oldsha1, 20))
  93                        die("Ref %s changed to %s", refname, sha1_to_hex(currsha1));
  94                /* Nothing to do? */
  95                if (!memcmp(oldsha1, sha1, 20))
  96                        exit(0);
  97        }
  98        path = strdup(path);
  99        lockpath = mkpath("%s.lock", path);
 100
 101        fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
 102        if (fd < 0)
 103                die("Unable to create %s", lockpath);
 104        hex = sha1_to_hex(sha1);
 105        hex[40] = '\n';
 106        written = write(fd, hex, 41);
 107        close(fd);
 108        if (written != 41) {
 109                unlink(lockpath);
 110                die("Unable to write to %s", lockpath);
 111        }
 112
 113        /*
 114         * Re-read the ref after getting the lock to verify
 115         */
 116        if (oldval && re_verify(path, oldsha1, currsha1) < 0) {
 117                unlink(lockpath);
 118                die("Ref lock failed");
 119        }
 120
 121        /*
 122         * Finally, replace the old ref with the new one
 123         */
 124        if (rename(lockpath, path) < 0) {
 125                unlink(lockpath);
 126                die("Unable to create %s", path);
 127        }
 128        return 0;
 129}