Commit 2b386acd by Carlos Martín Nieto Committed by Vicent Martí

error-handling: git transport

parent 84d250bf
...@@ -49,8 +49,10 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) ...@@ -49,8 +49,10 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
int len; int len;
delim = strchr(url, '/'); delim = strchr(url, '/');
if (delim == NULL) if (delim == NULL) {
return git__throw(GIT_EOBJCORRUPTED, "Failed to create proto-request: malformed URL"); giterr_set(GITERR_NET, "Malformed URL");
return -1;
}
repo = delim; repo = delim;
...@@ -80,7 +82,7 @@ static int send_request(GIT_SOCKET s, const char *cmd, const char *url) ...@@ -80,7 +82,7 @@ static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
git_buf request = GIT_BUF_INIT; git_buf request = GIT_BUF_INIT;
error = gen_proto(&request, cmd, url); error = gen_proto(&request, cmd, url);
if (error < GIT_SUCCESS) if (error < 0)
goto cleanup; goto cleanup;
error = gitno_send(s, request.ptr, request.size, 0); error = gitno_send(s, request.ptr, request.size, 0);
...@@ -105,9 +107,8 @@ static int do_connect(transport_git *t, const char *url) ...@@ -105,9 +107,8 @@ static int do_connect(transport_git *t, const char *url)
if (!git__prefixcmp(url, prefix)) if (!git__prefixcmp(url, prefix))
url += strlen(prefix); url += strlen(prefix);
error = gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT); if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)
if (error < GIT_SUCCESS) return -1;
return error;
s = gitno_connect(host, port); s = gitno_connect(host, port);
connected = 1; connected = 1;
...@@ -119,8 +120,10 @@ static int do_connect(transport_git *t, const char *url) ...@@ -119,8 +120,10 @@ static int do_connect(transport_git *t, const char *url)
if (error < GIT_SUCCESS && s > 0) if (error < GIT_SUCCESS && s > 0)
close(s); close(s);
if (!connected) if (!connected) {
error = git__throw(GIT_EOSERR, "Failed to connect to any of the addresses"); giterr_set(GITERR_NET, "Failed to connect to the host");
return -1;
}
return error; return error;
} }
...@@ -131,33 +134,30 @@ static int do_connect(transport_git *t, const char *url) ...@@ -131,33 +134,30 @@ static int do_connect(transport_git *t, const char *url)
static int store_refs(transport_git *t) static int store_refs(transport_git *t)
{ {
gitno_buffer *buf = &t->buf; gitno_buffer *buf = &t->buf;
int error = GIT_SUCCESS; int ret = 0;
while (1) { while (1) {
error = gitno_recv(buf); if ((ret = gitno_recv(buf)) < 0)
if (error < GIT_SUCCESS) return -1;
return git__rethrow(GIT_EOSERR, "Failed to receive data"); if (ret == 0) /* Orderly shutdown, so exit */
if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */ return 0;
return GIT_SUCCESS;
ret = git_protocol_store_refs(&t->proto, buf->data, buf->offset);
error = git_protocol_store_refs(&t->proto, buf->data, buf->offset); if (ret == GIT_ESHORTBUFFER) {
if (error == GIT_ESHORTBUFFER) {
gitno_consume_n(buf, buf->len); gitno_consume_n(buf, buf->len);
continue; continue;
} }
if (error < GIT_SUCCESS) if (ret < 0)
return git__rethrow(error, "Failed to store refs"); return ret;
gitno_consume_n(buf, buf->offset); gitno_consume_n(buf, buf->offset);
if (t->proto.flush) { /* No more refs */ if (t->proto.flush) { /* No more refs */
t->proto.flush = 0; t->proto.flush = 0;
return GIT_SUCCESS; return 0;
} }
} }
return error;
} }
static int detect_caps(transport_git *t) static int detect_caps(transport_git *t)
...@@ -170,7 +170,7 @@ static int detect_caps(transport_git *t) ...@@ -170,7 +170,7 @@ static int detect_caps(transport_git *t)
pkt = git_vector_get(refs, 0); pkt = git_vector_get(refs, 0);
/* No refs or capabilites, odd but not a problem */ /* No refs or capabilites, odd but not a problem */
if (pkt == NULL || pkt->capabilities == NULL) if (pkt == NULL || pkt->capabilities == NULL)
return GIT_SUCCESS; return 0;
ptr = pkt->capabilities; ptr = pkt->capabilities;
while (ptr != NULL && *ptr != '\0') { while (ptr != NULL && *ptr != '\0') {
...@@ -187,7 +187,7 @@ static int detect_caps(transport_git *t) ...@@ -187,7 +187,7 @@ static int detect_caps(transport_git *t)
ptr = strchr(ptr, ' '); ptr = strchr(ptr, ' ');
} }
return GIT_SUCCESS; return 0;
} }
/* /*
...@@ -197,36 +197,33 @@ static int detect_caps(transport_git *t) ...@@ -197,36 +197,33 @@ static int detect_caps(transport_git *t)
static int git_connect(git_transport *transport, int direction) static int git_connect(git_transport *transport, int direction)
{ {
transport_git *t = (transport_git *) transport; transport_git *t = (transport_git *) transport;
int error = GIT_SUCCESS;
if (direction == GIT_DIR_PUSH) if (direction == GIT_DIR_PUSH) {
return git__throw(GIT_EINVALIDARGS, "Pushing is not supported with the git protocol"); giterr_set(GITERR_NET, "Pushing over git:// is not supported");
return -1;
}
t->parent.direction = direction; t->parent.direction = direction;
error = git_vector_init(&t->refs, 16, NULL); if (git_vector_init(&t->refs, 16, NULL) < 0)
if (error < GIT_SUCCESS) return -1;
goto cleanup;
/* Connect and ask for the refs */ /* Connect and ask for the refs */
error = do_connect(t, transport->url); if (do_connect(t, transport->url) < 0)
if (error < GIT_SUCCESS) goto cleanup;
return error;
gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket); gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);
t->parent.connected = 1; t->parent.connected = 1;
error = store_refs(t); if (store_refs(t) < 0)
if (error < GIT_SUCCESS) goto cleanup;
return error;
error = detect_caps(t); if (detect_caps(t) < 0)
goto cleanup;
return 0;
cleanup: cleanup:
if (error < GIT_SUCCESS) { git_vector_free(&t->refs);
git_vector_free(&t->refs); return -1;
}
return error;
} }
static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque) static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
...@@ -244,12 +241,51 @@ static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaqu ...@@ -244,12 +241,51 @@ static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaqu
pkt = (git_pkt_ref *)p; pkt = (git_pkt_ref *)p;
if (list_cb(&pkt->head, opaque) < 0) if (list_cb(&pkt->head, opaque) < 0) {
return git__throw(GIT_ERROR, giterr_set(GITERR_NET, "User callback returned error");
"The user callback returned an error code"); return -1;
}
} }
return GIT_SUCCESS; return 0;
}
/* Wait until we get an ack from the */
static int recv_pkt(gitno_buffer *buf)
{
const char *ptr = buf->data, *line_end;
git_pkt *pkt;
int pkt_type, error;
do {
/* Wait for max. 1 second */
if ((error = gitno_select_in(buf, 1, 0)) < 0) {
return -1;
} else if (error == 0) {
/*
* Some servers don't respond immediately, so if this
* happens, we keep sending information until it
* answers. Pretend we received a NAK to convince higher
* layers to do so.
*/
return GIT_PKT_NAK;
}
if ((error = gitno_recv(buf)) < 0)
return -1;
error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
if (error == GIT_ESHORTBUFFER)
continue;
if (error < 0)
return -1;
} while (error);
gitno_consume(buf, line_end);
pkt_type = pkt->type;
git__free(pkt);
return pkt_type;
} }
static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants) static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
...@@ -263,19 +299,15 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c ...@@ -263,19 +299,15 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
unsigned int i; unsigned int i;
gitno_buffer *buf = &t->buf; gitno_buffer *buf = &t->buf;
error = git_pkt_send_wants(wants, &t->caps, t->socket); if (git_pkt_send_wants(wants, &t->caps, t->socket) < 0)
if (error < GIT_SUCCESS) return -1;
return git__rethrow(error, "Failed to send wants list");
error = git_reference_listall(&refs, repo, GIT_REF_LISTALL); if (git_reference_listall(&refs, repo, GIT_REF_LISTALL) < 0)
if (error < GIT_ERROR) return -1;
return git__rethrow(error, "Failed to list all references");
if (git_revwalk_new(&walk, repo) < 0)
return -1;
error = git_revwalk_new(&walk, repo);
if (error < GIT_ERROR) {
error = git__rethrow(error, "Failed to list all references");
goto cleanup;
}
git_revwalk_sorting(walk, GIT_SORT_TIME); git_revwalk_sorting(walk, GIT_SORT_TIME);
for (i = 0; i < refs.count; ++i) { for (i = 0; i < refs.count; ++i) {
...@@ -283,20 +315,15 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c ...@@ -283,20 +315,15 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR)) if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
continue; continue;
error = git_reference_lookup(&ref, repo, refs.strings[i]); if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
if (error < GIT_ERROR) { goto on_error;
error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]);
goto cleanup;
}
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
continue; continue;
error = git_revwalk_push(walk, git_reference_oid(ref)); if ((error = git_revwalk_push(walk, git_reference_oid(ref))) < 0)
if (error < GIT_ERROR) { goto on_error;
error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
goto cleanup;
}
} }
git_strarray_free(&refs); git_strarray_free(&refs);
...@@ -306,67 +333,38 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c ...@@ -306,67 +333,38 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
* every once in a while. * every once in a while.
*/ */
i = 0; i = 0;
while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) { while ((error = git_revwalk_next(&oid, walk)) == 0) {
error = git_pkt_send_have(&oid, t->socket); error = git_pkt_send_have(&oid, t->socket);
i++; i++;
if (i % 20 == 0) { if (i % 20 == 0) {
const char *ptr = buf->data, *line_end; int pkt_type;
git_pkt *pkt;
git_pkt_send_flush(t->socket); git_pkt_send_flush(t->socket);
while (1) { pkt_type = recv_pkt(buf);
/* Wait for max. 1 second */
error = gitno_select_in(buf, 1, 0); if (pkt_type == GIT_PKT_ACK) {
if (error < GIT_SUCCESS) { break;
error = git__throw(GIT_EOSERR, "Error in select"); } else if (pkt_type == GIT_PKT_NAK) {
} else if (error == 0) { continue;
/* } else {
* Some servers don't respond immediately, so if this giterr_set(GITERR_NET, "Unexpected pkt type");
* happens, we keep sending information until it goto on_error;
* answers.
*/
break;
}
error = gitno_recv(buf);
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Error receiving data");
goto cleanup;
}
error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
if (error == GIT_ESHORTBUFFER)
continue;
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Failed to get answer");
goto cleanup;
}
gitno_consume(buf, line_end);
if (pkt->type == GIT_PKT_ACK) {
git__free(pkt);
error = GIT_SUCCESS;
goto done;
} else if (pkt->type == GIT_PKT_NAK) {
git__free(pkt);
break;
} else {
error = git__throw(GIT_ERROR, "Got unexpected pkt type");
goto cleanup;
}
} }
} }
} }
if (error == GIT_EREVWALKOVER) if (error != GIT_EREVWALKOVER)
error = GIT_SUCCESS; goto on_error;
done:
git_pkt_send_flush(t->socket); git_pkt_send_flush(t->socket);
git_pkt_send_done(t->socket); git_pkt_send_done(t->socket);
cleanup:
git_revwalk_free(walk); git_revwalk_free(walk);
return 0;
return error; on_error:
git_revwalk_free(walk);
return -1;
} }
static int git_send_flush(git_transport *transport) static int git_send_flush(git_transport *transport)
...@@ -386,7 +384,7 @@ static int git_send_done(git_transport *transport) ...@@ -386,7 +384,7 @@ static int git_send_done(git_transport *transport)
static int git_download_pack(char **out, git_transport *transport, git_repository *repo) static int git_download_pack(char **out, git_transport *transport, git_repository *repo)
{ {
transport_git *t = (transport_git *) transport; transport_git *t = (transport_git *) transport;
int error = GIT_SUCCESS; int error = 0, read_bytes;
gitno_buffer *buf = &t->buf; gitno_buffer *buf = &t->buf;
git_pkt *pkt; git_pkt *pkt;
const char *line_end, *ptr; const char *line_end, *ptr;
...@@ -394,7 +392,7 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor ...@@ -394,7 +392,7 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
/* /*
* For now, we ignore everything and wait for the pack * For now, we ignore everything and wait for the pack
*/ */
while (1) { do {
ptr = buf->data; ptr = buf->data;
/* Whilst we're searching for the pack */ /* Whilst we're searching for the pack */
while (1) { while (1) {
...@@ -419,34 +417,29 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor ...@@ -419,34 +417,29 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
gitno_consume(buf, line_end); gitno_consume(buf, line_end);
} }
error = gitno_recv(buf); read_bytes = gitno_recv(buf);
if (error < GIT_SUCCESS) } while (read_bytes);
return git__rethrow(GIT_EOSERR, "Failed to receive data");
if (error == 0) { /* Orderly shutdown */
return GIT_SUCCESS;
}
} return read_bytes;
} }
static int git_close(git_transport *transport) static int git_close(git_transport *transport)
{ {
transport_git *t = (transport_git*) transport; transport_git *t = (transport_git*) transport;
int error;
/* Can't do anything if there's an error, so don't bother checking */ /* Can't do anything if there's an error, so don't bother checking */
git_pkt_send_flush(t->socket); git_pkt_send_flush(t->socket);
error = gitno_close(t->socket); if (gitno_close(t->socket) < 0) {
giterr_set(GITERR_NET, "Failed to close socket");
if (error < 0) return -1;
error = git__throw(GIT_EOSERR, "Failed to close socket"); }
#ifdef GIT_WIN32 #ifdef GIT_WIN32
WSACleanup(); WSACleanup();
#endif #endif
return error; return 0;
} }
static void git_free(git_transport *transport) static void git_free(git_transport *transport)
...@@ -475,8 +468,7 @@ int git_transport_git(git_transport **out) ...@@ -475,8 +468,7 @@ int git_transport_git(git_transport **out)
#endif #endif
t = git__malloc(sizeof(transport_git)); t = git__malloc(sizeof(transport_git));
if (t == NULL) GITERR_CHECK_ALLOC(t);
return GIT_ENOMEM;
memset(t, 0x0, sizeof(transport_git)); memset(t, 0x0, sizeof(transport_git));
...@@ -497,9 +489,10 @@ int git_transport_git(git_transport **out) ...@@ -497,9 +489,10 @@ int git_transport_git(git_transport **out)
ret = WSAStartup(MAKEWORD(2,2), &t->wsd); ret = WSAStartup(MAKEWORD(2,2), &t->wsd);
if (ret != 0) { if (ret != 0) {
git_free(*out); git_free(*out);
return git__throw(GIT_EOSERR, "Winsock init failed"); giterr_set(GITERR_NET, "Winsock init failed");
return -1;
} }
#endif #endif
return GIT_SUCCESS; return 0;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment