summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2022-12-02 00:45:13 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2023-02-12 22:02:00 +0000
commit53fcd5b8f5f069ee9a2567280dca81b74192da74 (patch)
tree17e9fa49386cdf35578070a7d5347ea842d42038
parent0006ff63359358bc5e65ea5a2b6ef7ba5eca2e8e (diff)
downloadlibgit2-53fcd5b8f5f069ee9a2567280dca81b74192da74.tar.gz
transport: teach transports about oid types and SHA256
-rw-r--r--include/git2/sys/transport.h12
-rw-r--r--src/libgit2/clone.c26
-rw-r--r--src/libgit2/fetch.c4
-rw-r--r--src/libgit2/indexer.c56
-rw-r--r--src/libgit2/remote.c64
-rw-r--r--src/libgit2/remote.h32
-rw-r--r--src/libgit2/streams/socket.c4
-rw-r--r--src/libgit2/transports/local.c14
-rw-r--r--src/libgit2/transports/smart.c35
-rw-r--r--src/libgit2/transports/smart.h4
-rw-r--r--src/libgit2/transports/smart_pkt.c88
-rw-r--r--src/libgit2/transports/smart_protocol.c29
-rw-r--r--src/util/hash.h13
13 files changed, 298 insertions, 83 deletions
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
index 06ae7079f..b70582188 100644
--- a/include/git2/sys/transport.h
+++ b/include/git2/sys/transport.h
@@ -57,6 +57,18 @@ struct git_transport {
unsigned int *capabilities,
git_transport *transport);
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /**
+ * Gets the object type for the remote repository.
+ *
+ * This function may be called after a successful call to
+ * `connect()`.
+ */
+ int GIT_CALLBACK(oid_type)(
+ git_oid_t *object_type,
+ git_transport *transport);
+#endif
+
/**
* Get the list of available references in the remote repository.
*
diff --git a/src/libgit2/clone.c b/src/libgit2/clone.c
index 0d393eb85..886159fbd 100644
--- a/src/libgit2/clone.c
+++ b/src/libgit2/clone.c
@@ -393,12 +393,19 @@ static int checkout_branch(git_repository *repo, git_remote *remote, const git_c
return error;
}
-static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch)
+static int clone_into(
+ git_repository *repo,
+ git_remote *_remote,
+ const git_fetch_options *opts,
+ const git_checkout_options *co_opts,
+ const char *branch)
{
int error;
git_str reflog_message = GIT_STR_INIT;
+ git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
git_fetch_options fetch_opts;
git_remote *remote;
+ git_oid_t oid_type;
GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(_remote);
@@ -414,8 +421,25 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch
memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
fetch_opts.update_fetchhead = 0;
fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
+
+ if ((error = git_remote_connect_options__from_fetch_opts(&connect_opts, remote, &fetch_opts)) < 0)
+ return error;
+
git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
+ /*
+ * Connect to the server so that we can identify the remote
+ * object format.
+ */
+
+ if ((error = git_remote_connect_ext(remote, GIT_DIRECTION_FETCH,
+ &connect_opts)) < 0)
+ goto cleanup;
+
+ if ((error = git_remote_oid_type(&oid_type, remote)) < 0 ||
+ (error = git_repository__set_objectformat(repo, oid_type)) < 0)
+ goto cleanup;
+
if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_str_cstr(&reflog_message))) != 0)
goto cleanup;
diff --git a/src/libgit2/fetch.c b/src/libgit2/fetch.c
index 5c2fee617..003b5198a 100644
--- a/src/libgit2/fetch.c
+++ b/src/libgit2/fetch.c
@@ -95,7 +95,6 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
git_remote_head **heads;
git_refspec tagspec, head, *spec;
int error = 0;
- git_odb *odb;
size_t i, heads_len;
unsigned int remote_caps;
unsigned int oid_mask = GIT_REMOTE_CAPABILITY_TIP_OID |
@@ -126,9 +125,6 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
goto cleanup;
}
- if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
- goto cleanup;
-
if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0 ||
(error = git_remote_capabilities(&remote_caps, remote)) < 0)
goto cleanup;
diff --git a/src/libgit2/indexer.c b/src/libgit2/indexer.c
index dfc326e02..fa55fb5ea 100644
--- a/src/libgit2/indexer.c
+++ b/src/libgit2/indexer.c
@@ -56,8 +56,8 @@ struct git_indexer {
git_vector deltas;
unsigned int fanout[256];
git_hash_ctx hash_ctx;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- char name[(GIT_HASH_SHA1_SIZE * 2) + 1];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ char name[(GIT_HASH_MAX_SIZE * 2) + 1];
git_indexer_progress_cb progress_cb;
void *progress_payload;
char objbuf[8*1024];
@@ -69,7 +69,7 @@ struct git_indexer {
git_odb *odb;
/* Fields for calculating the packfile trailer (hash of everything before it) */
- char inbuf[GIT_OID_MAX_SIZE];
+ char inbuf[GIT_HASH_MAX_SIZE];
size_t inbuf_len;
git_hash_ctx trailer;
};
@@ -137,6 +137,20 @@ int git_indexer_init_options(git_indexer_options *opts, unsigned int version)
}
#endif
+GIT_INLINE(git_hash_algorithm_t) indexer_hash_algorithm(git_indexer *idx)
+{
+ switch (idx->oid_type) {
+ case GIT_OID_SHA1:
+ return GIT_HASH_ALGORITHM_SHA1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return GIT_HASH_ALGORITHM_SHA256;
+#endif
+ }
+
+ return GIT_HASH_ALGORITHM_NONE;
+}
+
static int indexer_new(
git_indexer **out,
const char *prefix,
@@ -149,6 +163,7 @@ static int indexer_new(
git_indexer *idx;
git_str path = GIT_STR_INIT, tmp_path = GIT_STR_INIT;
static const char suff[] = "/pack";
+ git_hash_algorithm_t checksum_type;
int error, fd = -1;
if (in_opts)
@@ -163,8 +178,10 @@ static int indexer_new(
idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
git_str_init(&idx->entry_data, 0);
- if ((error = git_hash_ctx_init(&idx->hash_ctx, GIT_HASH_ALGORITHM_SHA1)) < 0 ||
- (error = git_hash_ctx_init(&idx->trailer, GIT_HASH_ALGORITHM_SHA1)) < 0 ||
+ checksum_type = indexer_hash_algorithm(idx);
+
+ if ((error = git_hash_ctx_init(&idx->hash_ctx, checksum_type)) < 0 ||
+ (error = git_hash_ctx_init(&idx->trailer, checksum_type)) < 0 ||
(error = git_oidmap_new(&idx->expected_oids)) < 0)
goto cleanup;
@@ -182,8 +199,7 @@ static int indexer_new(
if (fd < 0)
goto cleanup;
- /* TODO: SHA256 */
- error = git_packfile_alloc(&idx->pack, git_str_cstr(&tmp_path), 0);
+ error = git_packfile_alloc(&idx->pack, git_str_cstr(&tmp_path), oid_type);
git_str_dispose(&tmp_path);
if (error < 0)
@@ -614,7 +630,7 @@ static int do_progress_callback(git_indexer *idx, git_indexer_progress *stats)
return 0;
}
-/* Hash everything but the last 20B of input */
+/* Hash everything but the checksum trailer */
static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
{
size_t to_expell, to_keep;
@@ -623,7 +639,10 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
if (size == 0)
return;
- /* Easy case, dump the buffer and the data minus the last 20 bytes */
+ /*
+ * Easy case, dump the buffer and the data minus the trailing
+ * checksum (SHA1 or SHA256).
+ */
if (size >= oid_size) {
git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
git_hash_update(&idx->trailer, data, size - oid_size);
@@ -761,12 +780,14 @@ static int read_stream_object(git_indexer *idx, git_indexer_progress *stats)
{
git_packfile_stream *stream = &idx->stream;
off64_t entry_start = idx->off;
- size_t entry_size;
+ size_t oid_size, entry_size;
git_object_t type;
git_mwindow *w = NULL;
int error;
- if (idx->pack->mwf.size <= idx->off + 20)
+ oid_size = git_oid_size(idx->oid_type);
+
+ if (idx->pack->mwf.size <= idx->off + (long long)oid_size)
return GIT_EBUFS;
if (!idx->have_stream) {
@@ -963,15 +984,17 @@ static int inject_object(git_indexer *idx, git_oid *id)
git_odb_object *obj = NULL;
struct entry *entry = NULL;
struct git_pack_entry *pentry = NULL;
- unsigned char empty_checksum[GIT_HASH_SHA1_SIZE] = {0};
+ unsigned char empty_checksum[GIT_HASH_MAX_SIZE] = {0};
unsigned char hdr[64];
git_str buf = GIT_STR_INIT;
off64_t entry_start;
const void *data;
size_t len, hdr_len;
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ size_t checksum_size;
int error;
+ checksum_size = git_hash_size(indexer_hash_algorithm(idx));
+
if ((error = seek_back_trailer(idx)) < 0)
goto cleanup;
@@ -1205,10 +1228,10 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
struct git_pack_idx_header hdr;
git_str filename = GIT_STR_INIT;
struct entry *entry;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
git_filebuf index_file = {0};
void *packfile_trailer;
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ size_t checksum_size;
bool mismatch;
if (!idx->parsed_header) {
@@ -1216,6 +1239,9 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
return -1;
}
+ checksum_size = git_hash_size(indexer_hash_algorithm(idx));
+ GIT_ASSERT(checksum_size);
+
/* Test for this before resolve_deltas(), as it plays with idx->off */
if (idx->off + (ssize_t)checksum_size < idx->pack->mwf.size) {
git_error_set(GIT_ERROR_INDEXER, "unexpected data at the end of the pack");
diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c
index 02d271d7d..3583ec031 100644
--- a/src/libgit2/remote.c
+++ b/src/libgit2/remote.c
@@ -1026,6 +1026,24 @@ int git_remote_capabilities(unsigned int *out, git_remote *remote)
return remote->transport->capabilities(out, remote->transport);
}
+int git_remote_oid_type(git_oid_t *out, git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->transport) {
+ git_error_set(GIT_ERROR_NET, "this remote has never connected");
+ *out = 0;
+ return -1;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ return remote->transport->oid_type(out, remote->transport);
+#else
+ *out = GIT_OID_SHA1;
+ return 0;
+#endif
+}
+
static int lookup_config(char **out, git_config *cfg, const char *name)
{
git_config_entry *ce = NULL;
@@ -1225,24 +1243,6 @@ static int ls_to_vector(git_vector *out, git_remote *remote)
return 0;
}
-#define copy_opts(out, in) \
- if (in) { \
- (out)->callbacks = (in)->callbacks; \
- (out)->proxy_opts = (in)->proxy_opts; \
- (out)->custom_headers = (in)->custom_headers; \
- (out)->follow_redirects = (in)->follow_redirects; \
- }
-
-GIT_INLINE(int) connect_opts_from_fetch_opts(
- git_remote_connect_options *out,
- git_remote *remote,
- const git_fetch_options *fetch_opts)
-{
- git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
- copy_opts(&tmp, fetch_opts);
- return git_remote_connect_options_normalize(out, remote->repo, &tmp);
-}
-
static int connect_or_reset_options(
git_remote *remote,
int direction,
@@ -1330,7 +1330,8 @@ int git_remote_download(
return -1;
}
- if (connect_opts_from_fetch_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_fetch_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
@@ -1350,6 +1351,8 @@ int git_remote_fetch(
bool prune = false;
git_str reflog_msg_buf = GIT_STR_INIT;
git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ unsigned int capabilities;
+ git_oid_t oid_type;
GIT_ASSERT_ARG(remote);
@@ -1358,7 +1361,8 @@ int git_remote_fetch(
return -1;
}
- if (connect_opts_from_fetch_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_fetch_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
@@ -1369,6 +1373,10 @@ int git_remote_fetch(
tagopt = opts->download_tags;
}
+ if ((error = git_remote_capabilities(&capabilities, remote)) < 0 ||
+ (error = git_remote_oid_type(&oid_type, remote)) < 0)
+ return error;
+
/* Connect and download everything */
error = git_remote__download(remote, refspecs, opts);
@@ -2896,16 +2904,6 @@ done:
return error;
}
-GIT_INLINE(int) connect_opts_from_push_opts(
- git_remote_connect_options *out,
- git_remote *remote,
- const git_push_options *push_opts)
-{
- git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
- copy_opts(&tmp, push_opts);
- return git_remote_connect_options_normalize(out, remote->repo, &tmp);
-}
-
int git_remote_upload(
git_remote *remote,
const git_strarray *refspecs,
@@ -2924,7 +2922,8 @@ int git_remote_upload(
return -1;
}
- if ((error = connect_opts_from_push_opts(&connect_opts, remote, opts)) < 0)
+ if ((error = git_remote_connect_options__from_push_opts(
+ &connect_opts, remote, opts)) < 0)
goto cleanup;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_PUSH, &connect_opts)) < 0)
@@ -2985,7 +2984,8 @@ int git_remote_push(
return -1;
}
- if (connect_opts_from_push_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_push_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
diff --git a/src/libgit2/remote.h b/src/libgit2/remote.h
index 41ee58e0f..87dab4eba 100644
--- a/src/libgit2/remote.h
+++ b/src/libgit2/remote.h
@@ -56,5 +56,37 @@ int git_remote_connect_options_normalize(
const git_remote_connect_options *src);
int git_remote_capabilities(unsigned int *out, git_remote *remote);
+int git_remote_oid_type(git_oid_t *out, git_remote *remote);
+
+
+#define git_remote_connect_options__copy_opts(out, in) \
+ if (in) { \
+ (out)->callbacks = (in)->callbacks; \
+ (out)->proxy_opts = (in)->proxy_opts; \
+ (out)->custom_headers = (in)->custom_headers; \
+ (out)->follow_redirects = (in)->follow_redirects; \
+ }
+
+GIT_INLINE(int) git_remote_connect_options__from_fetch_opts(
+ git_remote_connect_options *out,
+ git_remote *remote,
+ const git_fetch_options *fetch_opts)
+{
+ git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_remote_connect_options__copy_opts(&tmp, fetch_opts);
+ return git_remote_connect_options_normalize(out, remote->repo, &tmp);
+}
+
+GIT_INLINE(int) git_remote_connect_options__from_push_opts(
+ git_remote_connect_options *out,
+ git_remote *remote,
+ const git_push_options *push_opts)
+{
+ git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_remote_connect_options__copy_opts(&tmp, push_opts);
+ return git_remote_connect_options_normalize(out, remote->repo, &tmp);
+}
+
+#undef git_remote_connect_options__copy_opts
#endif
diff --git a/src/libgit2/streams/socket.c b/src/libgit2/streams/socket.c
index 9415fe892..cbf805879 100644
--- a/src/libgit2/streams/socket.c
+++ b/src/libgit2/streams/socket.c
@@ -135,9 +135,11 @@ static ssize_t socket_write(git_stream *stream, const char *data, size_t len, in
git_socket_stream *st = (git_socket_stream *) stream;
ssize_t written;
+ assert(flags == 0);
+
errno = 0;
- if ((written = p_send(st->s, data, len, flags)) < 0) {
+ if ((written = p_send(st->s, data, len, 0)) < 0) {
net_set_error("error sending data");
return -1;
}
diff --git a/src/libgit2/transports/local.c b/src/libgit2/transports/local.c
index 6c754a034..4d86f1713 100644
--- a/src/libgit2/transports/local.c
+++ b/src/libgit2/transports/local.c
@@ -266,6 +266,17 @@ static int local_capabilities(unsigned int *capabilities, git_transport *transpo
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+static int local_oid_type(git_oid_t *out, git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ *out = t->repo->oid_type;
+
+ return 0;
+}
+#endif
+
static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_local *t = (transport_local *)transport;
@@ -732,6 +743,9 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
t->parent.connect = local_connect;
t->parent.set_connect_opts = local_set_connect_opts;
t->parent.capabilities = local_capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = local_oid_type;
+#endif
t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.download_pack = local_download_pack;
t->parent.push = local_push;
diff --git a/src/libgit2/transports/smart.c b/src/libgit2/transports/smart.c
index 7f57dba2a..c3a764bd3 100644
--- a/src/libgit2/transports/smart.c
+++ b/src/libgit2/transports/smart.c
@@ -54,6 +54,12 @@ GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransp
return -1;
}
+ git__free(t->caps.object_format);
+ t->caps.object_format = NULL;
+
+ git__free(t->caps.agent);
+ t->caps.agent = NULL;
+
return 0;
}
@@ -242,6 +248,30 @@ static int git_smart__capabilities(unsigned int *capabilities, git_transport *tr
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+static int git_smart__oid_type(git_oid_t *out, git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ *out = 0;
+
+ if (t->caps.object_format == NULL) {
+ *out = GIT_OID_DEFAULT;
+ } else {
+ *out = git_oid_type_fromstr(t->caps.object_format);
+
+ if (!*out) {
+ git_error_set(GIT_ERROR_INVALID,
+ "unknown object format '%s'",
+ t->caps.object_format);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
@@ -386,6 +416,8 @@ static void git_smart__free(git_transport *transport)
git_remote_connect_options_dispose(&t->connect_opts);
+ git__free(t->caps.object_format);
+ git__free(t->caps.agent);
git__free(t);
}
@@ -452,6 +484,9 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
t->parent.connect = git_smart__connect;
t->parent.set_connect_opts = git_smart__set_connect_opts;
t->parent.capabilities = git_smart__capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = git_smart__oid_type;
+#endif
t->parent.close = git_smart__close;
t->parent.free = git_smart__free;
t->parent.negotiate_fetch = git_smart__negotiate_fetch;
diff --git a/src/libgit2/transports/smart.h b/src/libgit2/transports/smart.h
index ca6496163..d71160d8e 100644
--- a/src/libgit2/transports/smart.h
+++ b/src/libgit2/transports/smart.h
@@ -32,6 +32,8 @@
#define GIT_CAP_SYMREF "symref"
#define GIT_CAP_WANT_TIP_SHA1 "allow-tip-sha1-in-want"
#define GIT_CAP_WANT_REACHABLE_SHA1 "allow-reachable-sha1-in-want"
+#define GIT_CAP_OBJECT_FORMAT "object-format="
+#define GIT_CAP_AGENT "agent="
extern bool git_smart__ofs_delta_enabled;
@@ -133,6 +135,8 @@ typedef struct transport_smart_caps {
thin_pack:1,
want_tip_sha1:1,
want_reachable_sha1:1;
+ char *object_format;
+ char *agent;
} transport_smart_caps;
typedef int (*packetsize_cb)(size_t received, void *payload);
diff --git a/src/libgit2/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c
index b6428d845..5fce42175 100644
--- a/src/libgit2/transports/smart_pkt.c
+++ b/src/libgit2/transports/smart_pkt.c
@@ -21,11 +21,14 @@
#include <ctype.h>
-#define PKT_LEN_SIZE 4
-static const char pkt_done_str[] = "0009done\n";
-static const char pkt_flush_str[] = "0000";
-static const char pkt_have_prefix[] = "0032have ";
-static const char pkt_want_prefix[] = "0032want ";
+#define PKT_DONE_STR "0009done\n"
+#define PKT_FLUSH_STR "0000"
+#define PKT_HAVE_PREFIX "have "
+#define PKT_WANT_PREFIX "want "
+
+#define PKT_LEN_SIZE 4
+#define PKT_MAX_SIZE 0xffff
+#define PKT_MAX_WANTLEN (PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) + GIT_OID_MAX_HEXSIZE + 1)
static int flush_pkt(git_pkt **out)
{
@@ -598,16 +601,20 @@ void git_pkt_free(git_pkt *pkt)
int git_pkt_buffer_flush(git_str *buf)
{
- return git_str_put(buf, pkt_flush_str, strlen(pkt_flush_str));
+ return git_str_put(buf, PKT_FLUSH_STR, CONST_STRLEN(PKT_FLUSH_STR));
}
-static int buffer_want_with_caps(const git_remote_head *head, transport_smart_caps *caps, git_str *buf)
+static int buffer_want_with_caps(
+ const git_remote_head *head,
+ transport_smart_caps *caps,
+ git_oid_t oid_type,
+ git_str *buf)
{
git_str str = GIT_STR_INIT;
- char oid[GIT_OID_MAX_HEXSIZE + 1] = {0};
+ char oid[GIT_OID_MAX_HEXSIZE];
size_t oid_hexsize, len;
- oid_hexsize = git_oid_hexsize(head->oid.type);
+ oid_hexsize = git_oid_hexsize(oid_type);
git_oid_fmt(oid, &head->oid);
/* Prefer multi_ack_detailed */
@@ -634,18 +641,19 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
if (git_str_oom(&str))
return -1;
- len = strlen("XXXXwant ") + oid_hexsize + 1 /* NUL */ +
- git_str_len(&str) + 1 /* LF */;
-
- if (len > 0xffff) {
+ if (str.size > (PKT_MAX_SIZE - (PKT_MAX_WANTLEN + 1))) {
git_error_set(GIT_ERROR_NET,
- "tried to produce packet with invalid length %" PRIuZ, len);
+ "tried to produce packet with invalid caps length %" PRIuZ, str.size);
return -1;
}
+ len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
+ oid_hexsize + 1 /* NUL */ +
+ git_str_len(&str) + 1 /* LF */;
+
git_str_grow_by(buf, len);
git_str_printf(buf,
- "%04xwant %.*s %s\n", (unsigned int)len,
+ "%04x%s%.*s %s\n", (unsigned int)len, PKT_WANT_PREFIX,
(int)oid_hexsize, oid, git_str_cstr(&str));
git_str_dispose(&str);
@@ -665,8 +673,21 @@ int git_pkt_buffer_wants(
transport_smart_caps *caps,
git_str *buf)
{
- size_t i = 0;
const git_remote_head *head;
+ char oid[GIT_OID_MAX_HEXSIZE];
+ git_oid_t oid_type;
+ size_t oid_hexsize, want_len, i = 0;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = count > 0 ? refs[0]->oid.type : GIT_OID_SHA1;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+
+ want_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
+ oid_hexsize + 1 /* LF */;
if (caps->common) {
for (; i < count; ++i) {
@@ -675,15 +696,13 @@ int git_pkt_buffer_wants(
break;
}
- if (buffer_want_with_caps(refs[i], caps, buf) < 0)
+ if (buffer_want_with_caps(refs[i], caps, oid_type, buf) < 0)
return -1;
i++;
}
for (; i < count; ++i) {
- char oid[GIT_OID_MAX_HEXSIZE];
-
head = refs[i];
if (head->local)
@@ -691,9 +710,9 @@ int git_pkt_buffer_wants(
git_oid_fmt(oid, &head->oid);
- git_str_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));
- git_str_put(buf, oid, git_oid_hexsize(head->oid.type));
- git_str_putc(buf, '\n');
+ git_str_printf(buf, "%04x%s%.*s\n",
+ (unsigned int)want_len, PKT_WANT_PREFIX,
+ (int)oid_hexsize, oid);
if (git_str_oom(buf))
return -1;
@@ -704,14 +723,27 @@ int git_pkt_buffer_wants(
int git_pkt_buffer_have(git_oid *oid, git_str *buf)
{
- char oidhex[GIT_OID_SHA1_HEXSIZE + 1];
-
- memset(oidhex, 0x0, sizeof(oidhex));
- git_oid_fmt(oidhex, oid);
- return git_str_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
+ char oid_str[GIT_OID_MAX_HEXSIZE];
+ git_oid_t oid_type;
+ size_t oid_hexsize, have_len;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = oid->type;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+ have_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_HAVE_PREFIX) +
+ oid_hexsize + 1 /* LF */;
+
+ git_oid_fmt(oid_str, oid);
+ return git_str_printf(buf, "%04x%s%.*s\n",
+ (unsigned int)have_len, PKT_HAVE_PREFIX,
+ (int)oid_hexsize, oid_str);
}
int git_pkt_buffer_done(git_str *buf)
{
- return git_str_puts(buf, pkt_done_str);
+ return git_str_put(buf, PKT_DONE_STR, CONST_STRLEN(PKT_DONE_STR));
}
diff --git a/src/libgit2/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c
index 86d02df83..0d47acafe 100644
--- a/src/libgit2/transports/smart_protocol.c
+++ b/src/libgit2/transports/smart_protocol.c
@@ -134,9 +134,12 @@ on_invalid:
return -1;
}
-int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
+int git_smart__detect_caps(
+ git_pkt_ref *pkt,
+ transport_smart_caps *caps,
+ git_vector *symrefs)
{
- const char *ptr;
+ const char *ptr, *start;
/* No refs or capabilities, odd but not a problem */
if (pkt == NULL || pkt->capabilities == NULL)
@@ -218,6 +221,28 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
continue;
}
+ if (!git__prefixcmp(ptr, GIT_CAP_OBJECT_FORMAT)) {
+ ptr += strlen(GIT_CAP_OBJECT_FORMAT);
+
+ start = ptr;
+ ptr = strchr(ptr, ' ');
+
+ if ((caps->object_format = git__strndup(start, (ptr - start))) == NULL)
+ return -1;
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_AGENT)) {
+ ptr += strlen(GIT_CAP_AGENT);
+
+ start = ptr;
+ ptr = strchr(ptr, ' ');
+
+ if ((caps->agent = git__strndup(start, (ptr - start))) == NULL)
+ return -1;
+ continue;
+ }
+
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
diff --git a/src/util/hash.h b/src/util/hash.h
index 387c5a66f..21fcaf045 100644
--- a/src/util/hash.h
+++ b/src/util/hash.h
@@ -23,6 +23,8 @@ typedef enum {
GIT_HASH_ALGORITHM_SHA256
} git_hash_algorithm_t;
+#define GIT_HASH_MAX_SIZE GIT_HASH_SHA256_SIZE
+
typedef struct git_hash_ctx {
union {
git_hash_sha1_ctx sha1;
@@ -45,4 +47,15 @@ int git_hash_vec(unsigned char *out, git_str_vec *vec, size_t n, git_hash_algori
int git_hash_fmt(char *out, unsigned char *hash, size_t hash_len);
+GIT_INLINE(size_t) git_hash_size(git_hash_algorithm_t algorithm) {
+ switch (algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return GIT_HASH_SHA1_SIZE;
+ case GIT_HASH_ALGORITHM_SHA256:
+ return GIT_HASH_SHA256_SIZE;
+ default:
+ return 0;
+ }
+}
+
#endif