2 * Copyright (c) 2019 Ori Bernstein <ori@openbsd.org>
3 * Copyright (c) 2021 Stefan Sperling <stsp@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <sys/types.h>
19 #include <sys/queue.h>
37 #include "got_compat.h"
38 #include "got_error.h"
39 #include "got_object.h"
41 #include "got_version.h"
42 #include "got_fetch.h"
43 #include "got_reference.h"
45 #include "got_lib_hash.h"
46 #include "got_lib_delta.h"
47 #include "got_lib_object.h"
48 #include "got_lib_object_parse.h"
49 #include "got_lib_privsep.h"
50 #include "got_lib_pack.h"
51 #include "got_lib_pkt.h"
52 #include "got_lib_gitproto.h"
53 #include "got_lib_ratelimit.h"
54 #include "got_lib_poll.h"
57 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
60 struct got_object
*indexed
;
63 static const struct got_capability got_capabilities
[] = {
64 { GOT_CAPA_AGENT
, "got/" GOT_VERSION_STR
},
65 { GOT_CAPA_OFS_DELTA
, NULL
},
67 { GOT_CAPA_SIDE_BAND_64K
, NULL
},
69 { GOT_CAPA_REPORT_STATUS
, NULL
},
70 { GOT_CAPA_DELETE_REFS
, NULL
},
73 static const struct got_error
*
74 send_upload_progress(struct imsgbuf
*ibuf
, off_t bytes
,
75 struct got_ratelimit
*rl
)
77 const struct got_error
*err
= NULL
;
81 err
= got_ratelimit_check(&elapsed
, rl
);
86 if (imsg_compose(ibuf
, GOT_IMSG_SEND_UPLOAD_PROGRESS
, 0, 0, -1,
87 &bytes
, sizeof(bytes
)) == -1)
88 return got_error_from_errno(
89 "imsg_compose SEND_UPLOAD_PROGRESS");
91 return got_privsep_flush_imsg(ibuf
);
94 static const struct got_error
*
95 send_pack_request(struct imsgbuf
*ibuf
)
97 if (imsg_compose(ibuf
, GOT_IMSG_SEND_PACK_REQUEST
, 0, 0, -1,
99 return got_error_from_errno("imsg_compose SEND_PACK_REQUEST");
100 return got_privsep_flush_imsg(ibuf
);
103 static const struct got_error
*
104 send_done(struct imsgbuf
*ibuf
)
106 if (imsg_compose(ibuf
, GOT_IMSG_SEND_DONE
, 0, 0, -1, NULL
, 0) == -1)
107 return got_error_from_errno("imsg_compose SEND_DONE");
108 return got_privsep_flush_imsg(ibuf
);
111 static const struct got_error
*
112 recv_packfd(int *packfd
, struct imsgbuf
*ibuf
)
114 const struct got_error
*err
;
119 err
= got_privsep_recv_imsg(&imsg
, ibuf
, 0);
123 if (imsg
.hdr
.type
== GOT_IMSG_STOP
) {
124 err
= got_error(GOT_ERR_CANCELLED
);
128 if (imsg
.hdr
.type
!= GOT_IMSG_SEND_PACKFD
) {
129 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
133 if (imsg
.hdr
.len
- IMSG_HEADER_SIZE
!= 0) {
134 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
144 static const struct got_error
*
145 send_pack_file(int sendfd
, int packfd
, struct imsgbuf
*ibuf
)
147 const struct got_error
*err
;
148 unsigned char buf
[8192];
151 struct got_ratelimit rl
;
153 if (lseek(packfd
, 0L, SEEK_SET
) == -1)
154 return got_error_from_errno("lseek");
156 got_ratelimit_init(&rl
, 0, 500);
159 r
= read(packfd
, buf
, sizeof(buf
));
161 return got_error_from_errno("read");
164 err
= got_poll_write_full(sendfd
, buf
, r
);
168 err
= send_upload_progress(ibuf
, wtotal
, &rl
);
173 return send_upload_progress(ibuf
, wtotal
, NULL
);
176 static const struct got_error
*
177 send_error(const char *buf
, size_t len
)
179 static char msg
[1024];
182 for (i
= 0; i
< len
&& i
< sizeof(msg
) - 1; i
++) {
183 if (!isprint((unsigned char)buf
[i
]))
184 return got_error_msg(GOT_ERR_BAD_PACKET
,
185 "non-printable error message received from server");
189 return got_error_msg(GOT_ERR_SEND_FAILED
, msg
);
192 static const struct got_error
*
193 send_their_ref(struct imsgbuf
*ibuf
, struct got_object_id
*refid
,
197 size_t len
, reflen
= strlen(refname
);
199 len
= sizeof(struct got_imsg_send_remote_ref
) + reflen
;
200 if (len
>= MAX_IMSGSIZE
- IMSG_HEADER_SIZE
)
201 return got_error(GOT_ERR_NO_SPACE
);
203 wbuf
= imsg_create(ibuf
, GOT_IMSG_SEND_REMOTE_REF
, 0, 0, len
);
205 return got_error_from_errno("imsg_create SEND_REMOTE_REF");
207 /* Keep in sync with struct got_imsg_send_remote_ref definition! */
208 if (imsg_add(wbuf
, refid
, sizeof(*refid
)) == -1)
209 return got_error_from_errno("imsg_add SEND_REMOTE_REF");
210 if (imsg_add(wbuf
, &reflen
, sizeof(reflen
)) == -1)
211 return got_error_from_errno("imsg_add SEND_REMOTE_REF");
212 if (imsg_add(wbuf
, refname
, reflen
) == -1)
213 return got_error_from_errno("imsg_add SEND_REMOTE_REF");
216 imsg_close(ibuf
, wbuf
);
217 return got_privsep_flush_imsg(ibuf
);
220 static const struct got_error
*
221 send_ref_status(struct imsgbuf
*ibuf
, const char *refname
, int success
,
222 struct got_pathlist_head
*refs
, struct got_pathlist_head
*delete_refs
)
225 size_t i
, len
, reflen
, errmsglen
= 0;
226 struct got_pathlist_entry
*pe
;
229 const char *errmsg
= "";
231 eol
= strchr(refname
, '\n');
233 return got_error_msg(GOT_ERR_BAD_PACKET
,
234 "unexpected message from server");
238 sp
= strchr(refname
, ' ');
242 errmsglen
= strlen(errmsg
);
244 for (i
= 0; i
< errmsglen
; ++i
) {
245 if (!isprint((unsigned char)errmsg
[i
])) {
246 return got_error_msg(GOT_ERR_BAD_PACKET
,
247 "non-printable error message received "
253 reflen
= strlen(refname
);
254 if (!got_ref_name_is_valid(refname
)) {
255 return got_error_msg(GOT_ERR_BAD_PACKET
,
256 "unexpected message from server");
259 TAILQ_FOREACH(pe
, refs
, entry
) {
260 if (strcmp(refname
, pe
->path
) == 0) {
266 TAILQ_FOREACH(pe
, delete_refs
, entry
) {
267 if (strcmp(refname
, pe
->path
) == 0) {
274 return got_error_msg(GOT_ERR_BAD_PACKET
,
275 "unexpected message from server");
278 len
= sizeof(struct got_imsg_send_ref_status
) + reflen
+ errmsglen
;
279 if (len
>= MAX_IMSGSIZE
- IMSG_HEADER_SIZE
)
280 return got_error(GOT_ERR_NO_SPACE
);
282 wbuf
= imsg_create(ibuf
, GOT_IMSG_SEND_REF_STATUS
,
285 return got_error_from_errno("imsg_create SEND_REF_STATUS");
287 /* Keep in sync with struct got_imsg_send_ref_status definition! */
288 if (imsg_add(wbuf
, &success
, sizeof(success
)) == -1)
289 return got_error_from_errno("imsg_add SEND_REF_STATUS");
290 if (imsg_add(wbuf
, &reflen
, sizeof(reflen
)) == -1)
291 return got_error_from_errno("imsg_add SEND_REF_STATUS");
292 if (imsg_add(wbuf
, &errmsglen
, sizeof(errmsglen
)) == -1)
293 return got_error_from_errno("imsg_add SEND_REF_STATUS");
294 if (imsg_add(wbuf
, refname
, reflen
) == -1)
295 return got_error_from_errno("imsg_add SEND_REF_STATUS");
296 if (imsg_add(wbuf
, errmsg
, errmsglen
) == -1)
297 return got_error_from_errno("imsg_add SEND_REF_STATUS");
300 imsg_close(ibuf
, wbuf
);
301 return got_privsep_flush_imsg(ibuf
);
304 static const struct got_error
*
305 describe_refchange(int *n
, int *sent_my_capabilites
,
306 const char *my_capabilities
, char *buf
, size_t bufsize
,
307 const char *refname
, const char *old_hashstr
, const char *new_hashstr
)
309 *n
= snprintf(buf
, bufsize
, "%s %s %s",
310 old_hashstr
, new_hashstr
, refname
);
311 if (*n
< 0 || (size_t)*n
>= bufsize
)
312 return got_error(GOT_ERR_NO_SPACE
);
315 * We must announce our capabilities along with the first
316 * reference. Unfortunately, the protocol requires an embedded
317 * NUL as a separator between reference name and capabilities,
318 * which we have to deal with here.
319 * It also requires a linefeed for terminating packet data.
321 if (!*sent_my_capabilites
&& my_capabilities
!= NULL
) {
323 if (*n
>= bufsize
- 1)
324 return got_error(GOT_ERR_NO_SPACE
);
325 m
= snprintf(buf
+ *n
+ 1, /* offset after '\0' */
326 bufsize
- (*n
+ 1), "%s\n", my_capabilities
);
327 if (m
< 0 || *n
+ m
>= bufsize
)
328 return got_error(GOT_ERR_NO_SPACE
);
330 *sent_my_capabilites
= 1;
332 *n
= strlcat(buf
, "\n", bufsize
);
334 return got_error(GOT_ERR_NO_SPACE
);
340 static const struct got_error
*
341 send_pack(int fd
, struct got_pathlist_head
*refs
,
342 struct got_pathlist_head
*delete_refs
, struct imsgbuf
*ibuf
)
344 const struct got_error
*err
= NULL
;
345 char buf
[GOT_PKT_MAX
];
346 const unsigned char zero_id
[SHA1_DIGEST_LENGTH
] = { 0 };
347 char old_hashstr
[SHA1_DIGEST_STRING_LENGTH
];
348 char new_hashstr
[SHA1_DIGEST_STRING_LENGTH
];
349 struct got_pathlist_head their_refs
;
353 char *id_str
= NULL
, *refname
= NULL
;
354 struct got_object_id
*id
= NULL
;
355 char *server_capabilities
= NULL
, *my_capabilities
= NULL
;
356 struct got_pathlist_entry
*pe
;
357 int sent_my_capabilites
= 0;
359 TAILQ_INIT(&their_refs
);
361 if (TAILQ_EMPTY(refs
) && TAILQ_EMPTY(delete_refs
))
362 return got_error(GOT_ERR_SEND_EMPTY
);
365 err
= got_pkt_readpkt(&n
, fd
, buf
, sizeof(buf
), chattygot
);
370 if (n
>= 4 && strncmp(buf
, "ERR ", 4) == 0) {
371 err
= send_error(&buf
[4], n
- 4);
376 err
= got_gitproto_parse_refline(&id_str
, &refname
,
377 &server_capabilities
, buf
, n
);
381 if (server_capabilities
== NULL
) {
382 server_capabilities
= strdup("");
383 if (server_capabilities
== NULL
) {
384 err
= got_error_from_errno("strdup");
388 if (chattygot
&& server_capabilities
[0] != '\0')
389 fprintf(stderr
, "%s: server capabilities: %s\n",
390 getprogname(), server_capabilities
);
391 err
= got_gitproto_match_capabilities(&my_capabilities
,
392 NULL
, server_capabilities
, got_capabilities
,
393 nitems(got_capabilities
));
397 fprintf(stderr
, "%s: my capabilities:%s\n",
399 my_capabilities
? my_capabilities
: "");
402 if (strstr(refname
, "^{}")) {
404 fprintf(stderr
, "%s: ignoring %s\n",
405 getprogname(), refname
);
410 id
= malloc(sizeof(*id
));
412 err
= got_error_from_errno("malloc");
415 if (!got_parse_object_id(id
, id_str
, GOT_HASH_SHA1
)) {
416 err
= got_error(GOT_ERR_BAD_OBJ_ID_STR
);
419 err
= send_their_ref(ibuf
, id
, refname
);
423 err
= got_pathlist_append(&their_refs
, refname
, id
);
428 fprintf(stderr
, "%s: remote has %s %s\n",
429 getprogname(), refname
, id_str
);
432 refname
= NULL
; /* do not free; owned by their_refs */
433 id
= NULL
; /* do not free; owned by their_refs */
436 if (!TAILQ_EMPTY(delete_refs
)) {
437 if (my_capabilities
== NULL
||
438 strstr(my_capabilities
, GOT_CAPA_DELETE_REFS
) == NULL
) {
439 err
= got_error(GOT_ERR_CAPA_DELETE_REFS
);
444 TAILQ_FOREACH(pe
, delete_refs
, entry
) {
445 const char *refname
= pe
->path
;
446 struct got_pathlist_entry
*their_pe
;
447 struct got_object_id
*their_id
= NULL
;
449 TAILQ_FOREACH(their_pe
, &their_refs
, entry
) {
450 const char *their_refname
= their_pe
->path
;
451 if (got_path_cmp(refname
, their_refname
,
452 strlen(refname
), strlen(their_refname
)) == 0) {
453 their_id
= their_pe
->data
;
457 if (their_id
== NULL
) {
458 err
= got_error_fmt(GOT_ERR_NOT_REF
,
459 "%s does not exist in remote repository",
464 got_sha1_digest_to_str(their_id
->sha1
, old_hashstr
,
465 sizeof(old_hashstr
));
466 got_sha1_digest_to_str(zero_id
, new_hashstr
,
467 sizeof(new_hashstr
));
468 err
= describe_refchange(&n
, &sent_my_capabilites
,
469 my_capabilities
, buf
, sizeof(buf
), refname
,
470 old_hashstr
, new_hashstr
);
473 err
= got_pkt_writepkt(fd
, buf
, n
, chattygot
);
477 fprintf(stderr
, "%s: deleting %s %s\n",
478 getprogname(), refname
, old_hashstr
);
483 TAILQ_FOREACH(pe
, refs
, entry
) {
484 const char *refname
= pe
->path
;
485 struct got_object_id
*id
= pe
->data
;
486 struct got_object_id
*their_id
= NULL
;
487 struct got_pathlist_entry
*their_pe
;
489 TAILQ_FOREACH(their_pe
, &their_refs
, entry
) {
490 const char *their_refname
= their_pe
->path
;
491 if (got_path_cmp(refname
, their_refname
,
492 strlen(refname
), strlen(their_refname
)) == 0) {
493 their_id
= their_pe
->data
;
498 if (got_object_id_cmp(id
, their_id
) == 0) {
501 "%s: no change for %s\n",
502 getprogname(), refname
);
506 got_sha1_digest_to_str(their_id
->sha1
, old_hashstr
,
507 sizeof(old_hashstr
));
509 got_sha1_digest_to_str(zero_id
, old_hashstr
,
510 sizeof(old_hashstr
));
512 got_sha1_digest_to_str(id
->sha1
, new_hashstr
,
513 sizeof(new_hashstr
));
514 err
= describe_refchange(&n
, &sent_my_capabilites
,
515 my_capabilities
, buf
, sizeof(buf
), refname
,
516 old_hashstr
, new_hashstr
);
519 err
= got_pkt_writepkt(fd
, buf
, n
, chattygot
);
524 fprintf(stderr
, "%s: updating %s %s -> %s\n",
525 getprogname(), refname
, old_hashstr
,
528 fprintf(stderr
, "%s: creating %s %s\n",
529 getprogname(), refname
, new_hashstr
);
534 err
= got_pkt_flushpkt(fd
, chattygot
);
538 err
= send_pack_request(ibuf
);
542 err
= recv_packfd(&packfd
, ibuf
);
547 err
= send_pack_file(fd
, packfd
, ibuf
);
552 err
= got_pkt_readpkt(&n
, fd
, buf
, sizeof(buf
), chattygot
);
555 if (n
>= 4 && strncmp(buf
, "ERR ", 4) == 0) {
556 err
= send_error(&buf
[4], n
- 4);
558 } else if (n
< 10 || strncmp(buf
, "unpack ok\n", 10) != 0) {
559 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
560 "unexpected message from server");
565 err
= got_pkt_readpkt(&n
, fd
, buf
, sizeof(buf
), chattygot
);
569 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
570 "unexpected message from server");
572 } else if (n
>= 4 && strncmp(buf
, "ERR ", 4) == 0) {
573 err
= send_error(&buf
[4], n
- 4);
575 } else if (strncmp(buf
, "ok ", 3) == 0) {
576 err
= send_ref_status(ibuf
, buf
+ 3, 1,
580 } else if (strncmp(buf
, "ng ", 3) == 0) {
581 err
= send_ref_status(ibuf
, buf
+ 3, 0,
586 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
587 "unexpected message from server");
593 err
= send_done(ibuf
);
595 got_pathlist_free(&their_refs
, GOT_PATHLIST_FREE_ALL
);
599 free(server_capabilities
);
604 main(int argc
, char **argv
)
606 const struct got_error
*err
= NULL
;
610 struct got_pathlist_head refs
;
611 struct got_pathlist_head delete_refs
;
612 struct got_imsg_send_request send_req
;
613 struct got_imsg_send_ref href
;
622 TAILQ_INIT(&delete_refs
);
624 imsg_init(&ibuf
, GOT_IMSG_FD_CHILD
);
626 /* revoke access to most system calls */
627 if (pledge("stdio recvfd", NULL
) == -1) {
628 err
= got_error_from_errno("pledge");
629 got_privsep_send_error(&ibuf
, err
);
633 /* revoke fs access */
634 if (landlock_no_fs() == -1) {
635 err
= got_error_from_errno("landlock_no_fs");
636 got_privsep_send_error(&ibuf
, err
);
639 if (cap_enter() == -1) {
640 err
= got_error_from_errno("cap_enter");
641 got_privsep_send_error(&ibuf
, err
);
645 if ((err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0)) != 0) {
646 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
650 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
652 if (imsg
.hdr
.type
!= GOT_IMSG_SEND_REQUEST
) {
653 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
656 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
657 if (datalen
< sizeof(send_req
)) {
658 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
661 memcpy(&send_req
, imsg
.data
, sizeof(send_req
));
665 if (send_req
.verbosity
> 0)
666 chattygot
+= send_req
.verbosity
;
668 for (i
= 0; i
< send_req
.nrefs
; i
++) {
669 struct got_object_id
*id
;
672 if ((err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0)) != 0) {
673 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
677 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
679 if (imsg
.hdr
.type
!= GOT_IMSG_SEND_REF
) {
680 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
683 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
684 if (datalen
< sizeof(href
)) {
685 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
688 memcpy(&href
, imsg
.data
, sizeof(href
));
689 if (datalen
- sizeof(href
) < href
.name_len
) {
690 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
693 refname
= malloc(href
.name_len
+ 1);
694 if (refname
== NULL
) {
695 err
= got_error_from_errno("malloc");
698 memcpy(refname
, imsg
.data
+ sizeof(href
), href
.name_len
);
699 refname
[href
.name_len
] = '\0';
702 * Prevent sending of references that won't make any
703 * sense outside the local repository's context.
705 if (strncmp(refname
, "refs/got/", 9) == 0 ||
706 strncmp(refname
, "refs/remotes/", 13) == 0) {
707 err
= got_error_fmt(GOT_ERR_SEND_BAD_REF
,
712 id
= malloc(sizeof(*id
));
715 err
= got_error_from_errno("malloc");
718 memcpy(id
, &href
.id
, sizeof(*id
));
720 err
= got_pathlist_append(&delete_refs
, refname
, id
);
722 err
= got_pathlist_append(&refs
, refname
, id
);
732 err
= send_pack(sendfd
, &refs
, &delete_refs
, &ibuf
);
734 got_pathlist_free(&refs
, GOT_PATHLIST_FREE_ALL
);
735 got_pathlist_free(&delete_refs
, GOT_PATHLIST_FREE_ALL
);
736 if (sendfd
!= -1 && close(sendfd
) == -1 && err
== NULL
)
737 err
= got_error_from_errno("close");
738 if (err
!= NULL
&& err
->code
!= GOT_ERR_CANCELLED
) {
739 fprintf(stderr
, "%s: %s\n", getprogname(), err
->msg
);
740 got_privsep_send_error(&ibuf
, err
);