clone-pack.con commit Fix fd leak in http-fetch (54a9ba0)
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include <sys/wait.h>
   5
   6static const char clone_pack_usage[] =
   7"git-clone-pack [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*";
   8static const char *exec = "git-upload-pack";
   9
  10static void clone_handshake(int fd[2], struct ref *ref)
  11{
  12        unsigned char sha1[20];
  13
  14        while (ref) {
  15                packet_write(fd[1], "want %s\n", sha1_to_hex(ref->old_sha1));
  16                ref = ref->next;
  17        }
  18        packet_flush(fd[1]);
  19
  20        /* We don't have nuttin' */
  21        packet_write(fd[1], "done\n");
  22        if (get_ack(fd[0], sha1))
  23                error("Huh! git-clone-pack got positive ack for %s", sha1_to_hex(sha1));
  24}
  25
  26static int is_master(struct ref *ref)
  27{
  28        return !strcmp(ref->name, "refs/heads/master");
  29}
  30
  31static void write_one_ref(struct ref *ref)
  32{
  33        char *path = git_path("%s", ref->name);
  34        int fd;
  35        char *hex;
  36
  37        if (!strncmp(ref->name, "refs/", 5) &&
  38            check_ref_format(ref->name + 5)) {
  39                error("refusing to create funny ref '%s' locally", ref->name);
  40                return;
  41        }
  42
  43        if (safe_create_leading_directories(path))
  44                die("unable to create leading directory for %s", ref->name);
  45        fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
  46        if (fd < 0)
  47                die("unable to create ref %s", ref->name);
  48        hex = sha1_to_hex(ref->old_sha1);
  49        hex[40] = '\n';
  50        if (write(fd, hex, 41) != 41)
  51                die("unable to write ref %s", ref->name);
  52        close(fd);
  53}
  54
  55static void write_refs(struct ref *ref)
  56{
  57        struct ref *head = NULL, *head_ptr, *master_ref;
  58        char *head_path;
  59
  60        /* Upload-pack must report HEAD first */
  61        if (!strcmp(ref->name, "HEAD")) {
  62                head = ref;
  63                ref = ref->next;
  64        }
  65        head_ptr = NULL;
  66        master_ref = NULL;
  67        while (ref) {
  68                if (is_master(ref))
  69                        master_ref = ref;
  70                if (head &&
  71                    !memcmp(ref->old_sha1, head->old_sha1, 20) &&
  72                    !strncmp(ref->name, "refs/heads/",11) &&
  73                    (!head_ptr || ref == master_ref))
  74                        head_ptr = ref;
  75
  76                write_one_ref(ref);
  77                ref = ref->next;
  78        }
  79        if (!head) {
  80                fprintf(stderr, "No HEAD in remote.\n");
  81                return;
  82        }
  83
  84        head_path = strdup(git_path("HEAD"));
  85        if (!head_ptr) {
  86                /*
  87                 * If we had a master ref, and it wasn't HEAD, we need to undo the
  88                 * symlink, and write a standalone HEAD. Give a warning, because that's
  89                 * really really wrong.
  90                 */
  91                if (master_ref) {
  92                        error("HEAD doesn't point to any refs! Making standalone HEAD");
  93                        unlink(head_path);
  94                }
  95                write_one_ref(head);
  96                free(head_path);
  97                return;
  98        }
  99
 100        /* We reset to the master branch if it's available */
 101        if (master_ref)
 102                return;
 103
 104        fprintf(stderr, "Setting HEAD to %s\n", head_ptr->name);
 105
 106        /*
 107         * Uhhuh. Other end didn't have master. We start HEAD off with
 108         * the first branch with the same value.
 109         */
 110        if (create_symref(head_path, head_ptr->name) < 0)
 111                die("unable to link HEAD to %s", head_ptr->name);
 112        free(head_path);
 113}
 114
 115static int finish_pack(const char *pack_tmp_name)
 116{
 117        int pipe_fd[2];
 118        pid_t pid;
 119        char idx[PATH_MAX];
 120        char final[PATH_MAX];
 121        char hash[41];
 122        unsigned char sha1[20];
 123        char *cp;
 124        int err = 0;
 125
 126        if (pipe(pipe_fd) < 0)
 127                die("git-clone-pack: unable to set up pipe");
 128
 129        strcpy(idx, pack_tmp_name); /* ".git/objects/pack-XXXXXX" */
 130        cp = strrchr(idx, '/');
 131        memcpy(cp, "/pidx", 5);
 132
 133        pid = fork();
 134        if (pid < 0)
 135                die("git-clone-pack: unable to fork off git-index-pack");
 136        if (!pid) {
 137                close(0);
 138                dup2(pipe_fd[1], 1);
 139                close(pipe_fd[0]);
 140                close(pipe_fd[1]);
 141                execlp("git-index-pack","git-index-pack",
 142                       "-o", idx, pack_tmp_name, NULL);
 143                error("cannot exec git-index-pack <%s> <%s>",
 144                      idx, pack_tmp_name);
 145                exit(1);
 146        }
 147        close(pipe_fd[1]);
 148        if (read(pipe_fd[0], hash, 40) != 40) {
 149                error("git-clone-pack: unable to read from git-index-pack");
 150                err = 1;
 151        }
 152        close(pipe_fd[0]);
 153
 154        for (;;) {
 155                int status, code;
 156                int retval = waitpid(pid, &status, 0);
 157
 158                if (retval < 0) {
 159                        if (errno == EINTR)
 160                                continue;
 161                        error("waitpid failed (%s)", strerror(retval));
 162                        goto error_die;
 163                }
 164                if (WIFSIGNALED(status)) {
 165                        int sig = WTERMSIG(status);
 166                        error("git-index-pack died of signal %d", sig);
 167                        goto error_die;
 168                }
 169                if (!WIFEXITED(status)) {
 170                        error("git-index-pack died of unnatural causes %d",
 171                              status);
 172                        goto error_die;
 173                }
 174                code = WEXITSTATUS(status);
 175                if (code) {
 176                        error("git-index-pack died with error code %d", code);
 177                        goto error_die;
 178                }
 179                if (err)
 180                        goto error_die;
 181                break;
 182        }
 183        hash[40] = 0;
 184        if (get_sha1_hex(hash, sha1)) {
 185                error("git-index-pack reported nonsense '%s'", hash);
 186                goto error_die;
 187        }
 188        /* Now we have pack in pack_tmp_name[], and
 189         * idx in idx[]; rename them to their final names.
 190         */
 191        snprintf(final, sizeof(final),
 192                 "%s/pack/pack-%s.pack", get_object_directory(), hash);
 193        move_temp_to_file(pack_tmp_name, final);
 194        chmod(final, 0444);
 195        snprintf(final, sizeof(final),
 196                 "%s/pack/pack-%s.idx", get_object_directory(), hash);
 197        move_temp_to_file(idx, final);
 198        chmod(final, 0444);
 199        return 0;
 200
 201 error_die:
 202        unlink(idx);
 203        unlink(pack_tmp_name);
 204        exit(1);
 205}
 206
 207static int clone_without_unpack(int fd[2])
 208{
 209        char tmpfile[PATH_MAX];
 210        int ofd, ifd;
 211
 212        ifd = fd[0];
 213        snprintf(tmpfile, sizeof(tmpfile),
 214                 "%s/pack/tmp-XXXXXX", get_object_directory());
 215        ofd = mkstemp(tmpfile);
 216        if (ofd < 0)
 217                return error("unable to create temporary file %s", tmpfile);
 218
 219        while (1) {
 220                char buf[8192];
 221                ssize_t sz, wsz, pos;
 222                sz = read(ifd, buf, sizeof(buf));
 223                if (sz == 0)
 224                        break;
 225                if (sz < 0) {
 226                        error("error reading pack (%s)", strerror(errno));
 227                        close(ofd);
 228                        unlink(tmpfile);
 229                        return -1;
 230                }
 231                pos = 0;
 232                while (pos < sz) {
 233                        wsz = write(ofd, buf + pos, sz - pos);
 234                        if (wsz < 0) {
 235                                error("error writing pack (%s)",
 236                                      strerror(errno));
 237                                close(ofd);
 238                                unlink(tmpfile);
 239                                return -1;
 240                        }
 241                        pos += wsz;
 242                }
 243        }
 244        close(ofd);
 245        return finish_pack(tmpfile);
 246}
 247
 248static int clone_pack(int fd[2], int nr_match, char **match)
 249{
 250        struct ref *refs;
 251        int status;
 252
 253        get_remote_heads(fd[0], &refs, nr_match, match, 1);
 254        if (!refs) {
 255                packet_flush(fd[1]);
 256                die("no matching remote head");
 257        }
 258        clone_handshake(fd, refs);
 259
 260        status = clone_without_unpack(fd);
 261
 262        if (!status)
 263                write_refs(refs);
 264        return status;
 265}
 266
 267int main(int argc, char **argv)
 268{
 269        int i, ret, nr_heads;
 270        char *dest = NULL, **heads;
 271        int fd[2];
 272        pid_t pid;
 273
 274        nr_heads = 0;
 275        heads = NULL;
 276        for (i = 1; i < argc; i++) {
 277                char *arg = argv[i];
 278
 279                if (*arg == '-') {
 280                        if (!strcmp("-q", arg))
 281                                continue;
 282                        if (!strncmp("--exec=", arg, 7)) {
 283                                exec = arg + 7;
 284                                continue;
 285                        }
 286                        if (!strcmp("--keep", arg))
 287                                continue;
 288                        usage(clone_pack_usage);
 289                }
 290                dest = arg;
 291                heads = argv + i + 1;
 292                nr_heads = argc - i - 1;
 293                break;
 294        }
 295        if (!dest)
 296                usage(clone_pack_usage);
 297        pid = git_connect(fd, dest, exec);
 298        if (pid < 0)
 299                return 1;
 300        ret = clone_pack(fd, nr_heads, heads);
 301        close(fd[0]);
 302        close(fd[1]);
 303        finish_connect(pid);
 304        return ret;
 305}