receive-pack.con commit Fix buggy ref recording (7c1a278)
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include "run-command.h"
   5
   6static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
   7
   8static const char *unpacker[] = { "unpack-objects", NULL };
   9
  10static int report_status;
  11
  12static char capabilities[] = "report-status";
  13static int capabilities_sent;
  14
  15static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
  16{
  17        if (capabilities_sent)
  18                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
  19        else
  20                packet_write(1, "%s %s%c%s\n",
  21                             sha1_to_hex(sha1), path, 0, capabilities);
  22        capabilities_sent = 1;
  23        return 0;
  24}
  25
  26static void write_head_info(void)
  27{
  28        for_each_ref(show_ref, NULL);
  29        if (!capabilities_sent)
  30                show_ref("capabilities^{}", null_sha1, 0, NULL);
  31
  32}
  33
  34struct command {
  35        struct command *next;
  36        const char *error_string;
  37        unsigned char old_sha1[20];
  38        unsigned char new_sha1[20];
  39        char ref_name[FLEX_ARRAY]; /* more */
  40};
  41
  42static struct command *commands;
  43
  44static int is_all_zeroes(const char *hex)
  45{
  46        int i;
  47        for (i = 0; i < 40; i++)
  48                if (*hex++ != '0')
  49                        return 0;
  50        return 1;
  51}
  52
  53static int verify_old_ref(const char *name, char *hex_contents)
  54{
  55        int fd, ret;
  56        char buffer[60];
  57
  58        if (is_all_zeroes(hex_contents))
  59                return 0;
  60        fd = open(name, O_RDONLY);
  61        if (fd < 0)
  62                return -1;
  63        ret = read(fd, buffer, 40);
  64        close(fd);
  65        if (ret != 40)
  66                return -1;
  67        if (memcmp(buffer, hex_contents, 40))
  68                return -1;
  69        return 0;
  70}
  71
  72static char update_hook[] = "hooks/update";
  73
  74static int run_update_hook(const char *refname,
  75                           char *old_hex, char *new_hex)
  76{
  77        int code;
  78
  79        if (access(update_hook, X_OK) < 0)
  80                return 0;
  81        code = run_command(update_hook, refname, old_hex, new_hex, NULL);
  82        switch (code) {
  83        case 0:
  84                return 0;
  85        case -ERR_RUN_COMMAND_FORK:
  86                return error("hook fork failed");
  87        case -ERR_RUN_COMMAND_EXEC:
  88                return error("hook execute failed");
  89        case -ERR_RUN_COMMAND_WAITPID:
  90                return error("waitpid failed");
  91        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
  92                return error("waitpid is confused");
  93        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
  94                return error("%s died of signal", update_hook);
  95        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
  96                return error("%s died strangely", update_hook);
  97        default:
  98                error("%s exited with error code %d", update_hook, -code);
  99                return -code;
 100        }
 101}
 102
 103static int update(struct command *cmd)
 104{
 105        const char *name = cmd->ref_name;
 106        unsigned char *old_sha1 = cmd->old_sha1;
 107        unsigned char *new_sha1 = cmd->new_sha1;
 108        char new_hex[60], *old_hex, *lock_name;
 109        int newfd, namelen, written;
 110
 111        cmd->error_string = NULL;
 112        if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) {
 113                cmd->error_string = "funny refname";
 114                return error("refusing to create funny ref '%s' locally",
 115                             name);
 116        }
 117
 118        namelen = strlen(name);
 119        lock_name = xmalloc(namelen + 10);
 120        memcpy(lock_name, name, namelen);
 121        memcpy(lock_name + namelen, ".lock", 6);
 122
 123        strcpy(new_hex, sha1_to_hex(new_sha1));
 124        old_hex = sha1_to_hex(old_sha1);
 125        if (!has_sha1_file(new_sha1)) {
 126                cmd->error_string = "bad pack";
 127                return error("unpack should have generated %s, "
 128                             "but I can't find it!", new_hex);
 129        }
 130        safe_create_leading_directories(lock_name);
 131
 132        newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
 133        if (newfd < 0) {
 134                cmd->error_string = "can't lock";
 135                return error("unable to create %s (%s)",
 136                             lock_name, strerror(errno));
 137        }
 138
 139        /* Write the ref with an ending '\n' */
 140        new_hex[40] = '\n';
 141        new_hex[41] = 0;
 142        written = write(newfd, new_hex, 41);
 143        /* Remove the '\n' again */
 144        new_hex[40] = 0;
 145
 146        close(newfd);
 147        if (written != 41) {
 148                unlink(lock_name);
 149                cmd->error_string = "can't write";
 150                return error("unable to write %s", lock_name);
 151        }
 152        if (verify_old_ref(name, old_hex) < 0) {
 153                unlink(lock_name);
 154                cmd->error_string = "raced";
 155                return error("%s changed during push", name);
 156        }
 157        if (run_update_hook(name, old_hex, new_hex)) {
 158                unlink(lock_name);
 159                cmd->error_string = "hook declined";
 160                return error("hook declined to update %s", name);
 161        }
 162        else if (rename(lock_name, name) < 0) {
 163                unlink(lock_name);
 164                cmd->error_string = "can't rename";
 165                return error("unable to replace %s", name);
 166        }
 167        else {
 168                fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
 169                return 0;
 170        }
 171}
 172
 173static char update_post_hook[] = "hooks/post-update";
 174
 175static void run_update_post_hook(struct command *cmd)
 176{
 177        struct command *cmd_p;
 178        int argc;
 179        const char **argv;
 180
 181        if (access(update_post_hook, X_OK) < 0)
 182                return;
 183        for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
 184                if (cmd_p->error_string)
 185                        continue;
 186                argc++;
 187        }
 188        argv = xmalloc(sizeof(*argv) * (1 + argc));
 189        argv[0] = update_post_hook;
 190
 191        for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
 192                char *p;
 193                if (cmd_p->error_string)
 194                        continue;
 195                p = xmalloc(strlen(cmd_p->ref_name) + 1);
 196                strcpy(p, cmd_p->ref_name);
 197                argv[argc] = p;
 198                argc++;
 199        }
 200        argv[argc] = NULL;
 201        run_command_v_opt(argc, argv, RUN_COMMAND_NO_STDIO);
 202}
 203
 204/*
 205 * This gets called after(if) we've successfully
 206 * unpacked the data payload.
 207 */
 208static void execute_commands(void)
 209{
 210        struct command *cmd = commands;
 211
 212        while (cmd) {
 213                update(cmd);
 214                cmd = cmd->next;
 215        }
 216        run_update_post_hook(commands);
 217}
 218
 219static void read_head_info(void)
 220{
 221        struct command **p = &commands;
 222        for (;;) {
 223                static char line[1000];
 224                unsigned char old_sha1[20], new_sha1[20];
 225                struct command *cmd;
 226                char *refname;
 227                int len, reflen;
 228
 229                len = packet_read_line(0, line, sizeof(line));
 230                if (!len)
 231                        break;
 232                if (line[len-1] == '\n')
 233                        line[--len] = 0;
 234                if (len < 83 ||
 235                    line[40] != ' ' ||
 236                    line[81] != ' ' ||
 237                    get_sha1_hex(line, old_sha1) ||
 238                    get_sha1_hex(line + 41, new_sha1))
 239                        die("protocol error: expected old/new/ref, got '%s'",
 240                            line);
 241
 242                refname = line + 82;
 243                reflen = strlen(refname);
 244                if (reflen + 82 < len) {
 245                        if (strstr(refname + reflen + 1, "report-status"))
 246                                report_status = 1;
 247                }
 248                cmd = xmalloc(sizeof(struct command) + len - 80);
 249                hashcpy(cmd->old_sha1, old_sha1);
 250                hashcpy(cmd->new_sha1, new_sha1);
 251                memcpy(cmd->ref_name, line + 82, len - 81);
 252                cmd->error_string = "n/a (unpacker error)";
 253                cmd->next = NULL;
 254                *p = cmd;
 255                p = &cmd->next;
 256        }
 257}
 258
 259static const char *unpack(int *error_code)
 260{
 261        int code = run_command_v_opt(1, unpacker, RUN_GIT_CMD);
 262
 263        *error_code = 0;
 264        switch (code) {
 265        case 0:
 266                return NULL;
 267        case -ERR_RUN_COMMAND_FORK:
 268                return "unpack fork failed";
 269        case -ERR_RUN_COMMAND_EXEC:
 270                return "unpack execute failed";
 271        case -ERR_RUN_COMMAND_WAITPID:
 272                return "waitpid failed";
 273        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 274                return "waitpid is confused";
 275        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 276                return "unpacker died of signal";
 277        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 278                return "unpacker died strangely";
 279        default:
 280                *error_code = -code;
 281                return "unpacker exited with error code";
 282        }
 283}
 284
 285static void report(const char *unpack_status)
 286{
 287        struct command *cmd;
 288        packet_write(1, "unpack %s\n",
 289                     unpack_status ? unpack_status : "ok");
 290        for (cmd = commands; cmd; cmd = cmd->next) {
 291                if (!cmd->error_string)
 292                        packet_write(1, "ok %s\n",
 293                                     cmd->ref_name);
 294                else
 295                        packet_write(1, "ng %s %s\n",
 296                                     cmd->ref_name, cmd->error_string);
 297        }
 298        packet_flush(1);
 299}
 300
 301int main(int argc, char **argv)
 302{
 303        int i;
 304        char *dir = NULL;
 305
 306        argv++;
 307        for (i = 1; i < argc; i++) {
 308                char *arg = *argv++;
 309
 310                if (*arg == '-') {
 311                        /* Do flag handling here */
 312                        usage(receive_pack_usage);
 313                }
 314                if (dir)
 315                        usage(receive_pack_usage);
 316                dir = arg;
 317        }
 318        if (!dir)
 319                usage(receive_pack_usage);
 320
 321        if(!enter_repo(dir, 0))
 322                die("'%s': unable to chdir or not a git archive", dir);
 323
 324        write_head_info();
 325
 326        /* EOF */
 327        packet_flush(1);
 328
 329        read_head_info();
 330        if (commands) {
 331                int code;
 332                const char *unpack_status = unpack(&code);
 333                if (!unpack_status)
 334                        execute_commands();
 335                if (report_status)
 336                        report(unpack_status);
 337        }
 338        return 0;
 339}