]> git.ipfire.org Git - thirdparty/git.git/blobdiff - remote-curl.c
Merge branch 'bc/sha-256-part-2'
[thirdparty/git.git] / remote-curl.c
index 1c9aa3d0ab978c1f98f1b4ff45143d765fcf6c27..5cbc6e50025b7cbf8b210cdd7bd6638fe2fa57fa 100644 (file)
@@ -41,7 +41,9 @@ struct options {
                deepen_relative : 1,
                from_promisor : 1,
                no_dependents : 1,
-               atomic : 1;
+               atomic : 1,
+               object_format : 1;
+       const struct git_hash_algo *hash_algo;
 };
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -190,6 +192,16 @@ static int set_option(const char *name, const char *value)
        } else if (!strcmp(name, "filter")) {
                options.filter = xstrdup(value);
                return 0;
+       } else if (!strcmp(name, "object-format")) {
+               int algo;
+               options.object_format = 1;
+               if (strcmp(value, "true")) {
+                       algo = hash_algo_by_name(value);
+                       if (algo == GIT_HASH_UNKNOWN)
+                               die("unknown object format '%s'", value);
+                       options.hash_algo = &hash_algos[algo];
+               }
+               return 0;
        } else {
                return 1 /* unsupported */;
        }
@@ -231,6 +243,7 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
        case protocol_v0:
                get_remote_heads(&reader, &list, for_push ? REF_NORMAL : 0,
                                 NULL, &heads->shallow);
+               options.hash_algo = reader.hash_algo;
                break;
        case protocol_unknown_version:
                BUG("unknown protocol version");
@@ -239,6 +252,19 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
        return list;
 }
 
+static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
+{
+       const char *p = memchr(heads->buf, '\t', heads->len);
+       int algo;
+       if (!p)
+               return the_hash_algo;
+
+       algo = hash_algo_by_length((p - heads->buf) / 2);
+       if (algo == GIT_HASH_UNKNOWN)
+               return NULL;
+       return &hash_algos[algo];
+}
+
 static struct ref *parse_info_refs(struct discovery *heads)
 {
        char *data, *start, *mid;
@@ -249,6 +275,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
        struct ref *ref = NULL;
        struct ref *last_ref = NULL;
 
+       options.hash_algo = detect_hash_algo(heads);
+       if (!options.hash_algo)
+               die("%sinfo/refs not valid: could not determine hash algorithm; "
+                   "is this a git repository?",
+                   transport_anonymize_url(url.buf));
+
        data = heads->buf;
        start = NULL;
        mid = data;
@@ -259,13 +291,13 @@ static struct ref *parse_info_refs(struct discovery *heads)
                if (data[i] == '\t')
                        mid = &data[i];
                if (data[i] == '\n') {
-                       if (mid - start != the_hash_algo->hexsz)
+                       if (mid - start != options.hash_algo->hexsz)
                                die(_("%sinfo/refs not valid: is this a git repository?"),
                                    transport_anonymize_url(url.buf));
                        data[i] = 0;
                        ref_name = mid + 1;
                        ref = alloc_ref(ref_name);
-                       get_oid_hex(start, &ref->old_oid);
+                       get_oid_hex_algop(start, &ref->old_oid, options.hash_algo);
                        if (!refs)
                                refs = ref;
                        if (last_ref)
@@ -509,11 +541,16 @@ static struct ref *get_refs(int for_push)
 static void output_refs(struct ref *refs)
 {
        struct ref *posn;
+       if (options.object_format && options.hash_algo) {
+               printf(":object-format %s\n", options.hash_algo->name);
+       }
        for (posn = refs; posn; posn = posn->next) {
                if (posn->symref)
                        printf("@%s %s\n", posn->symref, posn->name);
                else
-                       printf("%s %s\n", oid_to_hex(&posn->old_oid), posn->name);
+                       printf("%s %s\n", hash_to_hex_algop(posn->old_oid.hash,
+                                                           options.hash_algo),
+                                         posn->name);
        }
        printf("\n");
        fflush(stdout);
@@ -601,6 +638,8 @@ static int rpc_read_from_out(struct rpc_state *rpc, int options,
                case PACKET_READ_FLUSH:
                        memcpy(buf - 4, "0000", 4);
                        break;
+               case PACKET_READ_RESPONSE_END:
+                       die(_("remote server sent stateless separator"));
                }
        }
 
@@ -643,7 +682,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
                        return 0;
                }
                /*
-                * If avail is non-zerp, the line length for the flush still
+                * If avail is non-zero, the line length for the flush still
                 * hasn't been fully sent. Proceed with sending the line
                 * length.
                 */
@@ -679,9 +718,55 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
 }
 #endif
 
