X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=http-fetch.c;h=7fc363f8ea7ca622330eb17260bf76f977875d03;hb=5acb6de13d7af33abcfcbdc885ec365e6a51c486;hp=1a433a9842d9bd0a6ed8735e60690e1a73bb8ea8;hpb=1db69b571c9eaab73a7b21303bfedda5fb5f31ce;p=git.git diff --git a/http-fetch.c b/http-fetch.c index 1a433a98..7fc363f8 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -15,8 +15,19 @@ static CURL *curl; static struct curl_slist *no_pragma_header; +static char curl_errorstr[CURL_ERROR_SIZE]; -static char *base; +static char *initial_base; + +struct alt_base +{ + char *base; + int got_indices; + struct packed_git *packs; + struct alt_base *next; +}; + +struct alt_base *alt = NULL; static SHA_CTX c; static z_stream stream; @@ -25,6 +36,10 @@ static int local; static int zret; static int curl_ssl_verify; +static char *ssl_cert; +static char *ssl_key; +static char *ssl_capath; +static char *ssl_cainfo; struct buffer { @@ -72,11 +87,9 @@ void prefetch(unsigned char *sha1) { } -static int got_indices = 0; +static int got_alternates = 0; -static struct packed_git *packs = NULL; - -static int fetch_index(unsigned char *sha1) +static int fetch_index(struct alt_base *repo, unsigned char *sha1) { char *filename; char *url; @@ -90,9 +103,9 @@ static int fetch_index(unsigned char *sha1) fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1)); - url = xmalloc(strlen(base) + 64); + url = xmalloc(strlen(repo->base) + 64); sprintf(url, "%s/objects/pack/pack-%s.idx", - base, sha1_to_hex(sha1)); + repo->base, sha1_to_hex(sha1)); filename = sha1_pack_index_name(sha1); indexfile = fopen(filename, "w"); @@ -104,32 +117,135 @@ static int fetch_index(unsigned char *sha1) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_pragma_header); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr); if (curl_easy_perform(curl)) { fclose(indexfile); - return error("Unable to get pack index %s", url); + return error("Unable to get pack index %s\n%s", url, + curl_errorstr); } fclose(indexfile); return 0; } -static int setup_index(unsigned char *sha1) +static int setup_index(struct alt_base *repo, unsigned char *sha1) { struct packed_git *new_pack; if (has_pack_file(sha1)) return 0; // don't list this as something we can get - if (fetch_index(sha1)) + if (fetch_index(repo, sha1)) return -1; new_pack = parse_pack_index(sha1); - new_pack->next = packs; - packs = new_pack; + new_pack->next = repo->packs; + repo->packs = new_pack; return 0; } -static int fetch_indices(void) +static int fetch_alternates(char *base) +{ + int ret = 0; + struct buffer buffer; + char *url; + char *data; + int i = 0; + int http_specific = 1; + if (got_alternates) + return 0; + data = xmalloc(4096); + buffer.size = 4095; + buffer.posn = 0; + buffer.buffer = data; + + if (get_verbosely) + fprintf(stderr, "Getting alternates list\n"); + + url = xmalloc(strlen(base) + 31); + sprintf(url, "%s/objects/info/http-alternates", base); + + curl_easy_setopt(curl, CURLOPT_FILE, &buffer); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); + curl_easy_setopt(curl, CURLOPT_URL, url); + + if (curl_easy_perform(curl) || !buffer.posn) { + http_specific = 0; + + sprintf(url, "%s/objects/info/alternates", base); + + curl_easy_setopt(curl, CURLOPT_FILE, &buffer); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); + curl_easy_setopt(curl, CURLOPT_URL, url); + + if (curl_easy_perform(curl)) { + return 0; + } + } + + data[buffer.posn] = '\0'; + + while (i < buffer.posn) { + int posn = i; + while (posn < buffer.posn && data[posn] != '\n') + posn++; + if (data[posn] == '\n') { + int okay = 0; + int serverlen = 0; + struct alt_base *newalt; + char *target = NULL; + if (data[i] == '/') { + serverlen = strchr(base + 8, '/') - base; + okay = 1; + } else if (!memcmp(data + i, "../", 3)) { + 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 (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; + } + } + // skip 'objects' at end + if (okay) { + target = xmalloc(serverlen + posn - i - 6); + strncpy(target, base, serverlen); + strncpy(target + serverlen, data + i, + posn - i - 7); + target[serverlen + posn - i - 7] = '\0'; + if (get_verbosely) + fprintf(stderr, + "Also look at %s\n", target); + newalt = xmalloc(sizeof(*newalt)); + newalt->next = alt; + newalt->base = target; + newalt->got_indices = 0; + newalt->packs = NULL; + alt = newalt; + ret++; + } + } + i = posn + 1; + } + got_alternates = 1; + + return ret; +} + +static int fetch_indices(struct alt_base *repo) { unsigned char sha1[20]; char *url; @@ -137,7 +253,7 @@ static int fetch_indices(void) char *data; int i = 0; - if (got_indices) + if (repo->got_indices) return 0; data = xmalloc(4096); @@ -148,19 +264,19 @@ static int fetch_indices(void) if (get_verbosely) fprintf(stderr, "Getting pack list\n"); - url = xmalloc(strlen(base) + 21); - sprintf(url, "%s/objects/info/packs", base); + url = xmalloc(strlen(repo->base) + 21); + sprintf(url, "%s/objects/info/packs", repo->base); curl_easy_setopt(curl, CURLOPT_FILE, &buffer); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr); - if (curl_easy_perform(curl)) { - return error("Unable to get pack index %s", url); - } + if (curl_easy_perform(curl)) + return error("%s", curl_errorstr); - do { + while (i < buffer.posn) { switch (data[i]) { case 'P': i++; @@ -168,7 +284,7 @@ static int fetch_indices(void) !strncmp(data + i, " pack-", 6) && !strncmp(data + i + 46, ".pack\n", 6)) { get_sha1_hex(data + i + 6, sha1); - setup_index(sha1); + setup_index(repo, sha1); i += 51; break; } @@ -177,13 +293,13 @@ static int fetch_indices(void) i++; } i++; - } while (i < buffer.posn); + } - got_indices = 1; + repo->got_indices = 1; return 0; } -static int fetch_pack(unsigned char *sha1) +static int fetch_pack(struct alt_base *repo, unsigned char *sha1) { char *url; struct packed_git *target; @@ -191,12 +307,11 @@ static int fetch_pack(unsigned char *sha1) FILE *packfile; char *filename; - if (fetch_indices()) + if (fetch_indices(repo)) return -1; - target = find_sha1_pack(sha1, packs); + target = find_sha1_pack(sha1, repo->packs); if (!target) - return error("Couldn't get %s: not separate or in any pack", - sha1_to_hex(sha1)); + return -1; if (get_verbosely) { fprintf(stderr, "Getting pack %s\n", @@ -205,9 +320,9 @@ static int fetch_pack(unsigned char *sha1) sha1_to_hex(sha1)); } - url = xmalloc(strlen(base) + 65); + url = xmalloc(strlen(repo->base) + 65); sprintf(url, "%s/objects/pack/pack-%s.pack", - base, sha1_to_hex(target->sha1)); + repo->base, sha1_to_hex(target->sha1)); filename = sha1_pack_name(target->sha1); packfile = fopen(filename, "w"); @@ -219,15 +334,17 @@ static int fetch_pack(unsigned char *sha1) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_pragma_header); - + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr); + if (curl_easy_perform(curl)) { fclose(packfile); - return error("Unable to get pack file %s", url); + return error("Unable to get pack file %s\n%s", url, + curl_errorstr); } fclose(packfile); - lst = &packs; + lst = &repo->packs; while (*lst != target) lst = &((*lst)->next); *lst = (*lst)->next; @@ -237,18 +354,23 @@ static int fetch_pack(unsigned char *sha1) return 0; } -int fetch(unsigned char *sha1) +int fetch_object(struct alt_base *repo, unsigned char *sha1) { char *hex = sha1_to_hex(sha1); char *filename = sha1_file_name(sha1); unsigned char real_sha1[20]; + char tmpfile[PATH_MAX]; + int ret; char *url; char *posn; - local = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); + snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", + get_object_directory()); + local = mkstemp(tmpfile); if (local < 0) - return error("Couldn't open local object %s\n", filename); + return error("Couldn't create temporary file %s for %s: %s\n", + tmpfile, filename, strerror(errno)); memset(&stream, 0, sizeof(stream)); @@ -260,10 +382,11 @@ int fetch(unsigned char *sha1) curl_easy_setopt(curl, CURLOPT_FILE, NULL); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, no_pragma_header); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr); - url = xmalloc(strlen(base) + 50); - strcpy(url, base); - posn = url + strlen(base); + url = xmalloc(strlen(repo->base) + 50); + strcpy(url, repo->base); + posn = url + strlen(repo->base); strcpy(posn, "objects/"); posn += 8; memcpy(posn, hex, 2); @@ -275,32 +398,63 @@ int fetch(unsigned char *sha1) if (curl_easy_perform(curl)) { unlink(filename); - if (fetch_pack(sha1)) - return error("Tried %s", url); - return 0; + return error("%s", curl_errorstr); } + fchmod(local, 0444); close(local); inflateEnd(&stream); SHA1_Final(real_sha1, &c); if (zret != Z_STREAM_END) { - unlink(filename); + unlink(tmpfile); return error("File %s (%s) corrupt\n", hex, url); } if (memcmp(sha1, real_sha1, 20)) { - unlink(filename); + unlink(tmpfile); return error("File %s has bad hash\n", hex); } - + ret = link(tmpfile, filename); + if (ret < 0) { + /* Same Coda hack as in write_sha1_file(sha1_file.c) */ + ret = errno; + if (ret == EXDEV && !rename(tmpfile, filename)) + goto out; + } + unlink(tmpfile); + if (ret) { + if (ret != EEXIST) + return error("unable to write sha1 filename %s: %s", + filename, strerror(ret)); + } + out: pull_say("got %s\n", hex); return 0; } +int fetch(unsigned char *sha1) +{ + struct alt_base *altbase = alt; + while (altbase) { + if (!fetch_object(altbase, sha1)) + return 0; + if (!fetch_pack(altbase, sha1)) + return 0; + if (fetch_alternates(altbase->base) > 0) { + altbase = alt; + continue; + } + altbase = altbase->next; + } + return error("Unable to find %s under %s\n", sha1_to_hex(sha1), + initial_base); +} + int fetch_ref(char *ref, unsigned char *sha1) { char *url, *posn; char hex[42]; struct buffer buffer; + char *base = initial_base; buffer.size = 41; buffer.posn = 0; buffer.buffer = hex; @@ -309,6 +463,7 @@ int fetch_ref(char *ref, unsigned char *sha1) curl_easy_setopt(curl, CURLOPT_FILE, &buffer); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errorstr); url = xmalloc(strlen(base) + 6 + strlen(ref)); strcpy(url, base); @@ -320,7 +475,8 @@ int fetch_ref(char *ref, unsigned char *sha1) curl_easy_setopt(curl, CURLOPT_URL, url); if (curl_easy_perform(curl)) - return error("Couldn't get %s for %s\n", url, ref); + return error("Couldn't get %s for %s\n%s", + url, ref, curl_errorstr); hex[40] = '\0'; get_sha1_hex(hex, sha1); @@ -347,6 +503,8 @@ int main(int argc, char **argv) } else if (argv[arg][1] == 'w') { write_ref = argv[arg + 1]; arg++; + } else if (!strcmp(argv[arg], "--recover")) { + get_recover = 1; } arg++; } @@ -368,7 +526,27 @@ int main(int argc, char **argv) curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); #endif - base = url; + if ((ssl_cert = getenv("GIT_SSL_CERT")) != NULL) { + curl_easy_setopt(curl, CURLOPT_SSLCERT, ssl_cert); + } + if ((ssl_key = getenv("GIT_SSL_KEY")) != NULL) { + curl_easy_setopt(curl, CURLOPT_SSLKEY, ssl_key); + } +#if LIBCURL_VERSION_NUM >= 0x070908 + if ((ssl_capath = getenv("GIT_SSL_CAPATH")) != NULL) { + curl_easy_setopt(curl, CURLOPT_CAPATH, ssl_capath); + } +#endif + if ((ssl_cainfo = getenv("GIT_SSL_CAINFO")) != NULL) { + curl_easy_setopt(curl, CURLOPT_CAINFO, ssl_cainfo); + } + + alt = xmalloc(sizeof(*alt)); + alt->base = url; + alt->got_indices = 0; + alt->packs = NULL; + alt->next = NULL; + initial_base = url; if (pull(commit_id)) return 1;