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