]> git.proxmox.com Git - libgit2.git/blame - src/transports/smart_protocol.c
Merge pull request #1181 from nvloff/allow_note_overwrite
[libgit2.git] / src / transports / smart_protocol.c
CommitLineData
41fb1ca0
PK
1/*
2 * Copyright (C) 2009-2012 the libgit2 contributors
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 */
613d5eb9
PK
7#include "git2.h"
8
41fb1ca0
PK
9#include "smart.h"
10#include "refs.h"
09cc0b92 11#include "repository.h"
613d5eb9
PK
12#include "push.h"
13#include "pack-objects.h"
14#include "remote.h"
41fb1ca0
PK
15
16#define NETWORK_XFER_THRESHOLD (100*1024)
17
18int git_smart__store_refs(transport_smart *t, int flushes)
19{
20 gitno_buffer *buf = &t->buffer;
21 git_vector *refs = &t->refs;
22 int error, flush = 0, recvd;
23 const char *line_end;
24 git_pkt *pkt;
25
613d5eb9
PK
26 /* Clear existing refs in case git_remote_connect() is called again
27 * after git_remote_disconnect().
28 */
29 git_vector_clear(refs);
30
41fb1ca0
PK
31 do {
32 if (buf->offset > 0)
33 error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset);
34 else
35 error = GIT_EBUFS;
36
37 if (error < 0 && error != GIT_EBUFS)
38 return -1;
39
40 if (error == GIT_EBUFS) {
41 if ((recvd = gitno_recv(buf)) < 0)
42 return -1;
43
44 if (recvd == 0 && !flush) {
45 giterr_set(GITERR_NET, "Early EOF");
46 return -1;
47 }
48
49 continue;
50 }
51
52 gitno_consume(buf, line_end);
53 if (pkt->type == GIT_PKT_ERR) {
54 giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
55 git__free(pkt);
56 return -1;
57 }
58
59 if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
60 return -1;
61
62 if (pkt->type == GIT_PKT_FLUSH) {
63 flush++;
64 git_pkt_free(pkt);
65 }
66 } while (flush < flushes);
67
68 return flush;
69}
70
71int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
72{
73 const char *ptr;
74
75 /* No refs or capabilites, odd but not a problem */
76 if (pkt == NULL || pkt->capabilities == NULL)
77 return 0;
78
79 ptr = pkt->capabilities;
80 while (ptr != NULL && *ptr != '\0') {
81 if (*ptr == ' ')
82 ptr++;
83
613d5eb9 84 if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
41fb1ca0
PK
85 caps->common = caps->ofs_delta = 1;
86 ptr += strlen(GIT_CAP_OFS_DELTA);
87 continue;
88 }
89
613d5eb9 90 if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
41fb1ca0
PK
91 caps->common = caps->multi_ack = 1;
92 ptr += strlen(GIT_CAP_MULTI_ACK);
93 continue;
94 }
95
613d5eb9 96 if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
41fb1ca0
PK
97 caps->common = caps->include_tag = 1;
98 ptr += strlen(GIT_CAP_INCLUDE_TAG);
99 continue;
100 }
101
102 /* Keep side-band check after side-band-64k */
613d5eb9 103 if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
41fb1ca0
PK
104 caps->common = caps->side_band_64k = 1;
105 ptr += strlen(GIT_CAP_SIDE_BAND_64K);
106 continue;
107 }
108
613d5eb9 109 if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
41fb1ca0
PK
110 caps->common = caps->side_band = 1;
111 ptr += strlen(GIT_CAP_SIDE_BAND);
112 continue;
113 }
114
613d5eb9
PK
115 if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
116 caps->common = caps->delete_refs = 1;
117 ptr += strlen(GIT_CAP_DELETE_REFS);
118 continue;
119 }
120
41fb1ca0
PK
121 /* We don't know this capability, so skip it */
122 ptr = strchr(ptr, ' ');
123 }
124
125 return 0;
126}
127
128static int recv_pkt(git_pkt **out, gitno_buffer *buf)
129{
130 const char *ptr = buf->data, *line_end = ptr;
131 git_pkt *pkt;
132 int pkt_type, error = 0, ret;
133
134 do {
135 if (buf->offset > 0)
136 error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
137 else
138 error = GIT_EBUFS;
139
140 if (error == 0)
141 break; /* return the pkt */
142
143 if (error < 0 && error != GIT_EBUFS)
144 return -1;
145
146 if ((ret = gitno_recv(buf)) < 0)
147 return -1;
148 } while (error);
149
150 gitno_consume(buf, line_end);
151 pkt_type = pkt->type;
152 if (out != NULL)
153 *out = pkt;
154 else
155 git__free(pkt);
156
157 return pkt_type;
158}
159
160static int store_common(transport_smart *t)
161{
162 git_pkt *pkt = NULL;
163 gitno_buffer *buf = &t->buffer;
164
165 do {
166 if (recv_pkt(&pkt, buf) < 0)
167 return -1;
168
169 if (pkt->type == GIT_PKT_ACK) {
170 if (git_vector_insert(&t->common, pkt) < 0)
171 return -1;
172 } else {
173 git__free(pkt);
174 return 0;
175 }
176
177 } while (1);
178
179 return 0;
180}
181
182static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
183{
184 git_revwalk *walk;
185 git_strarray refs;
186 unsigned int i;
187 git_reference *ref;
188
189 if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0)
190 return -1;
191
192 if (git_revwalk_new(&walk, repo) < 0)
193 return -1;
194
195 git_revwalk_sorting(walk, GIT_SORT_TIME);
196
197 for (i = 0; i < refs.count; ++i) {
198 /* No tags */
199 if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
200 continue;
201
202 if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
203 goto on_error;
204
205 if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
206 continue;
2508cc66 207 if (git_revwalk_push(walk, git_reference_target(ref)) < 0)
41fb1ca0
PK
208 goto on_error;
209
210 git_reference_free(ref);
211 }
212
213 git_strarray_free(&refs);
214 *out = walk;
215 return 0;
216
217on_error:
218 git_reference_free(ref);
219 git_strarray_free(&refs);
220 return -1;
221}
222
223int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count)
224{
225 transport_smart *t = (transport_smart *)transport;
226 gitno_buffer *buf = &t->buffer;
227 git_buf data = GIT_BUF_INIT;
228 git_revwalk *walk = NULL;
229 int error = -1, pkt_type;
230 unsigned int i;
231 git_oid oid;
232
233 /* No own logic, do our thing */
234 if (git_pkt_buffer_wants(refs, count, &t->caps, &data) < 0)
235 return -1;
236
237 if (fetch_setup_walk(&walk, repo) < 0)
238 goto on_error;
239 /*
240 * We don't support any kind of ACK extensions, so the negotiation
241 * boils down to sending what we have and listening for an ACK
242 * every once in a while.
243 */
244 i = 0;
245 while ((error = git_revwalk_next(&oid, walk)) == 0) {
246 git_pkt_buffer_have(&oid, &data);
247 i++;
248 if (i % 20 == 0) {
249 if (t->cancelled.val) {
250 giterr_set(GITERR_NET, "The fetch was cancelled by the user");
251 error = GIT_EUSER;
252 goto on_error;
253 }
254
255 git_pkt_buffer_flush(&data);
256 if (git_buf_oom(&data))
257 goto on_error;
258
259 if (git_smart__negotiation_step(&t->parent, data.ptr, data.size) < 0)
260 goto on_error;
261
262 git_buf_clear(&data);
263 if (t->caps.multi_ack) {
264 if (store_common(t) < 0)
265 goto on_error;
266 } else {
267 pkt_type = recv_pkt(NULL, buf);
268
269 if (pkt_type == GIT_PKT_ACK) {
270 break;
271 } else if (pkt_type == GIT_PKT_NAK) {
272 continue;
273 } else {
274 giterr_set(GITERR_NET, "Unexpected pkt type");
275 goto on_error;
276 }
277 }
278 }
279
280 if (t->common.length > 0)
281 break;
282
283 if (i % 20 == 0 && t->rpc) {
284 git_pkt_ack *pkt;
285 unsigned int i;
286
287 if (git_pkt_buffer_wants(refs, count, &t->caps, &data) < 0)
288 goto on_error;
289
290 git_vector_foreach(&t->common, i, pkt) {
291 git_pkt_buffer_have(&pkt->oid, &data);
292 }
293
294 if (git_buf_oom(&data))
295 goto on_error;
296 }
297 }
298
299 if (error < 0 && error != GIT_ITEROVER)
300 goto on_error;
301
302 /* Tell the other end that we're done negotiating */
303 if (t->rpc && t->common.length > 0) {
304 git_pkt_ack *pkt;
305 unsigned int i;
306
307 if (git_pkt_buffer_wants(refs, count, &t->caps, &data) < 0)
308 goto on_error;
309
310 git_vector_foreach(&t->common, i, pkt) {
311 git_pkt_buffer_have(&pkt->oid, &data);
312 }
313
314 if (git_buf_oom(&data))
315 goto on_error;
316 }
317
318 git_pkt_buffer_done(&data);
319 if (t->cancelled.val) {
320 giterr_set(GITERR_NET, "The fetch was cancelled by the user");
321 error = GIT_EUSER;
322 goto on_error;
323 }
324 if (git_smart__negotiation_step(&t->parent, data.ptr, data.size) < 0)
325 goto on_error;
326
327 git_buf_free(&data);
328 git_revwalk_free(walk);
329
330 /* Now let's eat up whatever the server gives us */
331 if (!t->caps.multi_ack) {
332 pkt_type = recv_pkt(NULL, buf);
333 if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
334 giterr_set(GITERR_NET, "Unexpected pkt type");
335 return -1;
336 }
337 } else {
338 git_pkt_ack *pkt;
339 do {
340 if (recv_pkt((git_pkt **)&pkt, buf) < 0)
341 return -1;
342
343 if (pkt->type == GIT_PKT_NAK ||
344 (pkt->type == GIT_PKT_ACK && pkt->status != GIT_ACK_CONTINUE)) {
345 git__free(pkt);
346 break;
347 }
348
349 git__free(pkt);
350 } while (1);
351 }
352
353 return 0;
354
355on_error:
356 git_revwalk_free(walk);
357 git_buf_free(&data);
358 return error;
359}
360
09cc0b92 361static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats)
41fb1ca0
PK
362{
363 int recvd;
364
365 do {
366 if (t->cancelled.val) {
367 giterr_set(GITERR_NET, "The fetch was cancelled by the user");
368 return GIT_EUSER;
369 }
370
09cc0b92 371 if (writepack->add(writepack, buf->data, buf->offset, stats) < 0)
41fb1ca0
PK
372 return -1;
373
374 gitno_consume_n(buf, buf->offset);
375
376 if ((recvd = gitno_recv(buf)) < 0)
377 return -1;
378 } while(recvd > 0);
379
09cc0b92 380 if (writepack->commit(writepack, stats))
41fb1ca0
PK
381 return -1;
382
383 return 0;
384}
385
a8122b5d 386struct network_packetsize_payload
41fb1ca0
PK
387{
388 git_transfer_progress_callback callback;
389 void *payload;
390 git_transfer_progress *stats;
438906e1 391 size_t last_fired_bytes;
41fb1ca0
PK
392};
393
a8122b5d 394static void network_packetsize(size_t received, void *payload)
41fb1ca0
PK
395{
396 struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
397
398 /* Accumulate bytes */
399 npp->stats->received_bytes += received;
400
401 /* Fire notification if the threshold is reached */
402 if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
a8122b5d
RB
403 npp->last_fired_bytes = npp->stats->received_bytes;
404 npp->callback(npp->stats, npp->payload);
41fb1ca0
PK
405 }
406}
407
408int git_smart__download_pack(
409 git_transport *transport,
410 git_repository *repo,
411 git_transfer_progress *stats,
412 git_transfer_progress_callback progress_cb,
413 void *progress_payload)
414{
415 transport_smart *t = (transport_smart *)transport;
41fb1ca0 416 gitno_buffer *buf = &t->buffer;
09cc0b92
ET
417 git_odb *odb;
418 struct git_odb_writepack *writepack = NULL;
41fb1ca0
PK
419 int error = -1;
420 struct network_packetsize_payload npp = {0};
421
438906e1
PK
422 memset(stats, 0, sizeof(git_transfer_progress));
423
41fb1ca0
PK
424 if (progress_cb) {
425 npp.callback = progress_cb;
426 npp.payload = progress_payload;
427 npp.stats = stats;
428 t->packetsize_cb = &network_packetsize;
429 t->packetsize_payload = &npp;
438906e1
PK
430
431 /* We might have something in the buffer already from negotiate_fetch */
432 if (t->buffer.offset > 0)
a8122b5d 433 t->packetsize_cb((int)t->buffer.offset, t->packetsize_payload);
41fb1ca0
PK
434 }
435
09cc0b92
ET
436 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
437 ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0))
41fb1ca0
PK
438 goto on_error;
439
41fb1ca0
PK
440 /*
441 * If the remote doesn't support the side-band, we can feed
09cc0b92 442 * the data directly to the pack writer. Otherwise, we need to
41fb1ca0
PK
443 * check which one belongs there.
444 */
445 if (!t->caps.side_band && !t->caps.side_band_64k) {
09cc0b92 446 if (no_sideband(t, writepack, buf, stats) < 0)
41fb1ca0
PK
447 goto on_error;
448
438906e1 449 goto on_success;
41fb1ca0
PK
450 }
451
452 do {
453 git_pkt *pkt;
454
455 if (t->cancelled.val) {
456 giterr_set(GITERR_NET, "The fetch was cancelled by the user");
457 error = GIT_EUSER;
458 goto on_error;
459 }
460
461 if (recv_pkt(&pkt, buf) < 0)
462 goto on_error;
463
464 if (pkt->type == GIT_PKT_PROGRESS) {
465 if (t->progress_cb) {
466 git_pkt_progress *p = (git_pkt_progress *) pkt;
467 t->progress_cb(p->data, p->len, t->message_cb_payload);
468 }
469 git__free(pkt);
470 } else if (pkt->type == GIT_PKT_DATA) {
471 git_pkt_data *p = (git_pkt_data *) pkt;
09cc0b92 472 if (writepack->add(writepack, p->data, p->len, stats) < 0)
41fb1ca0
PK
473 goto on_error;
474
475 git__free(pkt);
476 } else if (pkt->type == GIT_PKT_FLUSH) {
477 /* A flush indicates the end of the packfile */
478 git__free(pkt);
479 break;
480 }
481 } while (1);
482
09cc0b92 483 if (writepack->commit(writepack, stats) < 0)
41fb1ca0
PK
484 goto on_error;
485
438906e1
PK
486on_success:
487 error = 0;
41fb1ca0
PK
488
489on_error:
613d5eb9
PK
490 if (writepack)
491 writepack->free(writepack);
438906e1
PK
492
493 /* Trailing execution of progress_cb, if necessary */
494 if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes)
495 npp.callback(npp.stats, npp.payload);
496
41fb1ca0
PK
497 return error;
498}
613d5eb9
PK
499
500static int gen_pktline(git_buf *buf, git_push *push)
501{
502 git_remote_head *head;
503 push_spec *spec;
504 unsigned int i, j, len;
505 char hex[41]; hex[40] = '\0';
506
507 git_vector_foreach(&push->specs, i, spec) {
508 len = 2*GIT_OID_HEXSZ + 7;
509
510 if (i == 0) {
511 len +=1; /* '\0' */
512 if (push->report_status)
513 len += strlen(GIT_CAP_REPORT_STATUS);
514 }
515
516 if (spec->lref) {
517 len += spec->rref ? strlen(spec->rref) : strlen(spec->lref);
518
519 if (git_oid_iszero(&spec->roid)) {
520
521 /*
522 * Create remote reference
523 */
524 git_oid_fmt(hex, &spec->loid);
525 git_buf_printf(buf, "%04x%s %s %s", len,
526 GIT_OID_HEX_ZERO, hex,
527 spec->rref ? spec->rref : spec->lref);
528
529 } else {
530
531 /*
532 * Update remote reference
533 */
534 git_oid_fmt(hex, &spec->roid);
535 git_buf_printf(buf, "%04x%s ", len, hex);
536
537 git_oid_fmt(hex, &spec->loid);
538 git_buf_printf(buf, "%s %s", hex,
539 spec->rref ? spec->rref : spec->lref);
540 }
541 } else {
542 /*
543 * Delete remote reference
544 */
545 git_vector_foreach(&push->remote->refs, j, head) {
546 if (!strcmp(spec->rref, head->name)) {
547 len += strlen(spec->rref);
548
549 git_oid_fmt(hex, &head->oid);
550 git_buf_printf(buf, "%04x%s %s %s", len,
551 hex, GIT_OID_HEX_ZERO, head->name);
552
553 break;
554 }
555 }
556 }
557
558 if (i == 0) {
559 git_buf_putc(buf, '\0');
560 if (push->report_status)
561 git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
562 }
563
564 git_buf_putc(buf, '\n');
565 }
566 git_buf_puts(buf, "0000");
567 return git_buf_oom(buf) ? -1 : 0;
568}
569
570static int parse_report(gitno_buffer *buf, git_push *push)
571{
572 git_pkt *pkt;
573 const char *line_end;
574 int error, recvd;
575
576 for (;;) {
577 if (buf->offset > 0)
578 error = git_pkt_parse_line(&pkt, buf->data,
579 &line_end, buf->offset);
580 else
581 error = GIT_EBUFS;
582
583 if (error < 0 && error != GIT_EBUFS)
584 return -1;
585
586 if (error == GIT_EBUFS) {
587 if ((recvd = gitno_recv(buf)) < 0)
588 return -1;
589
590 if (recvd == 0) {
591 giterr_set(GITERR_NET, "Early EOF");
592 return -1;
593 }
594 continue;
595 }
596
597 gitno_consume(buf, line_end);
598
599 if (pkt->type == GIT_PKT_OK) {
6762fe08 600 push_status *status = git__malloc(sizeof(push_status));
613d5eb9
PK
601 GITERR_CHECK_ALLOC(status);
602 status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
603 status->msg = NULL;
604 git_pkt_free(pkt);
605 if (git_vector_insert(&push->status, status) < 0) {
606 git__free(status);
607 return -1;
608 }
609 continue;
610 }
611
612 if (pkt->type == GIT_PKT_NG) {
6762fe08 613 push_status *status = git__malloc(sizeof(push_status));
613d5eb9
PK
614 GITERR_CHECK_ALLOC(status);
615 status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
616 status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
617 git_pkt_free(pkt);
618 if (git_vector_insert(&push->status, status) < 0) {
619 git__free(status);
620 return -1;
621 }
622 continue;
623 }
624
625 if (pkt->type == GIT_PKT_UNPACK) {
626 push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
627 git_pkt_free(pkt);
628 continue;
629 }
630
631 if (pkt->type == GIT_PKT_FLUSH) {
632 git_pkt_free(pkt);
633 return 0;
634 }
635
636 git_pkt_free(pkt);
637 giterr_set(GITERR_NET, "report-status: protocol error");
638 return -1;
639 }
640}
641
642static int stream_thunk(void *buf, size_t size, void *data)
643{
644 git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data;
645
646 return s->write(s, (const char *)buf, size);
647}
648
649int git_smart__push(git_transport *transport, git_push *push)
650{
651 transport_smart *t = (transport_smart *)transport;
652 git_smart_subtransport_stream *s;
653 git_buf pktline = GIT_BUF_INIT;
654 char *url = NULL;
655 int error = -1;
656
657#ifdef PUSH_DEBUG
658{
659 git_remote_head *head;
660 push_spec *spec;
661 unsigned int i;
662 char hex[41]; hex[40] = '\0';
663
664 git_vector_foreach(&push->remote->refs, i, head) {
665 git_oid_fmt(hex, &head->oid);
666 fprintf(stderr, "%s (%s)\n", hex, head->name);
667 }
668
669 git_vector_foreach(&push->specs, i, spec) {
670 git_oid_fmt(hex, &spec->roid);
671 fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
672 git_oid_fmt(hex, &spec->loid);
673 fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
674 spec->rref : spec->lref);
675 }
676}
677#endif
678
679 if (git_smart__get_push_stream(t, &s) < 0 ||
680 gen_pktline(&pktline, push) < 0 ||
681 s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0 ||
682 git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0)
683 goto on_error;
684
685 /* If we sent nothing or the server doesn't support report-status, then
686 * we consider the pack to have been unpacked successfully */
687 if (!push->specs.length || !push->report_status)
688 push->unpack_ok = 1;
689 else if (parse_report(&t->buffer, push) < 0)
690 goto on_error;
691
692 /* If we updated at least one ref, then we need to re-acquire the list of
693 * refs so the caller can call git_remote_update_tips afterward. TODO: Use
694 * the data from the push report to do this without another network call */
695 if (push->specs.length) {
696 git_cred_acquire_cb cred_cb = t->cred_acquire_cb;
59bccf33 697 void *cred_payload = t->cred_acquire_payload;
613d5eb9
PK
698 int flags = t->flags;
699
700 url = git__strdup(t->url);
701
702 if (!url || t->parent.close(&t->parent) < 0 ||
59bccf33 703 t->parent.connect(&t->parent, url, cred_cb, cred_payload, GIT_DIRECTION_PUSH, flags))
613d5eb9
PK
704 goto on_error;
705 }
706
707 error = 0;
708
709on_error:
710 git__free(url);
711 git_buf_free(&pktline);
712
713 return error;
714}