File Coverage

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