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