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