+struct check_pktline_state {
+       char len_buf[4];
+       int len_filled;
+       int remaining;
+};
+
+static void check_pktline(struct check_pktline_state *state, const char *ptr, size_t size)
+{
+       while (size) {
+               if (!state->remaining) {
+                       int digits_remaining = 4 - state->len_filled;
+                       if (digits_remaining > size)
+                               digits_remaining = size;
+                       memcpy(&state->len_buf[state->len_filled], ptr, digits_remaining);
+                       state->len_filled += digits_remaining;
+                       ptr += digits_remaining;
+                       size -= digits_remaining;
+
+                       if (state->len_filled == 4) {
+                               state->remaining = packet_length(state->len_buf);
+                               if (state->remaining < 0) {
+                                       die(_("remote-curl: bad line length character: %.4s"), state->len_buf);
+                               } else if (state->remaining == 2) {
+                                       die(_("remote-curl: unexpected response end packet"));
+                               } else if (state->remaining < 4) {
+                                       state->remaining = 0;
+                               } else {
+                                       state->remaining -= 4;
+                               }
+                               state->len_filled = 0;
+                       }
+               }
+
+               if (state->remaining) {
+                       int remaining = state->remaining;
+                       if (remaining > size)
+                               remaining = size;
+                       ptr += remaining;
+                       size -= remaining;
+                       state->remaining -= remaining;
+               }
+       }
+}
+
 struct rpc_in_data {
        struct rpc_state *rpc;
        struct active_request_slot *slot;
+       int check_pktline;
+       struct check_pktline_state pktline_state;
 };
 
 /*
@@ -702,6 +787,8 @@ static size_t rpc_in(char *ptr, size_t eltsize,
                return size;
        if (size)
                data->rpc->any_written = 1;
+       if (data->check_pktline)
+               check_pktline(&data->pktline_state, ptr, size);
        write_or_die(data->rpc->in, ptr, size);
        return size;
 }
@@ -778,7 +865,7 @@ static curl_off_t xcurl_off_t(size_t len)
  * If flush_received is true, do not attempt to read any more; just use what's
  * in rpc->buf.
  */
-static int post_rpc(struct rpc_state *rpc, int flush_received)
+static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_received)
 {
        struct active_request_slot *slot;
        struct curl_slist *headers = http_copy_default_headers();
@@ -920,6 +1007,8 @@ retry:
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
        rpc_in_data.rpc = rpc;
        rpc_in_data.slot = slot;
+       rpc_in_data.check_pktline = stateless_connect;
+       memset(&rpc_in_data.pktline_state, 0, sizeof(rpc_in_data.pktline_state));
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &rpc_in_data);
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
 
@@ -936,6 +1025,14 @@ retry:
        if (!rpc->any_written)
                err = -1;
 
+       if (rpc_in_data.pktline_state.len_filled)
+               err = error(_("%d bytes of length header were received"), rpc_in_data.pktline_state.len_filled);
+       if (rpc_in_data.pktline_state.remaining)
+               err = error(_("%d bytes of body are still expected"), rpc_in_data.pktline_state.remaining);
+
+       if (stateless_connect)
+               packet_response_end(rpc->in);
+
        curl_slist_free_all(headers);
        free(gzip_body);
        return err;
@@ -985,7 +1082,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
                        break;
                rpc->pos = 0;
                rpc->len = n;
-               err |= post_rpc(rpc, 0);
+               err |= post_rpc(rpc, 0, 0);
        }
 
        close(client.in);
@@ -1276,7 +1373,7 @@ static void parse_push(struct strbuf *buf)
        if (ret)
                exit(128); /* error already reported */
 
- free_specs:
+free_specs:
        argv_array_clear(&specs);
 }
 
@@ -1342,7 +1439,7 @@ static int stateless_connect(const char *service_name)
                        BUG("The entire rpc->buf should be larger than LARGE_PACKET_MAX");
                if (status == PACKET_READ_EOF)
                        break;
-               if (post_rpc(&rpc, status == PACKET_READ_FLUSH))
+               if (post_rpc(&rpc, 1, status == PACKET_READ_FLUSH))
                        /* We would have an err here */
                        break;
                /* Reset the buffer for next request */
@@ -1439,6 +1536,7 @@ int cmd_main(int argc, const char **argv)
                        printf("option\n");
                        printf("push\n");
                        printf("check-connectivity\n");
+                       printf("object-format\n");
                        printf("\n");
                        fflush(stdout);
                } else if (skip_prefix(buf.buf, "stateless-connect ", &arg)) {