File Coverage

deps/libgit2/src/libgit2/transports/smart_protocol.c
Criterion Covered Total %
statement 0 574 0.0
branch 0 388 0.0
condition n/a
subroutine n/a
pod n/a
total 0 962 0.0


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "common.h"
9              
10             #include "git2.h"
11             #include "git2/odb_backend.h"
12              
13             #include "smart.h"
14             #include "refs.h"
15             #include "repository.h"
16             #include "push.h"
17             #include "pack-objects.h"
18             #include "remote.h"
19             #include "util.h"
20             #include "revwalk.h"
21              
22             #define NETWORK_XFER_THRESHOLD (100*1024)
23             /* The minimal interval between progress updates (in seconds). */
24             #define MIN_PROGRESS_UPDATE_INTERVAL 0.5
25              
26             bool git_smart__ofs_delta_enabled = true;
27              
28 0           int git_smart__store_refs(transport_smart *t, int flushes)
29             {
30 0           gitno_buffer *buf = &t->buffer;
31 0           git_vector *refs = &t->refs;
32 0           int error, flush = 0, recvd;
33 0           const char *line_end = NULL;
34 0           git_pkt *pkt = NULL;
35             size_t i;
36              
37             /* Clear existing refs in case git_remote_connect() is called again
38             * after git_remote_disconnect().
39             */
40 0 0         git_vector_foreach(refs, i, pkt) {
41 0           git_pkt_free(pkt);
42             }
43 0           git_vector_clear(refs);
44 0           pkt = NULL;
45              
46             do {
47 0 0         if (buf->offset > 0)
48 0           error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset);
49             else
50 0           error = GIT_EBUFS;
51              
52 0 0         if (error < 0 && error != GIT_EBUFS)
    0          
53 0           return error;
54              
55 0 0         if (error == GIT_EBUFS) {
56 0 0         if ((recvd = gitno_recv(buf)) < 0)
57 0           return recvd;
58              
59 0 0         if (recvd == 0) {
60 0           git_error_set(GIT_ERROR_NET, "early EOF");
61 0           return GIT_EEOF;
62             }
63              
64 0           continue;
65             }
66              
67 0 0         if (gitno_consume(buf, line_end) < 0)
68 0           return -1;
69              
70 0 0         if (pkt->type == GIT_PKT_ERR) {
71 0           git_error_set(GIT_ERROR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error);
72 0           git__free(pkt);
73 0           return -1;
74             }
75              
76 0 0         if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
    0          
77 0           return -1;
78              
79 0 0         if (pkt->type == GIT_PKT_FLUSH) {
80 0           flush++;
81 0           git_pkt_free(pkt);
82             }
83 0 0         } while (flush < flushes);
84              
85 0           return flush;
86             }
87              
88 0           static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
89             {
90             int error;
91             const char *end;
92 0           git_str buf = GIT_STR_INIT;
93 0           git_refspec *mapping = NULL;
94              
95 0           ptr += strlen(GIT_CAP_SYMREF);
96 0 0         if (*ptr != '=')
97 0           goto on_invalid;
98              
99 0           ptr++;
100 0 0         if (!(end = strchr(ptr, ' ')) &&
    0          
101             !(end = strchr(ptr, '\0')))
102 0           goto on_invalid;
103              
104 0 0         if ((error = git_str_put(&buf, ptr, end - ptr)) < 0)
105 0           return error;
106              
107             /* symref mapping has refspec format */
108 0           mapping = git__calloc(1, sizeof(git_refspec));
109 0 0         GIT_ERROR_CHECK_ALLOC(mapping);
110              
111 0           error = git_refspec__parse(mapping, git_str_cstr(&buf), true);
112 0           git_str_dispose(&buf);
113              
114             /* if the error isn't OOM, then it's a parse error; let's use a nicer message */
115 0 0         if (error < 0) {
116 0 0         if (git_error_last()->klass != GIT_ERROR_NOMEMORY)
117 0           goto on_invalid;
118              
119 0           git__free(mapping);
120 0           return error;
121             }
122              
123 0 0         if ((error = git_vector_insert(symrefs, mapping)) < 0)
124 0           return error;
125              
126 0           *out = end;
127 0           return 0;
128              
129             on_invalid:
130 0           git_error_set(GIT_ERROR_NET, "remote sent invalid symref");
131 0           git_refspec__dispose(mapping);
132 0           git__free(mapping);
133 0           return -1;
134             }
135              
136 0           int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
137             {
138             const char *ptr;
139              
140             /* No refs or capabilities, odd but not a problem */
141 0 0         if (pkt == NULL || pkt->capabilities == NULL)
    0          
142 0           return GIT_ENOTFOUND;
143              
144 0           ptr = pkt->capabilities;
145 0 0         while (ptr != NULL && *ptr != '\0') {
    0          
146 0 0         if (*ptr == ' ')
147 0           ptr++;
148              
149 0 0         if (git_smart__ofs_delta_enabled && !git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
    0          
150 0           caps->common = caps->ofs_delta = 1;
151 0           ptr += strlen(GIT_CAP_OFS_DELTA);
152 0           continue;
153             }
154              
155             /* Keep multi_ack_detailed before multi_ack */
156 0 0         if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
157 0           caps->common = caps->multi_ack_detailed = 1;
158 0           ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
159 0           continue;
160             }
161              
162 0 0         if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
163 0           caps->common = caps->multi_ack = 1;
164 0           ptr += strlen(GIT_CAP_MULTI_ACK);
165 0           continue;
166             }
167              
168 0 0         if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
169 0           caps->common = caps->include_tag = 1;
170 0           ptr += strlen(GIT_CAP_INCLUDE_TAG);
171 0           continue;
172             }
173              
174             /* Keep side-band check after side-band-64k */
175 0 0         if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
176 0           caps->common = caps->side_band_64k = 1;
177 0           ptr += strlen(GIT_CAP_SIDE_BAND_64K);
178 0           continue;
179             }
180              
181 0 0         if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
182 0           caps->common = caps->side_band = 1;
183 0           ptr += strlen(GIT_CAP_SIDE_BAND);
184 0           continue;
185             }
186              
187 0 0         if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
188 0           caps->common = caps->delete_refs = 1;
189 0           ptr += strlen(GIT_CAP_DELETE_REFS);
190 0           continue;
191             }
192              
193 0 0         if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
194 0           caps->common = caps->thin_pack = 1;
195 0           ptr += strlen(GIT_CAP_THIN_PACK);
196 0           continue;
197             }
198              
199 0 0         if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
200             int error;
201              
202 0 0         if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
203 0           return error;
204              
205 0           continue;
206             }
207              
208 0 0         if (!git__prefixcmp(ptr, GIT_CAP_WANT_TIP_SHA1)) {
209 0           caps->common = caps->want_tip_sha1 = 1;
210 0           ptr += strlen(GIT_CAP_DELETE_REFS);
211 0           continue;
212             }
213              
214 0 0         if (!git__prefixcmp(ptr, GIT_CAP_WANT_REACHABLE_SHA1)) {
215 0           caps->common = caps->want_reachable_sha1 = 1;
216 0           ptr += strlen(GIT_CAP_DELETE_REFS);
217 0           continue;
218             }
219              
220             /* We don't know this capability, so skip it */
221 0           ptr = strchr(ptr, ' ');
222             }
223              
224 0           return 0;
225             }
226              
227 0           static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf)
228             {
229 0           const char *ptr = buf->data, *line_end = ptr;
230 0           git_pkt *pkt = NULL;
231 0           int error = 0, ret;
232              
233             do {
234 0 0         if (buf->offset > 0)
235 0           error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset);
236             else
237 0           error = GIT_EBUFS;
238              
239 0 0         if (error == 0)
240 0           break; /* return the pkt */
241              
242 0 0         if (error < 0 && error != GIT_EBUFS)
    0          
243 0           return error;
244              
245 0 0         if ((ret = gitno_recv(buf)) < 0) {
246 0           return ret;
247 0 0         } else if (ret == 0) {
248 0           git_error_set(GIT_ERROR_NET, "early EOF");
249 0           return GIT_EEOF;
250             }
251 0 0         } while (error);
252              
253 0 0         if (gitno_consume(buf, line_end) < 0)
254 0           return -1;
255              
256 0 0         if (out_type != NULL)
257 0           *out_type = pkt->type;
258 0 0         if (out_pkt != NULL)
259 0           *out_pkt = pkt;
260             else
261 0           git__free(pkt);
262              
263 0           return error;
264             }
265              
266 0           static int store_common(transport_smart *t)
267             {
268 0           git_pkt *pkt = NULL;
269 0           gitno_buffer *buf = &t->buffer;
270             int error;
271              
272             do {
273 0 0         if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
274 0           return error;
275              
276 0 0         if (pkt->type != GIT_PKT_ACK) {
277 0           git__free(pkt);
278 0           return 0;
279             }
280              
281 0 0         if (git_vector_insert(&t->common, pkt) < 0) {
282 0           git__free(pkt);
283 0           return -1;
284             }
285 0           } while (1);
286              
287             return 0;
288             }
289              
290 0           static int wait_while_ack(gitno_buffer *buf)
291             {
292             int error;
293 0           git_pkt *pkt = NULL;
294 0           git_pkt_ack *ack = NULL;
295              
296             while (1) {
297 0           git_pkt_free(pkt);
298              
299 0 0         if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
300 0           return error;
301              
302 0 0         if (pkt->type == GIT_PKT_NAK)
303 0           break;
304 0 0         if (pkt->type != GIT_PKT_ACK)
305 0           continue;
306              
307 0           ack = (git_pkt_ack*)pkt;
308              
309 0 0         if (ack->status != GIT_ACK_CONTINUE &&
    0          
310 0 0         ack->status != GIT_ACK_COMMON &&
311 0           ack->status != GIT_ACK_READY) {
312 0           break;
313             }
314 0           }
315              
316 0           git_pkt_free(pkt);
317 0           return 0;
318             }
319              
320 0           int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
321             {
322 0           transport_smart *t = (transport_smart *)transport;
323 0           git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
324 0           gitno_buffer *buf = &t->buffer;
325 0           git_str data = GIT_STR_INIT;
326 0           git_revwalk *walk = NULL;
327 0           int error = -1;
328             git_pkt_type pkt_type;
329             unsigned int i;
330             git_oid oid;
331              
332 0 0         if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
333 0           return error;
334              
335 0 0         if ((error = git_revwalk_new(&walk, repo)) < 0)
336 0           goto on_error;
337              
338 0           opts.insert_by_date = 1;
339 0 0         if ((error = git_revwalk__push_glob(walk, "refs/*", &opts)) < 0)
340 0           goto on_error;
341              
342             /*
343             * Our support for ACK extensions is simply to parse them. On
344             * the first ACK we will accept that as enough common
345             * objects. We give up if we haven't found an answer in the
346             * first 256 we send.
347             */
348 0           i = 0;
349 0 0         while (i < 256) {
350 0           error = git_revwalk_next(&oid, walk);
351              
352 0 0         if (error < 0) {
353 0 0         if (GIT_ITEROVER == error)
354 0           break;
355              
356 0           goto on_error;
357             }
358              
359 0           git_pkt_buffer_have(&oid, &data);
360 0           i++;
361 0 0         if (i % 20 == 0) {
362 0 0         if (t->cancelled.val) {
363 0           git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
364 0           error = GIT_EUSER;
365 0           goto on_error;
366             }
367              
368 0           git_pkt_buffer_flush(&data);
369 0 0         if (git_str_oom(&data)) {
370 0           error = -1;
371 0           goto on_error;
372             }
373              
374 0 0         if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
375 0           goto on_error;
376              
377 0           git_str_clear(&data);
378 0 0         if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
    0          
379 0 0         if ((error = store_common(t)) < 0)
380 0           goto on_error;
381             } else {
382 0 0         if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0)
383 0           goto on_error;
384              
385 0 0         if (pkt_type == GIT_PKT_ACK) {
386 0           break;
387 0 0         } else if (pkt_type == GIT_PKT_NAK) {
388 0           continue;
389             } else {
390 0           git_error_set(GIT_ERROR_NET, "unexpected pkt type");
391 0           error = -1;
392 0           goto on_error;
393             }
394             }
395             }
396              
397 0 0         if (t->common.length > 0)
398 0           break;
399              
400 0 0         if (i % 20 == 0 && t->rpc) {
    0          
401             git_pkt_ack *pkt;
402             unsigned int j;
403              
404 0 0         if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
405 0           goto on_error;
406              
407 0 0         git_vector_foreach(&t->common, j, pkt) {
408 0 0         if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
409 0           goto on_error;
410             }
411              
412 0 0         if (git_str_oom(&data)) {
413 0           error = -1;
414 0           goto on_error;
415             }
416             }
417             }
418              
419             /* Tell the other end that we're done negotiating */
420 0 0         if (t->rpc && t->common.length > 0) {
    0          
421             git_pkt_ack *pkt;
422             unsigned int j;
423              
424 0 0         if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
425 0           goto on_error;
426              
427 0 0         git_vector_foreach(&t->common, j, pkt) {
428 0 0         if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
429 0           goto on_error;
430             }
431              
432 0 0         if (git_str_oom(&data)) {
433 0           error = -1;
434 0           goto on_error;
435             }
436             }
437              
438 0 0         if ((error = git_pkt_buffer_done(&data)) < 0)
439 0           goto on_error;
440              
441 0 0         if (t->cancelled.val) {
442 0           git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
443 0           error = GIT_EUSER;
444 0           goto on_error;
445             }
446 0 0         if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
447 0           goto on_error;
448              
449 0           git_str_dispose(&data);
450 0           git_revwalk_free(walk);
451              
452             /* Now let's eat up whatever the server gives us */
453 0 0         if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
    0          
