123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- #include "cache.h"
- #include "repository.h"
- #include "commit.h"
- #include "walker.h"
- #include "http.h"
- #include "list.h"
- #include "transport.h"
- #include "packfile.h"
- #include "object-store.h"
- struct alt_base {
- char *base;
- int got_indices;
- struct packed_git *packs;
- struct alt_base *next;
- };
- enum object_request_state {
- WAITING,
- ABORTED,
- ACTIVE,
- COMPLETE
- };
- struct object_request {
- struct walker *walker;
- struct object_id oid;
- struct alt_base *repo;
- enum object_request_state state;
- struct http_object_request *req;
- struct list_head node;
- };
- struct alternates_request {
- struct walker *walker;
- const char *base;
- struct strbuf *url;
- struct strbuf *buffer;
- struct active_request_slot *slot;
- int http_specific;
- };
- struct walker_data {
- const char *url;
- int got_alternates;
- struct alt_base *alt;
- };
- static LIST_HEAD(object_queue_head);
- static void fetch_alternates(struct walker *walker, const char *base);
- static void process_object_response(void *callback_data);
- static void start_object_request(struct walker *walker,
- struct object_request *obj_req)
- {
- struct active_request_slot *slot;
- struct http_object_request *req;
- req = new_http_object_request(obj_req->repo->base, &obj_req->oid);
- if (req == NULL) {
- obj_req->state = ABORTED;
- return;
- }
- obj_req->req = req;
- slot = req->slot;
- slot->callback_func = process_object_response;
- slot->callback_data = obj_req;
- /* Try to get the request started, abort the request on error */
- obj_req->state = ACTIVE;
- if (!start_active_slot(slot)) {
- obj_req->state = ABORTED;
- release_http_object_request(req);
- return;
- }
- }
- static void finish_object_request(struct object_request *obj_req)
- {
- if (finish_http_object_request(obj_req->req))
- return;
- if (obj_req->req->rename == 0)
- walker_say(obj_req->walker, "got %s\n", oid_to_hex(&obj_req->oid));
- }
- static void process_object_response(void *callback_data)
- {
- struct object_request *obj_req =
- (struct object_request *)callback_data;
- struct walker *walker = obj_req->walker;
- struct walker_data *data = walker->data;
- struct alt_base *alt = data->alt;
- process_http_object_request(obj_req->req);
- obj_req->state = COMPLETE;
- normalize_curl_result(&obj_req->req->curl_result,
- obj_req->req->http_code,
- obj_req->req->errorstr,
- sizeof(obj_req->req->errorstr));
- /* Use alternates if necessary */
- if (missing_target(obj_req->req)) {
- fetch_alternates(walker, alt->base);
- if (obj_req->repo->next != NULL) {
- obj_req->repo =
- obj_req->repo->next;
- release_http_object_request(obj_req->req);
- start_object_request(walker, obj_req);
- return;
- }
- }
- finish_object_request(obj_req);
- }
- static void release_object_request(struct object_request *obj_req)
- {
- if (obj_req->req !=NULL && obj_req->req->localfile != -1)
- error("fd leakage in release: %d", obj_req->req->localfile);
- list_del(&obj_req->node);
- free(obj_req);
- }
- #ifdef USE_CURL_MULTI
- static int fill_active_slot(struct walker *walker)
- {
- struct object_request *obj_req;
- struct list_head *pos, *tmp, *head = &object_queue_head;
- list_for_each_safe(pos, tmp, head) {
- obj_req = list_entry(pos, struct object_request, node);
- if (obj_req->state == WAITING) {
- if (has_object_file(&obj_req->oid))
- obj_req->state = COMPLETE;
- else {
- start_object_request(walker, obj_req);
- return 1;
- }
- }
- }
- return 0;
- }
- #endif
- static void prefetch(struct walker *walker, unsigned char *sha1)
- {
- struct object_request *newreq;
- struct walker_data *data = walker->data;
- newreq = xmalloc(sizeof(*newreq));
- newreq->walker = walker;
- hashcpy(newreq->oid.hash, sha1);
- newreq->repo = data->alt;
- newreq->state = WAITING;
- newreq->req = NULL;
- http_is_verbose = walker->get_verbosely;
- list_add_tail(&newreq->node, &object_queue_head);
- #ifdef USE_CURL_MULTI
- fill_active_slots();
- step_active_slots();
- #endif
- }
- static int is_alternate_allowed(const char *url)
- {
- const char *protocols[] = {
- "http", "https", "ftp", "ftps"
- };
- int i;
- if (http_follow_config != HTTP_FOLLOW_ALWAYS) {
- warning("alternate disabled by http.followRedirects: %s", url);
- return 0;
- }
- for (i = 0; i < ARRAY_SIZE(protocols); i++) {
- const char *end;
- if (skip_prefix(url, protocols[i], &end) &&
- starts_with(end, "://"))
- break;
- }
- if (i >= ARRAY_SIZE(protocols)) {
- warning("ignoring alternate with unknown protocol: %s", url);
- return 0;
- }
- if (!is_transport_allowed(protocols[i], 0)) {
- warning("ignoring alternate with restricted protocol: %s", url);
- return 0;
- }
- return 1;
- }
- static void process_alternates_response(void *callback_data)
- {
- struct alternates_request *alt_req =
- (struct alternates_request *)callback_data;
- struct walker *walker = alt_req->walker;
- struct walker_data *cdata = walker->data;
- struct active_request_slot *slot = alt_req->slot;
- struct alt_base *tail = cdata->alt;
- const char *base = alt_req->base;
- const char null_byte = '\0';
- char *data;
- int i = 0;
- normalize_curl_result(&slot->curl_result, slot->http_code,
- curl_errorstr, sizeof(curl_errorstr));
- if (alt_req->http_specific) {
- if (slot->curl_result != CURLE_OK ||
- !alt_req->buffer->len) {
- /* Try reusing the slot to get non-http alternates */
- alt_req->http_specific = 0;
- strbuf_reset(alt_req->url);
- strbuf_addf(alt_req->url, "%s/objects/info/alternates",
- base);
- curl_easy_setopt(slot->curl, CURLOPT_URL,
- alt_req->url->buf);
- active_requests++;
- slot->in_use = 1;
- if (slot->finished != NULL)
- (*slot->finished) = 0;
- if (!start_active_slot(slot)) {
- cdata->got_alternates = -1;
- slot->in_use = 0;
- if (slot->finished != NULL)
- (*slot->finished) = 1;
- }
- return;
- }
- } else if (slot->curl_result != CURLE_OK) {
- if (!missing_target(slot)) {
- cdata->got_alternates = -1;
- return;
- }
- }
- fwrite_buffer((char *)&null_byte, 1, 1, alt_req->buffer);
- alt_req->buffer->len--;
- data = alt_req->buffer->buf;
- while (i < alt_req->buffer->len) {
- int posn = i;
- while (posn < alt_req->buffer->len && data[posn] != '\n')
- posn++;
- if (data[posn] == '\n') {
- int okay = 0;
- int serverlen = 0;
- struct alt_base *newalt;
- if (data[i] == '/') {
- /*
- * This counts
- * http://git.host/pub/scm/linux.git/
- * -----------here^
- * so memcpy(dst, base, serverlen) will
- * copy up to "...git.host".
- */
- const char *colon_ss = strstr(base,"://");
- if (colon_ss) {
- serverlen = (strchr(colon_ss + 3, '/')
- - base);
- okay = 1;
- }
- } else if (!memcmp(data + i, "../", 3)) {
- /*
- * Relative URL; chop the corresponding
- * number of subpath from base (and ../
- * from data), and concatenate the result.
- *
- * The code first drops ../ from data, and
- * then drops one ../ from data and one path
- * from base. IOW, one extra ../ is dropped
- * from data than path is dropped from base.
- *
- * This is not wrong. The alternate in
- * http://git.host/pub/scm/linux.git/
- * to borrow from
- * http://git.host/pub/scm/linus.git/
- * is ../../linus.git/objects/. You need
- * two ../../ to borrow from your direct
- * neighbour.
- */
- i += 3;
- serverlen = strlen(base);
- while (i + 2 < posn &&
- !memcmp(data + i, "../", 3)) {
- do {
- serverlen--;
- } while (serverlen &&
- base[serverlen - 1] != '/');
- i += 3;
- }
- /* If the server got removed, give up. */
- okay = strchr(base, ':') - base + 3 <
- serverlen;
- } else if (alt_req->http_specific) {
- char *colon = strchr(data + i, ':');
- char *slash = strchr(data + i, '/');
- if (colon && slash && colon < data + posn &&
- slash < data + posn && colon < slash) {
- okay = 1;
- }
- }
- if (okay) {
- struct strbuf target = STRBUF_INIT;
- strbuf_add(&target, base, serverlen);
- strbuf_add(&target, data + i, posn - i);
- if (!strbuf_strip_suffix(&target, "objects")) {
- warning("ignoring alternate that does"
- " not end in 'objects': %s",
- target.buf);
- strbuf_release(&target);
- } else if (is_alternate_allowed(target.buf)) {
- warning("adding alternate object store: %s",
- target.buf);
- newalt = xmalloc(sizeof(*newalt));
- newalt->next = NULL;
- newalt->base = strbuf_detach(&target, NULL);
- newalt->got_indices = 0;
- newalt->packs = NULL;
- while (tail->next != NULL)
- tail = tail->next;
- tail->next = newalt;
- } else {
- strbuf_release(&target);
- }
- }
- }
- i = posn + 1;
- }
- cdata->got_alternates = 1;
- }
- static void fetch_alternates(struct walker *walker, const char *base)
- {
- struct strbuf buffer = STRBUF_INIT;
- struct strbuf url = STRBUF_INIT;
- struct active_request_slot *slot;
- struct alternates_request alt_req;
- struct walker_data *cdata = walker->data;
- /*
- * If another request has already started fetching alternates,
- * wait for them to arrive and return to processing this request's
- * curl message
- */
- #ifdef USE_CURL_MULTI
- while (cdata->got_alternates == 0) {
- step_active_slots();
- }
- #endif
- /* Nothing to do if they've already been fetched */
- if (cdata->got_alternates == 1)
- return;
- /* Start the fetch */
- cdata->got_alternates = 0;
- if (walker->get_verbosely)
- fprintf(stderr, "Getting alternates list for %s\n", base);
- strbuf_addf(&url, "%s/objects/info/http-alternates", base);
- /*
- * Use a callback to process the result, since another request
- * may fail and need to have alternates loaded before continuing
- */
- slot = get_active_slot();
- slot->callback_func = process_alternates_response;
- alt_req.walker = walker;
- slot->callback_data = &alt_req;
- curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
- curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
- curl_easy_setopt(slot->curl, CURLOPT_URL, url.buf);
- alt_req.base = base;
- alt_req.url = &url;
- alt_req.buffer = &buffer;
- alt_req.http_specific = 1;
- alt_req.slot = slot;
- if (start_active_slot(slot))
- run_active_slot(slot);
- else
- cdata->got_alternates = -1;
- strbuf_release(&buffer);
- strbuf_release(&url);
- }
- static int fetch_indices(struct walker *walker, struct alt_base *repo)
- {
- int ret;
- if (repo->got_indices)
- return 0;
- if (walker->get_verbosely)
- fprintf(stderr, "Getting pack list for %s\n", repo->base);
- switch (http_get_info_packs(repo->base, &repo->packs)) {
- case HTTP_OK:
- case HTTP_MISSING_TARGET:
- repo->got_indices = 1;
- ret = 0;
- break;
- default:
- repo->got_indices = 0;
- ret = -1;
- }
- return ret;
- }
- static int http_fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
- {
- struct packed_git *target;
- int ret;
- struct slot_results results;
- struct http_pack_request *preq;
- if (fetch_indices(walker, repo))
- return -1;
- target = find_sha1_pack(sha1, repo->packs);
- if (!target)
- return -1;
- close_pack_index(target);
- if (walker->get_verbosely) {
- fprintf(stderr, "Getting pack %s\n",
- hash_to_hex(target->hash));
- fprintf(stderr, " which contains %s\n",
- hash_to_hex(sha1));
- }
- preq = new_http_pack_request(target->hash, repo->base);
- if (preq == NULL)
- goto abort;
- preq->slot->results = &results;
- if (start_active_slot(preq->slot)) {
- run_active_slot(preq->slot);
- if (results.curl_result != CURLE_OK) {
- error("Unable to get pack file %s\n%s", preq->url,
- curl_errorstr);
- goto abort;
- }
- } else {
- error("Unable to start request");
- goto abort;
- }
- ret = finish_http_pack_request(preq);
- release_http_pack_request(preq);
- if (ret)
- return ret;
- http_install_packfile(target, &repo->packs);
- return 0;
- abort:
- return -1;
- }
- static void abort_object_request(struct object_request *obj_req)
- {
- release_object_request(obj_req);
- }
- static int fetch_object(struct walker *walker, unsigned char *hash)
- {
- char *hex = hash_to_hex(hash);
- int ret = 0;
- struct object_request *obj_req = NULL;
- struct http_object_request *req;
- struct list_head *pos, *head = &object_queue_head;
- list_for_each(pos, head) {
- obj_req = list_entry(pos, struct object_request, node);
- if (hasheq(obj_req->oid.hash, hash))
- break;
- }
- if (obj_req == NULL)
- return error("Couldn't find request for %s in the queue", hex);
- if (has_object_file(&obj_req->oid)) {
- if (obj_req->req != NULL)
- abort_http_object_request(obj_req->req);
- abort_object_request(obj_req);
- return 0;
- }
- #ifdef USE_CURL_MULTI
- while (obj_req->state == WAITING)
- step_active_slots();
- #else
- start_object_request(walker, obj_req);
- #endif
- /*
- * obj_req->req might change when fetching alternates in the callback
- * process_object_response; therefore, the "shortcut" variable, req,
- * is used only after we're done with slots.
- */
- while (obj_req->state == ACTIVE)
- run_active_slot(obj_req->req->slot);
- req = obj_req->req;
- if (req->localfile != -1) {
- close(req->localfile);
- req->localfile = -1;
- }
- normalize_curl_result(&req->curl_result, req->http_code,
- req->errorstr, sizeof(req->errorstr));
- if (obj_req->state == ABORTED) {
- ret = error("Request for %s aborted", hex);
- } else if (req->curl_result != CURLE_OK &&
- req->http_code != 416) {
- if (missing_target(req))
- ret = -1; /* Be silent, it is probably in a pack. */
- else
- ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
- req->errorstr, req->curl_result,
- req->http_code, hex);
- } else if (req->zret != Z_STREAM_END) {
- walker->corrupt_object_found++;
- ret = error("File %s (%s) corrupt", hex, req->url);
- } else if (!oideq(&obj_req->oid, &req->real_oid)) {
- ret = error("File %s has bad hash", hex);
- } else if (req->rename < 0) {
- struct strbuf buf = STRBUF_INIT;
- loose_object_path(the_repository, &buf, &req->oid);
- ret = error("unable to write sha1 filename %s", buf.buf);
- strbuf_release(&buf);
- }
- release_http_object_request(req);
- release_object_request(obj_req);
- return ret;
- }
- static int fetch(struct walker *walker, unsigned char *hash)
- {
- struct walker_data *data = walker->data;
- struct alt_base *altbase = data->alt;
- if (!fetch_object(walker, hash))
- return 0;
- while (altbase) {
- if (!http_fetch_pack(walker, altbase, hash))
- return 0;
- fetch_alternates(walker, data->alt->base);
- altbase = altbase->next;
- }
- return error("Unable to find %s under %s", hash_to_hex(hash),
- data->alt->base);
- }
- static int fetch_ref(struct walker *walker, struct ref *ref)
- {
- struct walker_data *data = walker->data;
- return http_fetch_ref(data->alt->base, ref);
- }
- static void cleanup(struct walker *walker)
- {
- struct walker_data *data = walker->data;
- struct alt_base *alt, *alt_next;
- if (data) {
- alt = data->alt;
- while (alt) {
- alt_next = alt->next;
- free(alt->base);
- free(alt);
- alt = alt_next;
- }
- free(data);
- walker->data = NULL;
- }
- }
- struct walker *get_http_walker(const char *url)
- {
- char *s;
- struct walker_data *data = xmalloc(sizeof(struct walker_data));
- struct walker *walker = xmalloc(sizeof(struct walker));
- data->alt = xmalloc(sizeof(*data->alt));
- data->alt->base = xstrdup(url);
- for (s = data->alt->base + strlen(data->alt->base) - 1; *s == '/'; --s)
- *s = 0;
- data->alt->got_indices = 0;
- data->alt->packs = NULL;
- data->alt->next = NULL;
- data->got_alternates = -1;
- walker->corrupt_object_found = 0;
- walker->fetch = fetch;
- walker->fetch_ref = fetch_ref;
- walker->prefetch = prefetch;
- walker->cleanup = cleanup;
- walker->data = data;
- #ifdef USE_CURL_MULTI
- add_fill_function(walker, (int (*)(void *)) fill_active_slot);
- #endif
- return walker;
- }
|