85f5b1ed4cf5f9a97625606bba72143861920d0c
   1#include "cache.h"
   2#include "transport.h"
   3#include "run-command.h"
   4#include "http.h"
   5#include "pkt-line.h"
   6#include "fetch-pack.h"
   7#include "walker.h"
   8#include "bundle.h"
   9
  10/* Generic functions for using commit walkers */
  11
  12static int fetch_objs_via_walker(struct transport *transport,
  13                                 int nr_objs, struct ref **to_fetch)
  14{
  15        char *dest = xstrdup(transport->url);
  16        struct walker *walker = transport->data;
  17        char **objs = xmalloc(nr_objs * sizeof(*objs));
  18        int i;
  19
  20        walker->get_all = 1;
  21        walker->get_tree = 1;
  22        walker->get_history = 1;
  23        walker->get_verbosely = transport->verbose;
  24        walker->get_recover = 0;
  25
  26        for (i = 0; i < nr_objs; i++)
  27                objs[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
  28
  29        if (walker_fetch(walker, nr_objs, objs, NULL, NULL))
  30                die("Fetch failed.");
  31
  32        for (i = 0; i < nr_objs; i++)
  33                free(objs[i]);
  34        free(objs);
  35        free(dest);
  36        return 0;
  37}
  38
  39static int disconnect_walker(struct transport *transport)
  40{
  41        struct walker *walker = transport->data;
  42        if (walker)
  43                walker_free(walker);
  44        return 0;
  45}
  46
  47static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
  48        const char **argv;
  49        int argc;
  50        int err;
  51
  52        argv = xmalloc((refspec_nr + 11) * sizeof(char *));
  53        argv[0] = "http-push";
  54        argc = 1;
  55        if (flags & TRANSPORT_PUSH_ALL)
  56                argv[argc++] = "--all";
  57        if (flags & TRANSPORT_PUSH_FORCE)
  58                argv[argc++] = "--force";
  59        argv[argc++] = transport->url;
  60        while (refspec_nr--)
  61                argv[argc++] = *refspec++;
  62        argv[argc] = NULL;
  63        err = run_command_v_opt(argv, RUN_GIT_CMD);
  64        switch (err) {
  65        case -ERR_RUN_COMMAND_FORK:
  66                error("unable to fork for %s", argv[0]);
  67        case -ERR_RUN_COMMAND_EXEC:
  68                error("unable to exec %s", argv[0]);
  69                break;
  70        case -ERR_RUN_COMMAND_WAITPID:
  71        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
  72        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
  73        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
  74                error("%s died with strange error", argv[0]);
  75        }
  76        return !!err;
  77}
  78
  79#ifndef NO_CURL
  80static int missing__target(int code, int result)
  81{
  82        return  /* file:// URL -- do we ever use one??? */
  83                (result == CURLE_FILE_COULDNT_READ_FILE) ||
  84                /* http:// and https:// URL */
  85                (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
  86                /* ftp:// URL */
  87                (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
  88                ;
  89}
  90
  91#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
  92
  93static struct ref *get_refs_via_curl(const struct transport *transport)
  94{
  95        struct buffer buffer;
  96        char *data, *start, *mid;
  97        char *ref_name;
  98        char *refs_url;
  99        int i = 0;
 100
 101        struct active_request_slot *slot;
 102        struct slot_results results;
 103
 104        struct ref *refs = NULL;
 105        struct ref *ref = NULL;
 106        struct ref *last_ref = NULL;
 107
 108        data = xmalloc(4096);
 109        buffer.size = 4096;
 110        buffer.posn = 0;
 111        buffer.buffer = data;
 112
 113        refs_url = xmalloc(strlen(transport->url) + 11);
 114        sprintf(refs_url, "%s/info/refs", transport->url);
 115
 116        http_init();
 117
 118        slot = get_active_slot();
 119        slot->results = &results;
 120        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
 121        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
 122        curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
 123        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
 124        if (start_active_slot(slot)) {
 125                run_active_slot(slot);
 126                if (results.curl_result != CURLE_OK) {
 127                        if (missing_target(&results)) {
 128                                free(buffer.buffer);
 129                                return NULL;
 130                        } else {
 131                                free(buffer.buffer);
 132                                error("%s", curl_errorstr);
 133                                return NULL;
 134                        }
 135                }
 136        } else {
 137                free(buffer.buffer);
 138                error("Unable to start request");
 139                return NULL;
 140        }
 141
 142        http_cleanup();
 143
 144        data = buffer.buffer;
 145        start = NULL;
 146        mid = data;
 147        while (i < buffer.posn) {
 148                if (!start)
 149                        start = &data[i];
 150                if (data[i] == '\t')
 151                        mid = &data[i];
 152                if (data[i] == '\n') {
 153                        data[i] = 0;
 154                        ref_name = mid + 1;
 155                        ref = xmalloc(sizeof(struct ref) +
 156                                      strlen(ref_name) + 1);
 157                        memset(ref, 0, sizeof(struct ref));
 158                        strcpy(ref->name, ref_name);
 159                        get_sha1_hex(start, ref->old_sha1);
 160                        if (!refs)
 161                                refs = ref;
 162                        if (last_ref)
 163                                last_ref->next = ref;
 164                        last_ref = ref;
 165                        start = NULL;
 166                }
 167                i++;
 168        }
 169
 170        free(buffer.buffer);
 171
 172        return refs;
 173}
 174
 175static int fetch_objs_via_curl(struct transport *transport,
 176                                 int nr_objs, struct ref **to_fetch)
 177{
 178        if (!transport->data)
 179                transport->data = get_http_walker(transport->url);
 180        return fetch_objs_via_walker(transport, nr_objs, to_fetch);
 181}
 182
 183#else
 184
 185static struct ref *get_refs_via_curl(const struct transport *transport)
 186{
 187        die("Cannot fetch from '%s' without curl ...", transport->url);
 188        return NULL;
 189}
 190
 191static int fetch_objs_via_curl(struct transport *transport,
 192                                 int nr_objs, struct ref **to_fetch)
 193{
 194        die("Cannot fetch from '%s' without curl ...", transport->url);
 195        return -1;
 196}
 197
 198#endif
 199
 200struct bundle_transport_data {
 201        int fd;
 202        struct bundle_header header;
 203};
 204
 205static struct ref *get_refs_from_bundle(const struct transport *transport)
 206{
 207        struct bundle_transport_data *data = transport->data;
 208        struct ref *result = NULL;
 209        int i;
 210
 211        if (data->fd > 0)
 212                close(data->fd);
 213        data->fd = read_bundle_header(transport->url, &data->header);
 214        if (data->fd < 0)
 215                die ("Could not read bundle '%s'.", transport->url);
 216        for (i = 0; i < data->header.references.nr; i++) {
 217                struct ref_list_entry *e = data->header.references.list + i;
 218                struct ref *ref = alloc_ref(strlen(e->name));
 219                hashcpy(ref->old_sha1, e->sha1);
 220                strcpy(ref->name, e->name);
 221                ref->next = result;
 222                result = ref;
 223        }
 224        return result;
 225}
 226
 227static int fetch_refs_from_bundle(struct transport *transport,
 228                               int nr_heads, struct ref **to_fetch)
 229{
 230        struct bundle_transport_data *data = transport->data;
 231        return unbundle(&data->header, data->fd);
 232}
 233
 234static int close_bundle(struct transport *transport)
 235{
 236        struct bundle_transport_data *data = transport->data;
 237        if (data->fd > 0)
 238                close(data->fd);
 239        return 0;
 240}
 241
 242struct git_transport_data {
 243        unsigned thin : 1;
 244        unsigned keep : 1;
 245
 246        int unpacklimit;
 247
 248        int depth;
 249
 250        const char *uploadpack;
 251        const char *receivepack;
 252};
 253
 254static int set_git_option(struct transport *connection,
 255                          const char *name, const char *value)
 256{
 257        struct git_transport_data *data = connection->data;
 258        if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
 259                data->uploadpack = value;
 260                return 0;
 261        } else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
 262                data->receivepack = value;
 263                return 0;
 264        } else if (!strcmp(name, TRANS_OPT_THIN)) {
 265                data->thin = !!value;
 266                return 0;
 267        } else if (!strcmp(name, TRANS_OPT_KEEP)) {
 268                data->keep = !!value;
 269                return 0;
 270        } else if (!strcmp(name, TRANS_OPT_UNPACKLIMIT)) {
 271                data->unpacklimit = atoi(value);
 272                return 0;
 273        } else if (!strcmp(name, TRANS_OPT_DEPTH)) {
 274                if (!value)
 275                        data->depth = 0;
 276                else
 277                        data->depth = atoi(value);
 278                return 0;
 279        }
 280        return 1;
 281}
 282
 283static struct ref *get_refs_via_connect(const struct transport *transport)
 284{
 285        struct git_transport_data *data = transport->data;
 286        struct ref *refs;
 287        int fd[2];
 288        pid_t pid;
 289        char *dest = xstrdup(transport->url);
 290
 291        pid = git_connect(fd, dest, data->uploadpack, 0);
 292
 293        if (pid < 0)
 294                die("Failed to connect to \"%s\"", transport->url);
 295
 296        get_remote_heads(fd[0], &refs, 0, NULL, 0);
 297        packet_flush(fd[1]);
 298
 299        finish_connect(pid);
 300
 301        free(dest);
 302
 303        return refs;
 304}
 305
 306static int fetch_refs_via_pack(struct transport *transport,
 307                               int nr_heads, struct ref **to_fetch)
 308{
 309        struct git_transport_data *data = transport->data;
 310        char **heads = xmalloc(nr_heads * sizeof(*heads));
 311        char **origh = xmalloc(nr_heads * sizeof(*origh));
 312        struct ref *refs;
 313        char *dest = xstrdup(transport->url);
 314        struct fetch_pack_args args;
 315        int i;
 316
 317        memset(&args, 0, sizeof(args));
 318        args.uploadpack = data->uploadpack;
 319        args.keep_pack = data->keep;
 320        args.lock_pack = 1;
 321        args.unpacklimit = data->unpacklimit;
 322        args.use_thin_pack = data->thin;
 323        args.verbose = transport->verbose;
 324        args.depth = data->depth;
 325
 326        for (i = 0; i < nr_heads; i++)
 327                origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 328        refs = fetch_pack(&args, dest, nr_heads, heads, &transport->pack_lockfile);
 329
 330        for (i = 0; i < nr_heads; i++)
 331                free(origh[i]);
 332        free(origh);
 333        free(heads);
 334        free_refs(refs);
 335        free(dest);
 336        return 0;
 337}
 338
 339static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
 340        struct git_transport_data *data = transport->data;
 341        const char **argv;
 342        char *rem;
 343        int argc;
 344        int err;
 345
 346        argv = xmalloc((refspec_nr + 11) * sizeof(char *));
 347        argv[0] = "send-pack";
 348        argc = 1;
 349        if (flags & TRANSPORT_PUSH_ALL)
 350                argv[argc++] = "--all";
 351        if (flags & TRANSPORT_PUSH_FORCE)
 352                argv[argc++] = "--force";
 353        if (data->receivepack) {
 354                char *rp = xmalloc(strlen(data->receivepack) + 16);
 355                sprintf(rp, "--receive-pack=%s", data->receivepack);
 356                argv[argc++] = rp;
 357        }
 358        if (data->thin)
 359                argv[argc++] = "--thin";
 360        rem = xmalloc(strlen(transport->remote->name) + 10);
 361        sprintf(rem, "--remote=%s", transport->remote->name);
 362        argv[argc++] = rem;
 363        argv[argc++] = transport->url;
 364        while (refspec_nr--)
 365                argv[argc++] = *refspec++;
 366        argv[argc] = NULL;
 367        err = run_command_v_opt(argv, RUN_GIT_CMD);
 368        switch (err) {
 369        case -ERR_RUN_COMMAND_FORK:
 370                error("unable to fork for %s", argv[0]);
 371        case -ERR_RUN_COMMAND_EXEC:
 372                error("unable to exec %s", argv[0]);
 373                break;
 374        case -ERR_RUN_COMMAND_WAITPID:
 375        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 376        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 377        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 378                error("%s died with strange error", argv[0]);
 379        }
 380        return !!err;
 381}
 382
 383static int is_local(const char *url)
 384{
 385        const char *colon = strchr(url, ':');
 386        const char *slash = strchr(url, '/');
 387        return !colon || (slash && slash < colon);
 388}
 389
 390static int is_file(const char *url)
 391{
 392        struct stat buf;
 393        if (stat(url, &buf))
 394                return 0;
 395        return S_ISREG(buf.st_mode);
 396}
 397
 398struct transport *transport_get(struct remote *remote, const char *url)
 399{
 400        struct transport *ret = xcalloc(1, sizeof(*ret));
 401
 402        ret->remote = remote;
 403        ret->url = url;
 404
 405        if (!prefixcmp(url, "rsync://")) {
 406                /* not supported; don't populate any ops */
 407
 408        } else if (!prefixcmp(url, "http://")
 409                || !prefixcmp(url, "https://")
 410                || !prefixcmp(url, "ftp://")) {
 411                ret->get_refs_list = get_refs_via_curl;
 412                ret->fetch = fetch_objs_via_curl;
 413                ret->push = curl_transport_push;
 414                ret->disconnect = disconnect_walker;
 415
 416        } else if (is_local(url) && is_file(url)) {
 417                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
 418                ret->data = data;
 419                ret->get_refs_list = get_refs_from_bundle;
 420                ret->fetch = fetch_refs_from_bundle;
 421                ret->disconnect = close_bundle;
 422
 423        } else {
 424                struct git_transport_data *data = xcalloc(1, sizeof(*data));
 425                ret->data = data;
 426                ret->set_option = set_git_option;
 427                ret->get_refs_list = get_refs_via_connect;
 428                ret->fetch = fetch_refs_via_pack;
 429                ret->push = git_transport_push;
 430
 431                data->thin = 1;
 432                data->uploadpack = "git-upload-pack";
 433                if (remote && remote->uploadpack)
 434                        data->uploadpack = remote->uploadpack;
 435                data->receivepack = "git-receive-pack";
 436                if (remote && remote->receivepack)
 437                        data->receivepack = remote->receivepack;
 438                data->unpacklimit = -1;
 439        }
 440
 441        return ret;
 442}
 443
 444int transport_set_option(struct transport *transport,
 445                         const char *name, const char *value)
 446{
 447        if (transport->set_option)
 448                return transport->set_option(transport, name, value);
 449        return 1;
 450}
 451
 452int transport_push(struct transport *transport,
 453                   int refspec_nr, const char **refspec, int flags)
 454{
 455        if (!transport->push)
 456                return 1;
 457        return transport->push(transport, refspec_nr, refspec, flags);
 458}
 459
 460struct ref *transport_get_remote_refs(struct transport *transport)
 461{
 462        if (!transport->remote_refs)
 463                transport->remote_refs = transport->get_refs_list(transport);
 464        return transport->remote_refs;
 465}
 466
 467int transport_fetch_refs(struct transport *transport, struct ref *refs)
 468{
 469        int rc;
 470        int nr_heads = 0, nr_alloc = 0;
 471        struct ref **heads = NULL;
 472        struct ref *rm;
 473
 474        for (rm = refs; rm; rm = rm->next) {
 475                if (rm->peer_ref &&
 476                    !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
 477                        continue;
 478                ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
 479                heads[nr_heads++] = rm;
 480        }
 481
 482        rc = transport->fetch(transport, nr_heads, heads);
 483        free(heads);
 484        return rc;
 485}
 486
 487void transport_unlock_pack(struct transport *transport)
 488{
 489        if (transport->pack_lockfile) {
 490                unlink(transport->pack_lockfile);
 491                free(transport->pack_lockfile);
 492                transport->pack_lockfile = NULL;
 493        }
 494}
 495
 496int transport_disconnect(struct transport *transport)
 497{
 498        int ret = 0;
 499        if (transport->disconnect)
 500                ret = transport->disconnect(transport);
 501        free(transport);
 502        return ret;
 503}