454 0 0         if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0)
455 0           return error;
456              
457 0 0         if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
    0          
458 0           git_error_set(GIT_ERROR_NET, "unexpected pkt type");
459 0           return -1;
460             }
461             } else {
462 0           error = wait_while_ack(buf);
463             }
464              
465 0           return error;
466              
467             on_error:
468 0           git_revwalk_free(walk);
469 0           git_str_dispose(&data);
470 0           return error;
471             }
472              
473 0           static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_indexer_progress *stats)
474             {
475             int recvd;
476              
477             do {
478 0 0         if (t->cancelled.val) {
479 0           git_error_set(GIT_ERROR_NET, "the fetch was cancelled by the user");
480 0           return GIT_EUSER;
481             }
482              
483 0 0         if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
484 0           return -1;
485              
486 0           gitno_consume_n(buf, buf->offset);
487              
488 0 0         if ((recvd = gitno_recv(buf)) < 0)
489 0           return recvd;
490 0 0         } while(recvd > 0);
491              
492 0 0         if (writepack->commit(writepack, stats) < 0)
493 0           return -1;
494              
495 0           return 0;
496             }
497              
498             struct network_packetsize_payload
499             {
500             git_indexer_progress_cb callback;
501             void *payload;
502             git_indexer_progress *stats;
503             size_t last_fired_bytes;
504             };
505              
506 0           static int network_packetsize(size_t received, void *payload)
507             {
508 0           struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
509              
510             /* Accumulate bytes */
511 0           npp->stats->received_bytes += received;
512              
513             /* Fire notification if the threshold is reached */
514 0 0         if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
515 0           npp->last_fired_bytes = npp->stats->received_bytes;
516              
517 0 0         if (npp->callback(npp->stats, npp->payload))
518 0           return GIT_EUSER;
519             }
520              
521 0           return 0;
522             }
523              
524 0           int git_smart__download_pack(
525             git_transport *transport,
526             git_repository *repo,
527             git_indexer_progress *stats)
528             {
529 0           transport_smart *t = (transport_smart *)transport;
530 0           gitno_buffer *buf = &t->buffer;
531             git_odb *odb;
532 0           struct git_odb_writepack *writepack = NULL;
533 0           int error = 0;
534 0           struct network_packetsize_payload npp = {0};
535              
536 0           git_indexer_progress_cb progress_cb = t->connect_opts.callbacks.transfer_progress;
537 0           void *progress_payload = t->connect_opts.callbacks.payload;
538              
539 0           memset(stats, 0, sizeof(git_indexer_progress));
540              
541 0 0         if (progress_cb) {
542 0           npp.callback = progress_cb;
543 0           npp.payload = progress_payload;
544 0           npp.stats = stats;
545 0           t->packetsize_cb = &network_packetsize;
546 0           t->packetsize_payload = &npp;
547              
548             /* We might have something in the buffer already from negotiate_fetch */
549 0 0         if (t->buffer.offset > 0 && !t->cancelled.val)
    0          
550 0 0         if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
551 0           git_atomic32_set(&t->cancelled, 1);
552             }
553              
554 0 0         if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
    0          
555 0           ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0))
556             goto done;
557              
558             /*
559             * If the remote doesn't support the side-band, we can feed
560             * the data directly to the pack writer. Otherwise, we need to
561             * check which one belongs there.
562             */
563 0 0         if (!t->caps.side_band && !t->caps.side_band_64k) {
    0          
564 0           error = no_sideband(t, writepack, buf, stats);
565 0           goto done;
566             }
567              
568             do {
569 0           git_pkt *pkt = NULL;
570              
571             /* Check cancellation before network call */
572 0 0         if (t->cancelled.val) {
573 0           git_error_clear();
574 0           error = GIT_EUSER;
575 0           goto done;
576             }
577              
578 0 0         if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) {
579             /* Check cancellation after network call */
580 0 0         if (t->cancelled.val) {
581 0           git_error_clear();
582 0           error = GIT_EUSER;
583 0 0         } else if (pkt->type == GIT_PKT_PROGRESS) {
584 0 0         if (t->connect_opts.callbacks.sideband_progress) {
585 0           git_pkt_progress *p = (git_pkt_progress *) pkt;
586              
587 0 0         if (p->len > INT_MAX) {
588 0           git_error_set(GIT_ERROR_NET, "oversized progress message");
589 0           error = GIT_ERROR;
590 0           goto done;
591             }
592              
593 0           error = t->connect_opts.callbacks.sideband_progress(p->data, (int)p->len, t->connect_opts.callbacks.payload);
594             }
595 0 0         } else if (pkt->type == GIT_PKT_DATA) {
596 0           git_pkt_data *p = (git_pkt_data *) pkt;
597              
598 0 0         if (p->len)
599 0           error = writepack->append(writepack, p->data, p->len, stats);
600 0 0         } else if (pkt->type == GIT_PKT_FLUSH) {
601             /* A flush indicates the end of the packfile */
602 0           git__free(pkt);
603 0           break;
604             }
605             }
606              
607 0           git_pkt_free(pkt);
608              
609 0 0         if (error < 0)
610 0           goto done;
611              
612 0           } while (1);
613              
614             /*
615             * Trailing execution of progress_cb, if necessary...
616             * Only the callback through the npp datastructure currently
617             * updates the last_fired_bytes value. It is possible that
618             * progress has already been reported with the correct
619             * "received_bytes" value, but until (if?) this is unified
620             * then we will report progress again to be sure that the
621             * correct last received_bytes value is reported.
622             */
623 0 0         if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) {
    0          
624 0           error = npp.callback(npp.stats, npp.payload);
625 0 0         if (error != 0)
626 0           goto done;
627             }
628              
629 0           error = writepack->commit(writepack, stats);
630              
631             done:
632 0 0         if (writepack)
633 0           writepack->free(writepack);
634 0 0         if (progress_cb) {
635 0           t->packetsize_cb = NULL;
636 0           t->packetsize_payload = NULL;
637             }
638              
639 0           return error;
640             }
641              
642 0           static int gen_pktline(git_str *buf, git_push *push)
643             {
644             push_spec *spec;
645             size_t i, len;
646             char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1];
647              
648 0           old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0';
649              
650 0 0         git_vector_foreach(&push->specs, i, spec) {
651 0           len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst);
652              
653 0 0         if (i == 0) {
654 0           ++len; /* '\0' */
655 0 0         if (push->report_status)
656 0           len += strlen(GIT_CAP_REPORT_STATUS) + 1;
657 0           len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
658             }
659              
660 0           git_oid_fmt(old_id, &spec->roid);
661 0           git_oid_fmt(new_id, &spec->loid);
662              
663 0           git_str_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst);
664              
665 0 0         if (i == 0) {
666 0           git_str_putc(buf, '\0');
667             /* Core git always starts their capabilities string with a space */
668 0 0         if (push->report_status) {
669 0           git_str_putc(buf, ' ');
670 0           git_str_printf(buf, GIT_CAP_REPORT_STATUS);
671             }
672 0           git_str_putc(buf, ' ');
673 0           git_str_printf(buf, GIT_CAP_SIDE_BAND_64K);
674             }
675              
676 0           git_str_putc(buf, '\n');
677             }
678              
679 0           git_str_puts(buf, "0000");
680 0 0         return git_str_oom(buf) ? -1 : 0;
681             }
682              
683 0           static int add_push_report_pkt(git_push *push, git_pkt *pkt)
684             {
685             push_status *status;
686              
687 0           switch (pkt->type) {
688             case GIT_PKT_OK:
689 0           status = git__calloc(1, sizeof(push_status));
690 0 0         GIT_ERROR_CHECK_ALLOC(status);
691 0           status->msg = NULL;
692 0           status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
693 0           if (!status->ref ||
694 0           git_vector_insert(&push->status, status) < 0) {
695 0           git_push_status_free(status);
696 0           return -1;
697             }
698 0           break;
699             case GIT_PKT_NG:
700 0           status = git__calloc(1, sizeof(push_status));
701 0 0         GIT_ERROR_CHECK_ALLOC(status);
702 0           status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
703 0           status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
704 0 0         if (!status->ref || !status->msg ||
705 0           git_vector_insert(&push->status, status) < 0) {
706 0           git_push_status_free(status);
707 0           return -1;
708             }
709 0           break;
710             case GIT_PKT_UNPACK:
711 0           push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
712 0           break;
713             case GIT_PKT_FLUSH:
714 0           return GIT_ITEROVER;
715             default:
716 0           git_error_set(GIT_ERROR_NET, "report-status: protocol error");
717 0           return -1;
718             }
719              
720 0           return 0;
721             }
722              
723 0           static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_str *data_pkt_buf)
724             {
725             git_pkt *pkt;
726 0           const char *line, *line_end = NULL;
727             size_t line_len;
728             int error;
729 0           int reading_from_buf = data_pkt_buf->size > 0;
730              
731 0 0         if (reading_from_buf) {
732             /* We had an existing partial packet, so add the new
733             * packet to the buffer and parse the whole thing */
734 0           git_str_put(data_pkt_buf, data_pkt->data, data_pkt->len);
735 0           line = data_pkt_buf->ptr;
736 0           line_len = data_pkt_buf->size;
737             }
738             else {
739 0           line = data_pkt->data;
740 0           line_len = data_pkt->len;
741             }
742              
743 0 0         while (line_len > 0) {
744 0           error = git_pkt_parse_line(&pkt, &line_end, line, line_len);
745              
746 0 0         if (error == GIT_EBUFS) {
747             /* Buffer the data when the inner packet is split
748             * across multiple sideband packets */
749 0 0         if (!reading_from_buf)
750 0           git_str_put(data_pkt_buf, line, line_len);
751 0           error = 0;
752 0           goto done;
753             }
754 0 0         else if (error < 0)
755 0           goto done;
756              
757             /* Advance in the buffer */
758 0           line_len -= (line_end - line);
759 0           line = line_end;
760              
761 0           error = add_push_report_pkt(push, pkt);
762              
763 0           git_pkt_free(pkt);
764              
765 0 0         if (error < 0 && error != GIT_ITEROVER)
    0          
766 0           goto done;
767             }
768              
769 0           error = 0;
770              
771             done:
772 0 0         if (reading_from_buf)
773 0           git_str_consume(data_pkt_buf, line_end);
774 0           return error;
775             }
776              
777 0           static int parse_report(transport_smart *transport, git_push *push)
778             {
779 0           git_pkt *pkt = NULL;
780 0           const char *line_end = NULL;
781 0           gitno_buffer *buf = &transport->buffer;
782             int error, recvd;
783 0           git_str data_pkt_buf = GIT_STR_INIT;
784              
785             for (;;) {
786 0 0         if (buf->offset > 0)
787 0           error = git_pkt_parse_line(&pkt, &line_end,
788 0           buf->data, buf->offset);
789             else
790 0           error = GIT_EBUFS;
791              
792 0 0         if (error < 0 && error != GIT_EBUFS) {
    0          
793 0           error = -1;
794 0           goto done;
795             }
796              
797 0 0         if (error == GIT_EBUFS) {
798 0 0         if ((recvd = gitno_recv(buf)) < 0) {
799 0           error = recvd;
800 0           goto done;
801             }
802              
803 0 0         if (recvd == 0) {
804 0           git_error_set(GIT_ERROR_NET, "early EOF");
805 0           error = GIT_EEOF;
806 0           goto done;
807             }
808 0           continue;
809             }
810              
811 0 0         if (gitno_consume(buf, line_end) < 0)
812 0           return -1;
813              
814 0           error = 0;
815              
816 0           switch (pkt->type) {
817             case GIT_PKT_DATA:
818             /* This is a sideband packet which contains other packets */
819 0           error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf);
820 0           break;
821             case GIT_PKT_ERR:
822 0           git_error_set(GIT_ERROR_NET, "report-status: Error reported: %s",
823 0           ((git_pkt_err *)pkt)->error);
824 0           error = -1;
825 0           break;
826             case GIT_PKT_PROGRESS:
827 0 0         if (transport->connect_opts.callbacks.sideband_progress) {
828 0           git_pkt_progress *p = (git_pkt_progress *) pkt;
829              
830 0 0         if (p->len > INT_MAX) {
831 0           git_error_set(GIT_ERROR_NET, "oversized progress message");
832 0           error = GIT_ERROR;
833 0           goto done;
834             }
835              
836 0           error = transport->connect_opts.callbacks.sideband_progress(p->data, (int)p->len, transport->connect_opts.callbacks.payload);
837             }
838 0           break;
839             default:
840 0           error = add_push_report_pkt(push, pkt);
841 0           break;
842             }
843              
844 0           git_pkt_free(pkt);
845              
846             /* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
847 0 0         if (error == GIT_ITEROVER) {
848 0           error = 0;
849 0 0         if (data_pkt_buf.size > 0) {
850             /* If there was data remaining in the pack data buffer,
851             * then the server sent a partial pkt-line */
852 0           git_error_set(GIT_ERROR_NET, "incomplete pack data pkt-line");
853 0           error = GIT_ERROR;
854             }
855 0           goto done;
856             }
857              
858 0 0         if (error < 0) {
859 0           goto done;
860             }
861 0           }
862             done:
863 0           git_str_dispose(&data_pkt_buf);
864 0           return error;
865             }
866              
867 0           static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
868             {
869 0           git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
870 0 0         GIT_ERROR_CHECK_ALLOC(added);
871              
872 0           added->type = GIT_PKT_REF;
873 0           git_oid_cpy(&added->head.oid, &push_spec->loid);
874 0           added->head.name = git__strdup(push_spec->refspec.dst);
875              
876 0           if (!added->head.name ||
877 0           git_vector_insert(refs, added) < 0) {
878 0           git_pkt_free((git_pkt *)added);
879 0           return -1;
880             }
881              
882 0           return 0;
883             }
884              
885 0           static int update_refs_from_report(
886             git_vector *refs,
887             git_vector *push_specs,
888             git_vector *push_report)
889             {
890             git_pkt_ref *ref;
891             push_spec *push_spec;
892             push_status *push_status;
893             size_t i, j, refs_len;
894             int cmp;
895              
896             /* For each push spec we sent to the server, we should have
897             * gotten back a status packet in the push report */
898 0 0         if (push_specs->length != push_report->length) {
899 0           git_error_set(GIT_ERROR_NET, "report-status: protocol error");
900 0           return -1;
901             }
902              
903             /* We require that push_specs be sorted with push_spec_rref_cmp,
904             * and that push_report be sorted with push_status_ref_cmp */
905 0           git_vector_sort(push_specs);
906 0           git_vector_sort(push_report);
907              
908 0 0         git_vector_foreach(push_specs, i, push_spec) {
909 0           push_status = git_vector_get(push_report, i);
910              
911             /* For each push spec we sent to the server, we should have
912             * gotten back a status packet in the push report which matches */
913 0 0         if (strcmp(push_spec->refspec.dst, push_status->ref)) {
914 0           git_error_set(GIT_ERROR_NET, "report-status: protocol error");
915 0           return -1;
916             }
917             }
918              
919             /* We require that refs be sorted with ref_name_cmp */
920 0           git_vector_sort(refs);
921 0           i = j = 0;
922 0           refs_len = refs->length;
923              
924             /* Merge join push_specs with refs */
925 0 0         while (i < push_specs->length && j < refs_len) {
    0          
926 0           push_spec = git_vector_get(push_specs, i);
927 0           push_status = git_vector_get(push_report, i);
928 0           ref = git_vector_get(refs, j);
929              
930 0           cmp = strcmp(push_spec->refspec.dst, ref->head.name);
931              
932             /* Iterate appropriately */
933 0 0         if (cmp <= 0) i++;
934 0 0         if (cmp >= 0) j++;
935              
936             /* Add case */
937 0 0         if (cmp < 0 &&
    0          
938 0 0         !push_status->msg &&
939 0           add_ref_from_push_spec(refs, push_spec) < 0)
940 0           return -1;
941              
942             /* Update case, delete case */
943 0 0         if (cmp == 0 &&
    0          
944 0           !push_status->msg)
945 0           git_oid_cpy(&ref->head.oid, &push_spec->loid);
946             }
947              
948 0 0         for (; i < push_specs->length; i++) {
949 0           push_spec = git_vector_get(push_specs, i);
950 0           push_status = git_vector_get(push_report, i);
951              
952             /* Add case */
953 0           if (!push_status->msg &&
954 0           add_ref_from_push_spec(refs, push_spec) < 0)
955 0           return -1;
956             }
957              
958             /* Remove any refs which we updated to have a zero OID. */
959 0 0         git_vector_rforeach(refs, i, ref) {
960 0 0         if (git_oid_is_zero(&ref->head.oid)) {
961 0           git_vector_remove(refs, i);
962 0           git_pkt_free((git_pkt *)ref);
963             }
964             }
965              
966 0           git_vector_sort(refs);
967              
968 0           return 0;
969             }
970              
971             struct push_packbuilder_payload
972             {
973             git_smart_subtransport_stream *stream;
974             git_packbuilder *pb;
975             git_push_transfer_progress_cb cb;
976             void *cb_payload;
977             size_t last_bytes;
978             double last_progress_report_time;
979             };
980              
981 0           static int stream_thunk(void *buf, size_t size, void *data)
982             {
983 0           int error = 0;
984 0           struct push_packbuilder_payload *payload = data;
985              
986 0 0         if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
987 0           return error;
988              
989 0 0         if (payload->cb) {
990 0           double current_time = git__timer();
991 0           double elapsed = current_time - payload->last_progress_report_time;
992 0           payload->last_bytes += size;
993              
994 0 0         if (elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
    0          
995 0           payload->last_progress_report_time = current_time;
996 0           error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
997             }
998             }
999              
1000 0           return error;
1001             }
1002              
1003 0           int git_smart__push(git_transport *transport, git_push *push)
1004             {
1005 0           transport_smart *t = (transport_smart *)transport;
1006 0           git_remote_callbacks *cbs = &t->connect_opts.callbacks;
1007 0           struct push_packbuilder_payload packbuilder_payload = {0};
1008 0           git_str pktline = GIT_STR_INIT;
1009 0           int error = 0, need_pack = 0;
1010             push_spec *spec;
1011             unsigned int i;
1012              
1013 0           packbuilder_payload.pb = push->pb;
1014              
1015 0 0         if (cbs && cbs->push_transfer_progress) {
    0          
1016 0           packbuilder_payload.cb = cbs->push_transfer_progress;
1017 0           packbuilder_payload.cb_payload = cbs->payload;
1018             }
1019              
1020             #ifdef PUSH_DEBUG
1021             {
1022             git_remote_head *head;
1023             char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
1024              
1025             git_vector_foreach(&push->remote->refs, i, head) {
1026             git_oid_fmt(hex, &head->oid);
1027             fprintf(stderr, "%s (%s)\n", hex, head->name);
1028             }
1029              
1030             git_vector_foreach(&push->specs, i, spec) {
1031             git_oid_fmt(hex, &spec->roid);
1032             fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
1033             git_oid_fmt(hex, &spec->loid);
1034             fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
1035             spec->rref : spec->lref);
1036             }
1037             }
1038             #endif
1039              
1040             /*
1041             * Figure out if we need to send a packfile; which is in all
1042             * cases except when we only send delete commands
1043             */
1044 0 0         git_vector_foreach(&push->specs, i, spec) {
1045 0 0         if (spec->refspec.src && spec->refspec.src[0] != '\0') {
    0          
1046 0           need_pack = 1;
1047 0           break;
1048             }
1049             }
1050              
1051             /* prepare pack before sending pack header to avoid timeouts */
1052 0 0         if (need_pack && ((error = git_packbuilder__prepare(push->pb))) < 0)
    0          
