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