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