1053 0           goto done;
1054              
1055 0 0         if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
    0          
1056 0 0         (error = gen_pktline(&pktline, push)) < 0 ||
1057 0           (error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_str_cstr(&pktline), git_str_len(&pktline))) < 0)
1058             goto done;
1059              
1060 0 0         if (need_pack &&
    0          
1061 0           (error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
1062 0           goto done;
1063              
1064             /* If we sent nothing or the server doesn't support report-status, then
1065             * we consider the pack to have been unpacked successfully */
1066 0 0         if (!push->specs.length || !push->report_status)
    0          
1067 0           push->unpack_ok = 1;
1068 0 0         else if ((error = parse_report(t, push)) < 0)
1069 0           goto done;
1070              
1071             /* If progress is being reported write the final report */
1072 0 0         if (cbs && cbs->push_transfer_progress) {
    0          
1073 0           error = cbs->push_transfer_progress(
1074 0           push->pb->nr_written,
1075 0           push->pb->nr_objects,
1076             packbuilder_payload.last_bytes,
1077             cbs->payload);
1078              
1079 0 0         if (error < 0)
1080 0           goto done;
1081             }
1082              
1083 0 0         if (push->status.length) {
1084 0           error = update_refs_from_report(&t->refs, &push->specs, &push->status);
1085 0 0         if (error < 0)
1086 0           goto done;
1087              
1088 0           error = git_smart__update_heads(t, NULL);
1089             }
1090              
1091             done:
1092 0           git_str_dispose(&pktline);
1093 0           return error;
1094             }