summaryrefslogtreecommitdiff
path: root/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'http.c')
-rw-r--r--http.c461
1 files changed, 319 insertions, 142 deletions
diff --git a/http.c b/http.c
index 29de6bbb23..62aa995245 100644
--- a/http.c
+++ b/http.c
@@ -14,6 +14,7 @@
#include "packfile.h"
#include "protocol.h"
#include "string-list.h"
+#include "object-store.h"
static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
static int trace_curl_data = 1;
@@ -47,6 +48,7 @@ char curl_errorstr[CURL_ERROR_SIZE];
static int curl_ssl_verify = -1;
static int curl_ssl_try;
+static const char *curl_http_version = NULL;
static const char *ssl_cert;
static const char *ssl_cipherlist;
static const char *ssl_version;
@@ -62,6 +64,9 @@ static struct {
{ "tlsv1.1", CURL_SSLVERSION_TLSv1_1 },
{ "tlsv1.2", CURL_SSLVERSION_TLSv1_2 },
#endif
+#if LIBCURL_VERSION_NUM >= 0x073400
+ { "tlsv1.3", CURL_SSLVERSION_TLSv1_3 },
+#endif
};
#if LIBCURL_VERSION_NUM >= 0x070903
static const char *ssl_key;
@@ -81,6 +86,13 @@ static long curl_low_speed_time = -1;
static int curl_ftp_no_epsv;
static const char *curl_http_proxy;
static const char *http_proxy_authmethod;
+
+static const char *http_proxy_ssl_cert;
+static const char *http_proxy_ssl_key;
+static const char *http_proxy_ssl_ca_info;
+static struct credential proxy_cert_auth = CREDENTIAL_INIT;
+static int proxy_ssl_cert_password_required;
+
static struct {
const char *name;
long curlauth_param;
@@ -145,12 +157,22 @@ static unsigned long empty_auth_useless =
static struct curl_slist *pragma_header;
static struct curl_slist *no_pragma_header;
-static struct curl_slist *extra_http_headers;
+static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
static struct active_request_slot *active_queue_head;
static char *cached_accept_language;
+static char *http_ssl_backend;
+
+static int http_schannel_check_revoke = 1;
+/*
+ * With the backend being set to `schannel`, setting sslCAinfo would override
+ * the Certificate Store in cURL v7.60.0 and later, which is not what we want
+ * by default.
+ */
+static int http_schannel_use_ssl_cainfo;
+
size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
{
size_t size = eltsize * nmemb;
@@ -161,7 +183,7 @@ size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
memcpy(ptr, buffer->buf.buf + buffer->posn, size);
buffer->posn += size;
- return size;
+ return size / eltsize;
}
#ifndef NO_CURL_IOCTL
@@ -189,12 +211,12 @@ size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
struct strbuf *buffer = buffer_;
strbuf_add(buffer, ptr, size);
- return size;
+ return nmemb;
}
size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
{
- return eltsize * nmemb;
+ return nmemb;
}
static void closedown_active_slot(struct active_request_slot *slot)
@@ -270,6 +292,9 @@ static void process_curl_messages(void)
static int http_options(const char *var, const char *value, void *cb)
{
+ if (!strcmp("http.version", var)) {
+ return git_config_string(&curl_http_version, var, value);
+ }
if (!strcmp("http.sslverify", var)) {
curl_ssl_verify = git_config_bool(var, value);
return 0;
@@ -298,6 +323,22 @@ static int http_options(const char *var, const char *value, void *cb)
curl_ssl_try = git_config_bool(var, value);
return 0;
}
+ if (!strcmp("http.sslbackend", var)) {
+ free(http_ssl_backend);
+ http_ssl_backend = xstrdup_or_null(value);
+ return 0;
+ }
+
+ if (!strcmp("http.schannelcheckrevoke", var)) {
+ http_schannel_check_revoke = git_config_bool(var, value);
+ return 0;
+ }
+
+ if (!strcmp("http.schannelusesslcainfo", var)) {
+ http_schannel_use_ssl_cainfo = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp("http.minsessions", var)) {
min_curl_sessions = git_config_int(var, value);
#ifndef USE_CURL_MULTI
@@ -331,6 +372,20 @@ static int http_options(const char *var, const char *value, void *cb)
if (!strcmp("http.proxyauthmethod", var))
return git_config_string(&http_proxy_authmethod, var, value);
+ if (!strcmp("http.proxysslcert", var))
+ return git_config_string(&http_proxy_ssl_cert, var, value);
+
+ if (!strcmp("http.proxysslkey", var))
+ return git_config_string(&http_proxy_ssl_key, var, value);
+
+ if (!strcmp("http.proxysslcainfo", var))
+ return git_config_string(&http_proxy_ssl_ca_info, var, value);
+
+ if (!strcmp("http.proxysslcertpasswordprotected", var)) {
+ proxy_ssl_cert_password_required = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp("http.cookiefile", var))
return git_config_pathname(&curl_cookie_file, var, value);
if (!strcmp("http.savecookies", var)) {
@@ -380,11 +435,9 @@ static int http_options(const char *var, const char *value, void *cb)
if (!value) {
return config_error_nonbool(var);
} else if (!*value) {
- curl_slist_free_all(extra_http_headers);
- extra_http_headers = NULL;
+ string_list_clear(&extra_http_headers, 0);
} else {
- extra_http_headers =
- curl_slist_append(extra_http_headers, value);
+ string_list_append(&extra_http_headers, value);
}
return 0;
}
@@ -479,9 +532,11 @@ static void set_proxyauth_name_password(CURL *result)
#else
struct strbuf s = STRBUF_INIT;
- strbuf_addstr_urlencode(&s, proxy_auth.username, 1);
+ strbuf_addstr_urlencode(&s, proxy_auth.username,
+ is_rfc3986_unreserved);
strbuf_addch(&s, ':');
- strbuf_addstr_urlencode(&s, proxy_auth.password, 1);
+ strbuf_addstr_urlencode(&s, proxy_auth.password,
+ is_rfc3986_unreserved);
curl_proxyuserpwd = strbuf_detach(&s, NULL);
curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd);
#endif
@@ -532,6 +587,22 @@ static int has_cert_password(void)
return 1;
}
+#if LIBCURL_VERSION_NUM >= 0x073400
+static int has_proxy_cert_password(void)
+{
+ if (http_proxy_ssl_cert == NULL || proxy_ssl_cert_password_required != 1)
+ return 0;
+ if (!proxy_cert_auth.password) {
+ proxy_cert_auth.protocol = xstrdup("cert");
+ proxy_cert_auth.host = xstrdup("");
+ proxy_cert_auth.username = xstrdup("");
+ proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
+ credential_fill(&proxy_cert_auth);
+ }
+ return 1;
+}
+#endif
+
#if LIBCURL_VERSION_NUM >= 0x071900
static void set_curl_keepalive(CURL *c)
{
@@ -647,8 +718,8 @@ static void curl_dump_header(const char *text, unsigned char *ptr, size_t size,
for (header = headers; *header; header++) {
if (hide_sensitive_header)
redact_sensitive_header(*header);
- strbuf_insert((*header), 0, text, strlen(text));
- strbuf_insert((*header), strlen(text), ": ", 2);
+ strbuf_insertstr((*header), 0, text);
+ strbuf_insertstr((*header), strlen(text), ": ");
strbuf_rtrim((*header));
strbuf_addch((*header), '\n');
trace_strbuf(&trace_curl, (*header));
@@ -760,6 +831,31 @@ static long get_curl_allowed_protocols(int from_user)
}
#endif
+#if LIBCURL_VERSION_NUM >=0x072f00
+static int get_curl_http_version_opt(const char *version_string, long *opt)
+{
+ int i;
+ static struct {
+ const char *name;
+ long opt_token;
+ } choice[] = {
+ { "HTTP/1.1", CURL_HTTP_VERSION_1_1 },
+ { "HTTP/2", CURL_HTTP_VERSION_2 }
+ };
+
+ for (i = 0; i < ARRAY_SIZE(choice); i++) {
+ if (!strcmp(version_string, choice[i].name)) {
+ *opt = choice[i].opt_token;
+ return 0;
+ }
+ }
+
+ warning("unknown value given to http.version: '%s'", version_string);
+ return -1; /* not found */
+}
+
+#endif
+
static CURL *get_curl_handle(void)
{
CURL *result = curl_easy_init();
@@ -777,6 +873,16 @@ static CURL *get_curl_handle(void)
curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
}
+#if LIBCURL_VERSION_NUM >= 0x072f00 // 7.47.0
+ if (curl_http_version) {
+ long opt;
+ if (!get_curl_http_version_opt(curl_http_version, &opt)) {
+ /* Set request use http version */
+ curl_easy_setopt(result, CURLOPT_HTTP_VERSION, opt);
+ }
+ }
+#endif
+
#if LIBCURL_VERSION_NUM >= 0x070907
curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
#endif
@@ -800,6 +906,15 @@ static CURL *get_curl_handle(void)
}
#endif
+ if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
+ !http_schannel_check_revoke) {
+#if LIBCURL_VERSION_NUM >= 0x072c00
+ curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
+#else
+ warning(_("CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"));
+#endif
+ }
+
if (http_proactive_auth)
init_curl_http_auth(result);
@@ -841,8 +956,20 @@ static CURL *get_curl_handle(void)
if (ssl_pinnedkey != NULL)
curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey);
#endif
- if (ssl_cainfo != NULL)
- curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
+ if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
+ !http_schannel_use_ssl_cainfo) {
+ curl_easy_setopt(result, CURLOPT_CAINFO, NULL);
+#if LIBCURL_VERSION_NUM >= 0x073400
+ curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
+#endif
+ } else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) {
+ if (ssl_cainfo != NULL)
+ curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
+#if LIBCURL_VERSION_NUM >= 0x073400
+ if (http_proxy_ssl_ca_info != NULL)
+ curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);
+#endif
+ }
if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
@@ -863,8 +990,7 @@ static CURL *get_curl_handle(void)
curl_easy_setopt(result, CURLOPT_PROTOCOLS,
get_curl_allowed_protocols(-1));
#else
- warning("protocol restrictions not applied to curl redirects because\n"
- "your curl version is too old (>= 7.19.4)");
+ warning(_("Protocol restrictions not supported with cURL < 7.19.4"));
#endif
if (getenv("GIT_CURL_VERBOSE"))
curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
@@ -936,9 +1062,18 @@ static CURL *get_curl_handle(void)
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
#endif
#if LIBCURL_VERSION_NUM >= 0x073400
- else if (starts_with(curl_http_proxy, "https"))
- curl_easy_setopt(result,
- CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
+ else if (starts_with(curl_http_proxy, "https")) {
+ curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
+
+ if (http_proxy_ssl_cert)
+ curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
+
+ if (http_proxy_ssl_key)
+ curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
+
+ if (has_proxy_cert_password())
+ curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
+ }
#endif
if (strstr(curl_http_proxy, "://"))
credential_from_url(&proxy_auth, curl_http_proxy);
@@ -973,21 +1108,6 @@ static void set_from_env(const char **var, const char *envname)
*var = val;
}
-static void protocol_http_header(void)
-{
- if (get_protocol_version_config() > 0) {
- struct strbuf protocol_header = STRBUF_INIT;
-
- strbuf_addf(&protocol_header, GIT_PROTOCOL_HEADER ": version=%d",
- get_protocol_version_config());
-
-
- extra_http_headers = curl_slist_append(extra_http_headers,
- protocol_header.buf);
- strbuf_release(&protocol_header);
- }
-}
-
void http_init(struct remote *remote, const char *url, int proactive_auth)
{
char *low_speed_limit;
@@ -1006,6 +1126,34 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
git_config(urlmatch_config_entry, &config);
free(normalized_url);
+ string_list_clear(&config.vars, 1);
+
+#if LIBCURL_VERSION_NUM >= 0x073800
+ if (http_ssl_backend) {
+ const curl_ssl_backend **backends;
+ struct strbuf buf = STRBUF_INIT;
+ int i;
+
+ switch (curl_global_sslset(-1, http_ssl_backend, &backends)) {
+ case CURLSSLSET_UNKNOWN_BACKEND:
+ strbuf_addf(&buf, _("Unsupported SSL backend '%s'. "
+ "Supported SSL backends:"),
+ http_ssl_backend);
+ for (i = 0; backends[i]; i++)
+ strbuf_addf(&buf, "\n\t%s", backends[i]->name);
+ die("%s", buf.buf);
+ case CURLSSLSET_NO_BACKENDS:
+ die(_("Could not set SSL backend to '%s': "
+ "cURL was built without SSL backends"),
+ http_ssl_backend);
+ case CURLSSLSET_TOO_LATE:
+ die(_("Could not set SSL backend to '%s': already set"),
+ http_ssl_backend);
+ case CURLSSLSET_OK:
+ break; /* Okay! */
+ }
+ }
+#endif
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
die("curl_global_init failed");
@@ -1018,8 +1166,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
if (remote)
var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
- protocol_http_header();
-
pragma_header = curl_slist_append(http_copy_default_headers(),
"Pragma: no-cache");
no_pragma_header = curl_slist_append(http_copy_default_headers(),
@@ -1067,6 +1213,13 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
max_requests = DEFAULT_MAX_REQUESTS;
#endif
+ set_from_env(&http_proxy_ssl_cert, "GIT_PROXY_SSL_CERT");
+ set_from_env(&http_proxy_ssl_key, "GIT_PROXY_SSL_KEY");
+ set_from_env(&http_proxy_ssl_ca_info, "GIT_PROXY_SSL_CAINFO");
+
+ if (getenv("GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED"))
+ proxy_ssl_cert_password_required = 1;
+
if (getenv("GIT_CURL_FTP_NO_EPSV"))
curl_ftp_no_epsv = 1;
@@ -1107,8 +1260,7 @@ void http_cleanup(void)
#endif
curl_global_cleanup();
- curl_slist_free_all(extra_http_headers);
- extra_http_headers = NULL;
+ string_list_clear(&extra_http_headers, 0);
curl_slist_free_all(pragma_header);
pragma_header = NULL;
@@ -1138,6 +1290,12 @@ void http_cleanup(void)
}
ssl_cert_password_required = 0;
+ if (proxy_cert_auth.password != NULL) {
+ memset(proxy_cert_auth.password, 0, strlen(proxy_cert_auth.password));
+ FREE_AND_NULL(proxy_cert_auth.password);
+ }
+ proxy_ssl_cert_password_required = 0;
+
FREE_AND_NULL(cached_accept_language);
}
@@ -1452,7 +1610,8 @@ char *get_remote_object_url(const char *url, const char *hex,
return strbuf_detach(&buf, NULL);
}
-static int handle_curl_result(struct slot_results *results)
+void normalize_curl_result(CURLcode *result, long http_code,
+ char *errorstr, size_t errorlen)
{
/*
* If we see a failing http code with CURLE_OK, we have turned off
@@ -1462,19 +1621,24 @@ static int handle_curl_result(struct slot_results *results)
* Likewise, if we see a redirect (30x code), that means we turned off
* redirect-following, and we should treat the result as an error.
*/
- if (results->curl_result == CURLE_OK &&
- results->http_code >= 300) {
- results->curl_result = CURLE_HTTP_RETURNED_ERROR;
+ if (*result == CURLE_OK && http_code >= 300) {
+ *result = CURLE_HTTP_RETURNED_ERROR;
/*
* Normally curl will already have put the "reason phrase"
* from the server into curl_errorstr; unfortunately without
* FAILONERROR it is lost, so we can give only the numeric
* status code.
*/
- xsnprintf(curl_errorstr, sizeof(curl_errorstr),
+ xsnprintf(errorstr, errorlen,
"The requested URL returned error: %ld",
- results->http_code);
+ http_code);
}
+}
+
+static int handle_curl_result(struct slot_results *results)
+{
+ normalize_curl_result(&results->curl_result, results->http_code,
+ curl_errorstr, sizeof(curl_errorstr));
if (results->curl_result == CURLE_OK) {
credential_approve(&http_auth);
@@ -1526,10 +1690,11 @@ int run_one_slot(struct active_request_slot *slot,
struct curl_slist *http_copy_default_headers(void)
{
- struct curl_slist *headers = NULL, *h;
+ struct curl_slist *headers = NULL;
+ const struct string_list_item *item;
- for (h = extra_http_headers; h; h = h->next)
- headers = curl_slist_append(headers, h->data);
+ for_each_string_list_item(item, &extra_http_headers)
+ headers = curl_slist_append(headers, item->string);
return headers;
}
@@ -1784,17 +1949,24 @@ static int http_request(const char *url,
strbuf_addstr(&buf, "Pragma:");
if (options && options->no_cache)
strbuf_addstr(&buf, " no-cache");
- if (options && options->keep_error)
- curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
if (options && options->initial_request &&
http_follow_config == HTTP_FOLLOW_INITIAL)
curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
headers = curl_slist_append(headers, buf.buf);
+ /* Add additional headers here */
+ if (options && options->extra_headers) {
+ const struct string_list_item *item;
+ for_each_string_list_item(item, options->extra_headers) {
+ headers = curl_slist_append(headers, item->string);
+ }
+ }
+
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
+ curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
+ curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
ret = run_one_slot(slot, &results);
@@ -1852,7 +2024,7 @@ static int update_url_from_redirect(struct strbuf *base,
return 0;
if (!skip_prefix(asked, base->buf, &tail))
- die("BUG: update_url_from_redirect: %s is not a superset of %s",
+ BUG("update_url_from_redirect: %s is not a superset of %s",
asked, base->buf);
new_len = got->len;
@@ -1889,19 +2061,26 @@ static int http_request_reauth(const char *url,
return ret;
/*
- * If we are using KEEP_ERROR, the previous request may have
- * put cruft into our output stream; we should clear it out before
- * making our next request. We only know how to do this for
- * the strbuf case, but that is enough to satisfy current callers.
+ * The previous request may have put cruft into our output stream; we
+ * should clear it out before making our next request.
*/
- if (options && options->keep_error) {
- switch (target) {
- case HTTP_REQUEST_STRBUF:
- strbuf_reset(result);
- break;
- default:
- die("BUG: HTTP_KEEP_ERROR is only supported with strbufs");
+ switch (target) {
+ case HTTP_REQUEST_STRBUF:
+ strbuf_reset(result);
+ break;
+ case HTTP_REQUEST_FILE:
+ if (fflush(result)) {
+ error_errno("unable to flush a file");
+ return HTTP_START_FAILED;
}
+ rewind(result);
+ if (ftruncate(fileno(result), 0) < 0) {
+ error_errno("unable to truncate a file");
+ return HTTP_START_FAILED;
+ }
+ break;
+ default:
+ BUG("Unknown http_request target");
}
credential_fill(&http_auth);
@@ -1959,7 +2138,7 @@ int http_fetch_ref(const char *base, struct ref *ref)
url = quote_ref_url(base, ref->name);
if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {
strbuf_rtrim(&buffer);
- if (buffer.len == 40)
+ if (buffer.len == the_hash_algo->hexsz)
ret = get_oid_hex(buffer.buf, &ref->old_oid);
else if (starts_with(buffer.buf, "ref: ")) {
ref->symref = xstrdup(buffer.buf + 5);
@@ -1973,19 +2152,19 @@ int http_fetch_ref(const char *base, struct ref *ref)
}
/* Helpers for fetching packs */
-static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
+static char *fetch_pack_index(unsigned char *hash, const char *base_url)
{
char *url, *tmp;
struct strbuf buf = STRBUF_INIT;
if (http_is_verbose)
- fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
+ fprintf(stderr, "Getting index for pack %s\n", hash_to_hex(hash));
end_url_with_slash(&buf, base_url);
- strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
+ strbuf_addf(&buf, "objects/pack/pack-%s.idx", hash_to_hex(hash));
url = strbuf_detach(&buf, NULL);
- strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
+ strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(hash));
tmp = strbuf_detach(&buf, NULL);
if (http_get_file(url, tmp, NULL) != HTTP_OK) {
@@ -2041,10 +2220,11 @@ add_pack:
int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
{
struct http_get_options options = {0};
- int ret = 0, i = 0;
- char *url, *data;
+ int ret = 0;
+ char *url;
+ const char *data;
struct strbuf buf = STRBUF_INIT;
- unsigned char sha1[20];
+ struct object_id oid;
end_url_with_slash(&buf, base_url);
strbuf_addstr(&buf, "objects/info/packs");
@@ -2056,24 +2236,17 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
goto cleanup;
data = buf.buf;
- while (i < buf.len) {
- switch (data[i]) {
- case 'P':
- i++;
- if (i + 52 <= buf.len &&
- starts_with(data + i, " pack-") &&
- starts_with(data + i + 46, ".pack\n")) {
- get_sha1_hex(data + i + 6, sha1);
- fetch_and_setup_pack_index(packs_head, sha1,
- base_url);
- i += 51;
- break;
- }
- default:
- while (i < buf.len && data[i] != '\n')
- i++;
+ while (*data) {
+ if (skip_prefix(data, "P pack-", &data) &&
+ !parse_oid_hex(data, &oid, &data) &&
+ skip_prefix(data, ".pack", &data) &&
+ (*data == '\n' || *data == '\0')) {
+ fetch_and_setup_pack_index(packs_head, oid.hash, base_url);
+ } else {
+ data = strchrnul(data, '\n');
}
- i++;
+ if (*data)
+ data++; /* skip past newline */
}
cleanup:
@@ -2088,6 +2261,7 @@ void release_http_pack_request(struct http_pack_request *preq)
preq->packfile = NULL;
}
preq->slot = NULL;
+ strbuf_release(&preq->tmpfile);
free(preq->url);
free(preq);
}
@@ -2110,33 +2284,33 @@ int finish_http_pack_request(struct http_pack_request *preq)
lst = &((*lst)->next);
*lst = (*lst)->next;
- if (!strip_suffix(preq->tmpfile, ".pack.temp", &len))
- die("BUG: pack tmpfile does not end in .pack.temp?");
- tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile);
+ if (!strip_suffix(preq->tmpfile.buf, ".pack.temp", &len))
+ BUG("pack tmpfile does not end in .pack.temp?");
+ tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile.buf);
argv_array_push(&ip.args, "index-pack");
argv_array_pushl(&ip.args, "-o", tmp_idx, NULL);
- argv_array_push(&ip.args, preq->tmpfile);
+ argv_array_push(&ip.args, preq->tmpfile.buf);
ip.git_cmd = 1;
ip.no_stdin = 1;
ip.no_stdout = 1;
if (run_command(&ip)) {
- unlink(preq->tmpfile);
+ unlink(preq->tmpfile.buf);
unlink(tmp_idx);
free(tmp_idx);
return -1;
}
- unlink(sha1_pack_index_name(p->sha1));
+ unlink(sha1_pack_index_name(p->hash));
- if (finalize_object_file(preq->tmpfile, sha1_pack_name(p->sha1))
- || finalize_object_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
+ if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->hash))
+ || finalize_object_file(tmp_idx, sha1_pack_index_name(p->hash))) {
free(tmp_idx);
return -1;
}
- install_packed_git(p);
+ install_packed_git(the_repository, p);
free(tmp_idx);
return 0;
}
@@ -2149,19 +2323,19 @@ struct http_pack_request *new_http_pack_request(
struct http_pack_request *preq;
preq = xcalloc(1, sizeof(*preq));
+ strbuf_init(&preq->tmpfile, 0);
preq->target = target;
end_url_with_slash(&buf, base_url);
strbuf_addf(&buf, "objects/pack/pack-%s.pack",
- sha1_to_hex(target->sha1));
+ hash_to_hex(target->hash));
preq->url = strbuf_detach(&buf, NULL);
- snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
- sha1_pack_name(target->sha1));
- preq->packfile = fopen(preq->tmpfile, "a");
+ strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->hash));
+ preq->packfile = fopen(preq->tmpfile.buf, "a");
if (!preq->packfile) {
error("Unable to open local file %s for pack",
- preq->tmpfile);
+ preq->tmpfile.buf);
goto abort;
}
@@ -2181,13 +2355,15 @@ struct http_pack_request *new_http_pack_request(
if (http_is_verbose)
fprintf(stderr,
"Resuming fetch of pack %s at byte %"PRIuMAX"\n",
- sha1_to_hex(target->sha1), (uintmax_t)prev_posn);
+ hash_to_hex(target->hash),
+ (uintmax_t)prev_posn);
http_opt_request_remainder(preq->slot->curl, prev_posn);
}
return preq;
abort:
+ strbuf_release(&preq->tmpfile);
free(preq->url);
free(preq);
return NULL;
@@ -2207,17 +2383,17 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
CURLcode c = curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE,
&slot->http_code);
if (c != CURLE_OK)
- die("BUG: curl_easy_getinfo for HTTP code failed: %s",
+ BUG("curl_easy_getinfo for HTTP code failed: %s",
curl_easy_strerror(c));
if (slot->http_code >= 300)
- return size;
+ return nmemb;
}
do {
ssize_t retval = xwrite(freq->localfile,
(char *) ptr + posn, size - posn);
if (retval < 0)
- return posn;
+ return posn / eltsize;
posn += retval;
} while (posn < size);
@@ -2227,18 +2403,18 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
freq->stream.next_out = expn;
freq->stream.avail_out = sizeof(expn);
freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
- git_SHA1_Update(&freq->c, expn,
- sizeof(expn) - freq->stream.avail_out);
+ the_hash_algo->update_fn(&freq->c, expn,
+ sizeof(expn) - freq->stream.avail_out);
} while (freq->stream.avail_in && freq->zret == Z_OK);
- return size;
+ return nmemb;
}
struct http_object_request *new_http_object_request(const char *base_url,
- unsigned char *sha1)
+ const struct object_id *oid)
{
- char *hex = sha1_to_hex(sha1);
+ char *hex = oid_to_hex(oid);
struct strbuf filename = STRBUF_INIT;
- char prevfile[PATH_MAX];
+ struct strbuf prevfile = STRBUF_INIT;
int prevlocal;
char prev_buf[PREV_BUF_SIZE];
ssize_t prev_read = 0;
@@ -2246,46 +2422,47 @@ struct http_object_request *new_http_object_request(const char *base_url,
struct http_object_request *freq;
freq = xcalloc(1, sizeof(*freq));
- hashcpy(freq->sha1, sha1);
+ strbuf_init(&freq->tmpfile, 0);
+ oidcpy(&freq->oid, oid);
freq->localfile = -1;
- sha1_file_name(&filename, sha1);
- snprintf(freq->tmpfile, sizeof(freq->tmpfile),
- "%s.temp", filename.buf);
+ loose_object_path(the_repository, &filename, oid);
+ strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf);
- snprintf(prevfile, sizeof(prevfile), "%s.prev", filename.buf);
- unlink_or_warn(prevfile);
- rename(freq->tmpfile, prevfile);
- unlink_or_warn(freq->tmpfile);
+ strbuf_addf(&prevfile, "%s.prev", filename.buf);
+ unlink_or_warn(prevfile.buf);
+ rename(freq->tmpfile.buf, prevfile.buf);
+ unlink_or_warn(freq->tmpfile.buf);
strbuf_release(&filename);
if (freq->localfile != -1)
error("fd leakage in start: %d", freq->localfile);
- freq->localfile = open(freq->tmpfile,
+ freq->localfile = open(freq->tmpfile.buf,
O_WRONLY | O_CREAT | O_EXCL, 0666);
/*
* This could have failed due to the "lazy directory creation";
* try to mkdir the last path component.
*/
if (freq->localfile < 0 && errno == ENOENT) {
- char *dir = strrchr(freq->tmpfile, '/');
+ char *dir = strrchr(freq->tmpfile.buf, '/');
if (dir) {
*dir = 0;
- mkdir(freq->tmpfile, 0777);
+ mkdir(freq->tmpfile.buf, 0777);
*dir = '/';
}
- freq->localfile = open(freq->tmpfile,
+ freq->localfile = open(freq->tmpfile.buf,
O_WRONLY | O_CREAT | O_EXCL, 0666);
}
if (freq->localfile < 0) {
- error_errno("Couldn't create temporary file %s", freq->tmpfile);
+ error_errno("Couldn't create temporary file %s",
+ freq->tmpfile.buf);
goto abort;
}
git_inflate_init(&freq->stream);
- git_SHA1_Init(&freq->c);
+ the_hash_algo->init_fn(&freq->c);
freq->url = get_remote_object_url(base_url, hex, 0);
@@ -2293,7 +2470,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
* If a previous temp file is present, process what was already
* fetched.
*/
- prevlocal = open(prevfile, O_RDONLY);
+ prevlocal = open(prevfile.buf, O_RDONLY);
if (prevlocal != -1) {
do {
prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
@@ -2310,7 +2487,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
} while (prev_read > 0);
close(prevlocal);
}
- unlink_or_warn(prevfile);
+ unlink_or_warn(prevfile.buf);
+ strbuf_release(&prevfile);
/*
* Reset inflate/SHA1 if there was an error reading the previous temp
@@ -2319,13 +2497,13 @@ struct http_object_request *new_http_object_request(const char *base_url,
if (prev_read == -1) {
memset(&freq->stream, 0, sizeof(freq->stream));
git_inflate_init(&freq->stream);
- git_SHA1_Init(&freq->c);
+ the_hash_algo->init_fn(&freq->c);
if (prev_posn>0) {
prev_posn = 0;
lseek(freq->localfile, 0, SEEK_SET);
if (ftruncate(freq->localfile, 0) < 0) {
error_errno("Couldn't truncate temporary file %s",
- freq->tmpfile);
+ freq->tmpfile.buf);
goto abort;
}
}
@@ -2355,6 +2533,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
return freq;
abort:
+ strbuf_release(&prevfile);
free(freq->url);
free(freq);
return NULL;
@@ -2382,25 +2561,24 @@ int finish_http_object_request(struct http_object_request *freq)
if (freq->http_code == 416) {
warning("requested range invalid; we may already have all the data.");
} else if (freq->curl_result != CURLE_OK) {
- if (stat(freq->tmpfile, &st) == 0)
+ if (stat(freq->tmpfile.buf, &st) == 0)
if (st.st_size == 0)
- unlink_or_warn(freq->tmpfile);
+ unlink_or_warn(freq->tmpfile.buf);
return -1;
}
git_inflate_end(&freq->stream);
- git_SHA1_Final(freq->real_sha1, &freq->c);
+ the_hash_algo->final_fn(freq->real_oid.hash, &freq->c);
if (freq->zret != Z_STREAM_END) {
- unlink_or_warn(freq->tmpfile);
+ unlink_or_warn(freq->tmpfile.buf);
return -1;
}
- if (hashcmp(freq->sha1, freq->real_sha1)) {
- unlink_or_warn(freq->tmpfile);
+ if (!oideq(&freq->oid, &freq->real_oid)) {
+ unlink_or_warn(freq->tmpfile.buf);
return -1;
}
-
- sha1_file_name(&filename, freq->sha1);
- freq->rename = finalize_object_file(freq->tmpfile, filename.buf);
+ loose_object_path(the_repository, &filename, &freq->oid);
+ freq->rename = finalize_object_file(freq->tmpfile.buf, filename.buf);
strbuf_release(&filename);
return freq->rename;
@@ -2408,7 +2586,7 @@ int finish_http_object_request(struct http_object_request *freq)
void abort_http_object_request(struct http_object_request *freq)
{
- unlink_or_warn(freq->tmpfile);
+ unlink_or_warn(freq->tmpfile.buf);
release_http_object_request(freq);
}
@@ -2419,13 +2597,12 @@ void release_http_object_request(struct http_object_request *freq)
close(freq->localfile);
freq->localfile = -1;
}
- if (freq->url != NULL) {
- FREE_AND_NULL(freq->url);
- }
+ FREE_AND_NULL(freq->url);
if (freq->slot != NULL) {
freq->slot->callback_func = NULL;
freq->slot->callback_data = NULL;
release_active_slot(freq->slot);
freq->slot = NULL;
}
+ strbuf_release(&freq->tmpfile);
}