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