upload-pack.con commit upload-pack: avoid sending an incomplete pack upon failure (b1c71b7)
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include "tag.h"
   5#include "object.h"
   6#include "commit.h"
   7#include "exec_cmd.h"
   8#include <signal.h>
   9#include <sys/poll.h>
  10#include <sys/wait.h>
  11
  12static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
  13
  14#define THEY_HAVE (1U << 0)
  15#define OUR_REF (1U << 1)
  16#define WANTED (1U << 2)
  17#define MAX_HAS 256
  18#define MAX_NEEDS 256
  19static int nr_has = 0, nr_needs = 0, multi_ack = 0, nr_our_refs = 0;
  20static int use_thin_pack = 0;
  21static unsigned char has_sha1[MAX_HAS][20];
  22static unsigned char needs_sha1[MAX_NEEDS][20];
  23static unsigned int timeout = 0;
  24
  25static void reset_timeout(void)
  26{
  27        alarm(timeout);
  28}
  29
  30static int strip(char *line, int len)
  31{
  32        if (len && line[len-1] == '\n')
  33                line[--len] = 0;
  34        return len;
  35}
  36
  37static void create_pack_file(void)
  38{
  39        /* Pipes between rev-list to pack-objects and pack-objects to us. */
  40        int lp_pipe[2], pu_pipe[2];
  41        pid_t pid_rev_list, pid_pack_objects;
  42        int create_full_pack = (nr_our_refs == nr_needs && !nr_has);
  43        char data[8193];
  44        int buffered = -1;
  45
  46        if (pipe(lp_pipe) < 0)
  47                die("git-upload-pack: unable to create pipe");
  48        pid_rev_list = fork();
  49        if (pid_rev_list < 0)
  50                die("git-upload-pack: unable to fork git-rev-list");
  51
  52        if (!pid_rev_list) {
  53                int i;
  54                int args;
  55                const char **argv;
  56                char *buf;
  57                char **p;
  58
  59                if (create_full_pack) {
  60                        args = 10;
  61                        use_thin_pack = 0; /* no point doing it */
  62                }
  63                else
  64                        args = nr_has + nr_needs + 5;
  65                p = xmalloc(args * sizeof(char *));
  66                argv = (const char **) p;
  67                buf = xmalloc(args * 45);
  68
  69                dup2(lp_pipe[1], 1);
  70                close(0);
  71                close(lp_pipe[0]);
  72                close(lp_pipe[1]);
  73                *p++ = "rev-list";
  74                *p++ = use_thin_pack ? "--objects-edge" : "--objects";
  75                if (create_full_pack || MAX_NEEDS <= nr_needs)
  76                        *p++ = "--all";
  77                else {
  78                        for (i = 0; i < nr_needs; i++) {
  79                                *p++ = buf;
  80                                memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
  81                                buf += 41;
  82                        }
  83                }
  84                if (!create_full_pack)
  85                        for (i = 0; i < nr_has; i++) {
  86                                *p++ = buf;
  87                                *buf++ = '^';
  88                                memcpy(buf, sha1_to_hex(has_sha1[i]), 41);
  89                                buf += 41;
  90                        }
  91                *p++ = NULL;
  92                execv_git_cmd(argv);
  93                die("git-upload-pack: unable to exec git-rev-list");
  94        }
  95
  96        if (pipe(pu_pipe) < 0)
  97                die("git-upload-pack: unable to create pipe");
  98        pid_pack_objects = fork();
  99        if (pid_pack_objects < 0) {
 100                /* daemon sets things up to ignore TERM */
 101                kill(pid_rev_list, SIGKILL);
 102                die("git-upload-pack: unable to fork git-pack-objects");
 103        }
 104        if (!pid_pack_objects) {
 105                dup2(lp_pipe[0], 0);
 106                dup2(pu_pipe[1], 1);
 107
 108                close(lp_pipe[0]);
 109                close(lp_pipe[1]);
 110                close(pu_pipe[0]);
 111                close(pu_pipe[1]);
 112                execl_git_cmd("pack-objects", "--stdout", NULL);
 113                kill(pid_rev_list, SIGKILL);
 114                die("git-upload-pack: unable to exec git-pack-objects");
 115        }
 116
 117        close(lp_pipe[0]);
 118        close(lp_pipe[1]);
 119
 120        /* We read from pu_pipe[0] to capture the pack data.
 121         */
 122        close(pu_pipe[1]);
 123
 124        while (1) {
 125                const char *who;
 126                struct pollfd pfd[2];
 127                pid_t pid;
 128                int status;
 129                ssize_t sz;
 130                int pu, pollsize;
 131
 132                pollsize = 0;
 133                pu = -1;
 134
 135                if (0 <= pu_pipe[0]) {
 136                        pfd[pollsize].fd = pu_pipe[0];
 137                        pfd[pollsize].events = POLLIN;
 138                        pu = pollsize;
 139                        pollsize++;
 140                }
 141
 142                if (pollsize) {
 143                        if (poll(pfd, pollsize, -1) < 0) {
 144                                if (errno != EINTR) {
 145                                        error("poll failed, resuming: %s",
 146                                              strerror(errno));
 147                                        sleep(1);
 148                                }
 149                                continue;
 150                        }
 151                        if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
 152                                /* Data ready; we keep the last byte
 153                                 * to ourselves in case we detect
 154                                 * broken rev-list, so that we can
 155                                 * leave the stream corrupted.  This
 156                                 * is unfortunate -- unpack-objects
 157                                 * would happily accept a valid pack
 158                                 * data with trailing garbage, so
 159                                 * appending garbage after we pass all
 160                                 * the pack data is not good enough to
 161                                 * signal breakage to downstream.
 162                                 */
 163                                char *cp = data;
 164                                ssize_t outsz = 0;
 165                                if (0 <= buffered) {
 166                                        *cp++ = buffered;
 167                                        outsz++;
 168                                }
 169                                sz = read(pu_pipe[0], cp,
 170                                          sizeof(data) - outsz);
 171                                if (0 < sz)
 172                                                ;
 173                                else if (sz == 0) {
 174                                        close(pu_pipe[0]);
 175                                        pu_pipe[0] = -1;
 176                                }
 177                                else
 178                                        goto fail;
 179                                sz += outsz;
 180                                if (1 < sz) {
 181                                        buffered = data[sz-1] & 0xFF;
 182                                        sz--;
 183                                }
 184                                else
 185                                        buffered = -1;
 186                                sz = xwrite(1, data, sz);
 187                                if (sz < 0)
 188                                        goto fail;
 189                        }
 190                }
 191
 192                /* See if the children are still there */
 193                if (pid_rev_list || pid_pack_objects) {
 194                        pid = waitpid(-1, &status, WNOHANG);
 195                        if (!pid)
 196                                continue;
 197                        who = ((pid == pid_rev_list) ? "git-rev-list" :
 198                               (pid == pid_pack_objects) ? "git-pack-objects" :
 199                               NULL);
 200                        if (!who) {
 201                                if (pid < 0) {
 202                                        error("git-upload-pack: %s",
 203                                              strerror(errno));
 204                                        goto fail;
 205                                }
 206                                error("git-upload-pack: we weren't "
 207                                      "waiting for %d", pid);
 208                                continue;
 209                        }
 210                        if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) {
 211                                error("git-upload-pack: %s died with error.",
 212                                      who);
 213                                goto fail;
 214                        }
 215                        if (pid == pid_rev_list)
 216                                pid_rev_list = 0;
 217                        if (pid == pid_pack_objects)
 218                                pid_pack_objects = 0;
 219                        if (pid_rev_list || pid_pack_objects)
 220                                continue;
 221                }
 222
 223                /* both died happily */
 224                if (pollsize)
 225                        continue;
 226
 227                /* flush the data */
 228                if (0 <= buffered) {
 229                        data[0] = buffered;
 230                        sz = xwrite(1, data, 1);
 231                        if (sz < 0)
 232                                goto fail;
 233                        fprintf(stderr, "flushed.\n");
 234                }
 235                return;
 236        }
 237 fail:
 238        if (pid_pack_objects)
 239                kill(pid_pack_objects, SIGKILL);
 240        if (pid_rev_list)
 241                kill(pid_rev_list, SIGKILL);
 242        die("git-upload-pack: aborting due to possible repository corruption on the remote side.");
 243}
 244
 245static int got_sha1(char *hex, unsigned char *sha1)
 246{
 247        if (get_sha1_hex(hex, sha1))
 248                die("git-upload-pack: expected SHA1 object, got '%s'", hex);
 249        if (!has_sha1_file(sha1))
 250                return 0;
 251        if (nr_has < MAX_HAS) {
 252                struct object *o = lookup_object(sha1);
 253                if (!(o && o->parsed))
 254                        o = parse_object(sha1);
 255                if (!o)
 256                        die("oops (%s)", sha1_to_hex(sha1));
 257                if (o->type == TYPE_COMMIT) {
 258                        struct commit_list *parents;
 259                        if (o->flags & THEY_HAVE)
 260                                return 0;
 261                        o->flags |= THEY_HAVE;
 262                        for (parents = ((struct commit*)o)->parents;
 263                             parents;
 264                             parents = parents->next)
 265                                parents->item->object.flags |= THEY_HAVE;
 266                }
 267                memcpy(has_sha1[nr_has++], sha1, 20);
 268        }
 269        return 1;
 270}
 271
 272static int get_common_commits(void)
 273{
 274        static char line[1000];
 275        unsigned char sha1[20], last_sha1[20];
 276        int len;
 277
 278        track_object_refs = 0;
 279        save_commit_buffer = 0;
 280
 281        for(;;) {
 282                len = packet_read_line(0, line, sizeof(line));
 283                reset_timeout();
 284
 285                if (!len) {
 286                        if (nr_has == 0 || multi_ack)
 287                                packet_write(1, "NAK\n");
 288                        continue;
 289                }
 290                len = strip(line, len);
 291                if (!strncmp(line, "have ", 5)) {
 292                        if (got_sha1(line+5, sha1) &&
 293                                        (multi_ack || nr_has == 1)) {
 294                                if (nr_has >= MAX_HAS)
 295                                        multi_ack = 0;
 296                                packet_write(1, "ACK %s%s\n",
 297                                        sha1_to_hex(sha1),
 298                                        multi_ack ?  " continue" : "");
 299                                if (multi_ack)
 300                                        memcpy(last_sha1, sha1, 20);
 301                        }
 302                        continue;
 303                }
 304                if (!strcmp(line, "done")) {
 305                        if (nr_has > 0) {
 306                                if (multi_ack)
 307                                        packet_write(1, "ACK %s\n",
 308                                                        sha1_to_hex(last_sha1));
 309                                return 0;
 310                        }
 311                        packet_write(1, "NAK\n");
 312                        return -1;
 313                }
 314                die("git-upload-pack: expected SHA1 list, got '%s'", line);
 315        }
 316}
 317
 318static int receive_needs(void)
 319{
 320        static char line[1000];
 321        int len, needs;
 322
 323        needs = 0;
 324        for (;;) {
 325                struct object *o;
 326                unsigned char dummy[20], *sha1_buf;
 327                len = packet_read_line(0, line, sizeof(line));
 328                reset_timeout();
 329                if (!len)
 330                        return needs;
 331
 332                sha1_buf = dummy;
 333                if (needs == MAX_NEEDS) {
 334                        fprintf(stderr,
 335                                "warning: supporting only a max of %d requests. "
 336                                "sending everything instead.\n",
 337                                MAX_NEEDS);
 338                }
 339                else if (needs < MAX_NEEDS)
 340                        sha1_buf = needs_sha1[needs];
 341
 342                if (strncmp("want ", line, 5) || get_sha1_hex(line+5, sha1_buf))
 343                        die("git-upload-pack: protocol error, "
 344                            "expected to get sha, not '%s'", line);
 345                if (strstr(line+45, "multi_ack"))
 346                        multi_ack = 1;
 347                if (strstr(line+45, "thin-pack"))
 348                        use_thin_pack = 1;
 349
 350                /* We have sent all our refs already, and the other end
 351                 * should have chosen out of them; otherwise they are
 352                 * asking for nonsense.
 353                 *
 354                 * Hmph.  We may later want to allow "want" line that
 355                 * asks for something like "master~10" (symbolic)...
 356                 * would it make sense?  I don't know.
 357                 */
 358                o = lookup_object(sha1_buf);
 359                if (!o || !(o->flags & OUR_REF))
 360                        die("git-upload-pack: not our ref %s", line+5);
 361                if (!(o->flags & WANTED)) {
 362                        o->flags |= WANTED;
 363                        needs++;
 364                }
 365        }
 366}
 367
 368static int send_ref(const char *refname, const unsigned char *sha1)
 369{
 370        static char *capabilities = "multi_ack thin-pack";
 371        struct object *o = parse_object(sha1);
 372
 373        if (!o)
 374                die("git-upload-pack: cannot find object %s:", sha1_to_hex(sha1));
 375
 376        if (capabilities)
 377                packet_write(1, "%s %s%c%s\n", sha1_to_hex(sha1), refname,
 378                        0, capabilities);
 379        else
 380                packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
 381        capabilities = NULL;
 382        if (!(o->flags & OUR_REF)) {
 383                o->flags |= OUR_REF;
 384                nr_our_refs++;
 385        }
 386        if (o->type == TYPE_TAG) {
 387                o = deref_tag(o, refname, 0);
 388                packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 389        }
 390        return 0;
 391}
 392
 393static int upload_pack(void)
 394{
 395        reset_timeout();
 396        head_ref(send_ref);
 397        for_each_ref(send_ref);
 398        packet_flush(1);
 399        nr_needs = receive_needs();
 400        if (!nr_needs)
 401                return 0;
 402        get_common_commits();
 403        create_pack_file();
 404        return 0;
 405}
 406
 407int main(int argc, char **argv)
 408{
 409        char *dir;
 410        int i;
 411        int strict = 0;
 412
 413        for (i = 1; i < argc; i++) {
 414                char *arg = argv[i];
 415
 416                if (arg[0] != '-')
 417                        break;
 418                if (!strcmp(arg, "--strict")) {
 419                        strict = 1;
 420                        continue;
 421                }
 422                if (!strncmp(arg, "--timeout=", 10)) {
 423                        timeout = atoi(arg+10);
 424                        continue;
 425                }
 426                if (!strcmp(arg, "--")) {
 427                        i++;
 428                        break;
 429                }
 430        }
 431        
 432        if (i != argc-1)
 433                usage(upload_pack_usage);
 434        dir = argv[i];
 435
 436        if (!enter_repo(dir, strict))
 437                die("'%s': unable to chdir or not a git archive", dir);
 438
 439        upload_pack();
 440        return 0;
 441}