d05f7d5ec42a088b29d3cac14d42b416013aedf7
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include <sys/wait.h>
   5
   6static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
   7
   8static const char *unpacker = "git-unpack-objects";
   9
  10static int show_ref(const char *path, const unsigned char *sha1)
  11{
  12        packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
  13        return 0;
  14}
  15
  16static void write_head_info(void)
  17{
  18        for_each_ref(show_ref);
  19}
  20
  21struct command {
  22        struct command *next;
  23        unsigned char old_sha1[20];
  24        unsigned char new_sha1[20];
  25        char ref_name[0];
  26};
  27
  28struct command *commands = NULL;
  29
  30static int is_all_zeroes(const char *hex)
  31{
  32        int i;
  33        for (i = 0; i < 40; i++)
  34                if (*hex++ != '0')
  35                        return 0;
  36        return 1;
  37}
  38
  39static int verify_old_ref(const char *name, char *hex_contents)
  40{
  41        int fd, ret;
  42        char buffer[60];
  43
  44        if (is_all_zeroes(hex_contents))
  45                return 0;
  46        fd = open(name, O_RDONLY);
  47        if (fd < 0)
  48                return -1;
  49        ret = read(fd, buffer, 40);
  50        close(fd);
  51        if (ret != 40)
  52                return -1;
  53        if (memcmp(buffer, hex_contents, 40))
  54                return -1;
  55        return 0;
  56}
  57
  58static void update(const char *name, unsigned char *old_sha1, unsigned char *new_sha1)
  59{
  60        char new_hex[60], *old_hex, *lock_name;
  61        int newfd, namelen, written;
  62
  63        namelen = strlen(name);
  64        lock_name = xmalloc(namelen + 10);
  65        memcpy(lock_name, name, namelen);
  66        memcpy(lock_name + namelen, ".lock", 6);
  67
  68        strcpy(new_hex, sha1_to_hex(new_sha1));
  69        old_hex = sha1_to_hex(old_sha1);
  70        if (!has_sha1_file(new_sha1))
  71                die("unpack should have generated %s, but I can't find it!", new_hex);
  72
  73        newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0644);
  74        if (newfd < 0)
  75                die("unable to create %s (%s)", lock_name, strerror(errno));
  76
  77        /* Write the ref with an ending '\n' */
  78        new_hex[40] = '\n';
  79        new_hex[41] = 0;
  80        written = write(newfd, new_hex, 41);
  81        /* Remove the '\n' again */
  82        new_hex[40] = 0;
  83
  84        close(newfd);
  85        if (written != 41) {
  86                unlink(lock_name);
  87                die("unable to write %s", lock_name);
  88        }
  89        if (verify_old_ref(name, old_hex) < 0) {
  90                unlink(lock_name);
  91                die("%s changed during push", name);
  92        }
  93        if (rename(lock_name, name) < 0) {
  94                unlink(lock_name);
  95                die("unable to replace %s", name);
  96        }
  97        fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
  98}
  99
 100
 101/*
 102 * This gets called after(if) we've successfully
 103 * unpacked the data payload.
 104 */
 105static void execute_commands(void)
 106{
 107        struct command *cmd = commands;
 108
 109        while (cmd) {
 110                update(cmd->ref_name, cmd->old_sha1, cmd->new_sha1);
 111                cmd = cmd->next;
 112        }
 113}
 114
 115static void read_head_info(void)
 116{
 117        struct command **p = &commands;
 118        for (;;) {
 119                static char line[1000];
 120                unsigned char old_sha1[20], new_sha1[20];
 121                struct command *cmd;
 122                int len;
 123
 124                len = packet_read_line(0, line, sizeof(line));
 125                if (!len)
 126                        break;
 127                if (line[len-1] == '\n')
 128                        line[--len] = 0;
 129                if (len < 83 ||
 130                    line[40] != ' ' ||
 131                    line[81] != ' ' ||
 132                    get_sha1_hex(line, old_sha1) ||
 133                    get_sha1_hex(line + 41, new_sha1))
 134                        die("protocol error: expected old/new/ref, got '%s'", line);
 135                cmd = xmalloc(sizeof(struct command) + len - 80);
 136                memcpy(cmd->old_sha1, old_sha1, 20);
 137                memcpy(cmd->new_sha1, new_sha1, 20);
 138                memcpy(cmd->ref_name, line + 82, len - 81);
 139                cmd->next = NULL;
 140                *p = cmd;
 141                p = &cmd->next;
 142        }
 143}
 144
 145static void unpack(void)
 146{
 147        pid_t pid = fork();
 148
 149        if (pid < 0)
 150                die("unpack fork failed");
 151        if (!pid) {
 152                execlp(unpacker, unpacker, NULL);
 153                die("unpack execute failed");
 154        }
 155
 156        for (;;) {
 157                int status, code;
 158                int retval = waitpid(pid, &status, 0);
 159
 160                if (retval < 0) {
 161                        if (errno == EINTR)
 162                                continue;
 163                        die("waitpid failed (%s)", strerror(retval));
 164                }
 165                if (retval != pid)
 166                        die("waitpid is confused");
 167                if (WIFSIGNALED(status))
 168                        die("%s died of signal %d", unpacker, WTERMSIG(status));
 169                if (!WIFEXITED(status))
 170                        die("%s died out of really strange complications", unpacker);
 171                code = WEXITSTATUS(status);
 172                if (code)
 173                        die("%s exited with error code %d", unpacker, code);
 174                return;
 175        }
 176}
 177
 178int main(int argc, char **argv)
 179{
 180        int i;
 181        const char *dir = NULL;
 182
 183        argv++;
 184        for (i = 1; i < argc; i++) {
 185                const char *arg = *argv++;
 186
 187                if (*arg == '-') {
 188                        /* Do flag handling here */
 189                        usage(receive_pack_usage);
 190                }
 191                if (dir)
 192                        usage(receive_pack_usage);
 193                dir = arg;
 194        }
 195        if (!dir)
 196                usage(receive_pack_usage);
 197
 198        /* chdir to the directory. If that fails, try appending ".git" */
 199        if (chdir(dir) < 0) {
 200                static char path[PATH_MAX];
 201                snprintf(path, sizeof(path), "%s.git", dir);
 202                if (chdir(path) < 0)
 203                        die("unable to cd to %s", dir);
 204        }
 205
 206        /* If we have a ".git" directory, chdir to it */
 207        chdir(".git");
 208        setenv("GIT_DIR", ".", 1);
 209
 210        if (access("objects", X_OK) < 0 || access("refs/heads", X_OK) < 0)
 211                die("%s doesn't appear to be a git directory", dir);
 212        write_head_info();
 213
 214        /* EOF */
 215        packet_flush(1);
 216
 217        read_head_info();
 218        if (commands) {
 219                unpack();
 220                execute_commands();
 221        }
 222        return 0;
 223}