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>
40 #include "got_compat.h"
41 #include "got_error.h"
42 #include "got_object.h"
44 #include "got_version.h"
45 #include "got_fetch.h"
46 #include "got_reference.h"
48 #include "got_lib_hash.h"
49 #include "got_lib_delta.h"
50 #include "got_lib_object.h"
51 #include "got_lib_object_parse.h"
52 #include "got_lib_privsep.h"
53 #include "got_lib_pack.h"
54 #include "got_lib_pkt.h"
55 #include "got_lib_gitproto.h"
56 #include "got_lib_ratelimit.h"
57 #include "got_lib_poll.h"
60 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
63 struct got_object
*indexed
;
66 static const struct got_capability got_capabilities
[] = {
67 { GOT_CAPA_AGENT
, "got/" GOT_VERSION_STR
},
68 { GOT_CAPA_OFS_DELTA
, NULL
},
70 { GOT_CAPA_SIDE_BAND_64K
, NULL
},
72 { GOT_CAPA_REPORT_STATUS
, NULL
},
73 { GOT_CAPA_DELETE_REFS
, NULL
},
76 static const struct got_error
*
77 send_upload_progress(struct imsgbuf
*ibuf
, off_t bytes
,
78 struct got_ratelimit
*rl
)
80 const struct got_error
*err
= NULL
;
84 err
= got_ratelimit_check(&elapsed
, rl
);
89 if (imsg_compose(ibuf
, GOT_IMSG_SEND_UPLOAD_PROGRESS
, 0, 0, -1,
90 &bytes
, sizeof(bytes
)) == -1)
91 return got_error_from_errno(
92 "imsg_compose SEND_UPLOAD_PROGRESS");
94 return got_privsep_flush_imsg(ibuf
);
97 static const struct got_error
*
98 send_pack_request(struct imsgbuf
*ibuf
)
100 if (imsg_compose(ibuf
, GOT_IMSG_SEND_PACK_REQUEST
, 0, 0, -1,
102 return got_error_from_errno("imsg_compose SEND_PACK_REQUEST");
103 return got_privsep_flush_imsg(ibuf
);
106 static const struct got_error
*
107 send_done(struct imsgbuf
*ibuf
)
109 if (imsg_compose(ibuf
, GOT_IMSG_SEND_DONE
, 0, 0, -1, NULL
, 0) == -1)
110 return got_error_from_errno("imsg_compose SEND_DONE");
111 return got_privsep_flush_imsg(ibuf
);
114 static const struct got_error
*
115 recv_packfd(int *packfd
, struct imsgbuf
*ibuf
)
117 const struct got_error
*err
;
122 err
= got_privsep_recv_imsg(&imsg
, ibuf
, 0);
126 if (imsg
.hdr
.type
== GOT_IMSG_STOP
) {
127 err
= got_error(GOT_ERR_CANCELLED
);
131 if (imsg
.hdr
.type
!= GOT_IMSG_SEND_PACKFD
) {
132 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
136 if (imsg
.hdr
.len
- IMSG_HEADER_SIZE
!= 0) {
137 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
141 *packfd
= imsg_get_fd(&imsg
);
147 static const struct got_error
*
148 send_pack_file(int sendfd
, int packfd
, struct imsgbuf
*ibuf
)
150 const struct got_error
*err
;
151 unsigned char buf
[8192];
154 struct got_ratelimit rl
;
156 if (lseek(packfd
, 0L, SEEK_SET
) == -1)
157 return got_error_from_errno("lseek");
159 got_ratelimit_init(&rl
, 0, 500);
162 r
= read(packfd
, buf
, sizeof(buf
));
164 return got_error_from_errno("read");
167 err
= got_poll_write_full(sendfd
, buf
, r
);
171 err
= send_upload_progress(ibuf
, wtotal
, &rl
);
176 return send_upload_progress(ibuf
, wtotal
, NULL
);
179 static const struct got_error
*
180 send_error(const char *buf
, size_t len
)
182 static char msg
[1024];
185 for (i
= 0; i
< len
&& i
< sizeof(msg
) - 1; i
++) {
186 if (!isprint((unsigned char)buf
[i
]))
187 return got_error_msg(GOT_ERR_BAD_PACKET
,
188 "non-printable error message received from server");
192 return got_error_msg(GOT_ERR_SEND_FAILED
, msg
);
195 static const struct got_error
*
196 send_their_ref(struct imsgbuf
*ibuf
, struct got_object_id
*refid
,
200 size_t len
, reflen
= strlen(refname
);
202 len
= sizeof(struct got_imsg_send_remote_ref
) + reflen
;
203 if (len
>= MAX_IMSGSIZE
- IMSG_HEADER_SIZE
)
204 return got_error(GOT_ERR_NO_SPACE
);
206 wbuf
= imsg_create(ibuf
, GOT_IMSG_SEND_REMOTE_REF
, 0, 0, len
);
208 return got_error_from_errno("imsg_create SEND_REMOTE_REF");
210 /* Keep in sync with struct got_imsg_send_remote_ref definition! */
211 if (imsg_add(wbuf
, refid
, sizeof(*refid
)) == -1)
212 return got_error_from_errno("imsg_add SEND_REMOTE_REF");
213 if (imsg_add(wbuf
, &reflen
, sizeof(reflen
)) == -1)
214 return got_error_from_errno("imsg_add SEND_REMOTE_REF");
215 if (imsg_add(wbuf
, refname
, reflen
) == -1)
216 return got_error_from_errno("imsg_add SEND_REMOTE_REF");
218 imsg_close(ibuf
, wbuf
);
219 return got_privsep_flush_imsg(ibuf
);
222 static const struct got_error
*
223 send_ref_status(struct imsgbuf
*ibuf
, const char *refname
, int success
,
224 struct got_pathlist_head
*refs
, struct got_pathlist_head
*delete_refs
)
227 size_t i
, len
, reflen
, errmsglen
= 0;
228 struct got_pathlist_entry
*pe
;
231 const char *errmsg
= "";
233 eol
= strchr(refname
, '\n');
235 return got_error_msg(GOT_ERR_BAD_PACKET
,
236 "unexpected message from server");
240 sp
= strchr(refname
, ' ');
244 errmsglen
= strlen(errmsg
);
246 for (i
= 0; i
< errmsglen
; ++i
) {
247 if (!isprint((unsigned char)errmsg
[i
])) {
248 return got_error_msg(GOT_ERR_BAD_PACKET
,
249 "non-printable error message received "
255 reflen
= strlen(refname
);
256 if (!got_ref_name_is_valid(refname
)) {
257 return got_error_msg(GOT_ERR_BAD_PACKET
,
258 "unexpected message from server");
261 RB_FOREACH(pe
, got_pathlist_head
, refs
) {
262 if (strcmp(refname
, pe
->path
) == 0) {
268 RB_FOREACH(pe
, got_pathlist_head
, delete_refs
) {
269 if (strcmp(refname
, pe
->path
) == 0) {
276 return got_error_msg(GOT_ERR_BAD_PACKET
,
277 "unexpected message from server");
280 len
= sizeof(struct got_imsg_send_ref_status
) + reflen
+ errmsglen
;
281 if (len
>= MAX_IMSGSIZE
- IMSG_HEADER_SIZE
)
282 return got_error(GOT_ERR_NO_SPACE
);
284 wbuf
= imsg_create(ibuf
, GOT_IMSG_SEND_REF_STATUS
,
287 return got_error_from_errno("imsg_create SEND_REF_STATUS");
289 /* Keep in sync with struct got_imsg_send_ref_status definition! */
290 if (imsg_add(wbuf
, &success
, sizeof(success
)) == -1)
291 return got_error_from_errno("imsg_add SEND_REF_STATUS");
292 if (imsg_add(wbuf
, &reflen
, sizeof(reflen
)) == -1)
293 return got_error_from_errno("imsg_add SEND_REF_STATUS");
294 if (imsg_add(wbuf
, &errmsglen
, sizeof(errmsglen
)) == -1)
295 return got_error_from_errno("imsg_add SEND_REF_STATUS");
296 if (imsg_add(wbuf
, refname
, reflen
) == -1)
297 return got_error_from_errno("imsg_add SEND_REF_STATUS");
298 if (imsg_add(wbuf
, errmsg
, errmsglen
) == -1)
299 return got_error_from_errno("imsg_add SEND_REF_STATUS");
301 imsg_close(ibuf
, wbuf
);
302 return got_privsep_flush_imsg(ibuf
);
305 static const struct got_error
*
306 describe_refchange(int *n
, int *sent_my_capabilites
,
307 const char *my_capabilities
, char *buf
, size_t bufsize
,
308 const char *refname
, const char *old_hashstr
, const char *new_hashstr
)
310 *n
= snprintf(buf
, bufsize
, "%s %s %s",
311 old_hashstr
, new_hashstr
, refname
);
312 if (*n
< 0 || (size_t)*n
>= bufsize
)
313 return got_error(GOT_ERR_NO_SPACE
);
316 * We must announce our capabilities along with the first
317 * reference. Unfortunately, the protocol requires an embedded
318 * NUL as a separator between reference name and capabilities,
319 * which we have to deal with here.
320 * It also requires a linefeed for terminating packet data.
322 if (!*sent_my_capabilites
&& my_capabilities
!= NULL
) {
324 if (*n
>= bufsize
- 1)
325 return got_error(GOT_ERR_NO_SPACE
);
326 m
= snprintf(buf
+ *n
+ 1, /* offset after '\0' */
327 bufsize
- (*n
+ 1), "%s\n", my_capabilities
);
328 if (m
< 0 || *n
+ m
>= bufsize
)
329 return got_error(GOT_ERR_NO_SPACE
);
331 *sent_my_capabilites
= 1;
333 *n
= strlcat(buf
, "\n", bufsize
);
335 return got_error(GOT_ERR_NO_SPACE
);
341 static const struct got_error
*
342 send_pack(int fd
, struct got_pathlist_head
*refs
,
343 struct got_pathlist_head
*delete_refs
, struct imsgbuf
*ibuf
)
345 const struct got_error
*err
= NULL
;
346 char buf
[GOT_PKT_MAX
];
347 const unsigned char zero_id
[SHA1_DIGEST_LENGTH
] = { 0 };
348 char old_hashstr
[SHA1_DIGEST_STRING_LENGTH
];
349 char new_hashstr
[SHA1_DIGEST_STRING_LENGTH
];
350 struct got_pathlist_head their_refs
;
354 char *id_str
= NULL
, *refname
= NULL
;
355 struct got_object_id
*id
= NULL
;
356 char *server_capabilities
= NULL
, *my_capabilities
= NULL
;
357 struct got_pathlist_entry
*pe
;
358 int sent_my_capabilites
= 0;
360 RB_INIT(&their_refs
);
362 if (RB_EMPTY(refs
) && RB_EMPTY(delete_refs
))
363 return got_error(GOT_ERR_SEND_EMPTY
);
366 err
= got_pkt_readpkt(&n
, fd
, buf
, sizeof(buf
), chattygot
,
372 if (n
>= 4 && strncmp(buf
, "ERR ", 4) == 0) {
373 err
= send_error(&buf
[4], n
- 4);
378 err
= got_gitproto_parse_refline(&id_str
, &refname
,
379 &server_capabilities
, buf
, n
);
383 if (server_capabilities
== NULL
) {
384 server_capabilities
= strdup("");
385 if (server_capabilities
== NULL
) {
386 err
= got_error_from_errno("strdup");
390 if (chattygot
&& server_capabilities
[0] != '\0')
391 fprintf(stderr
, "%s: server capabilities: %s\n",
392 getprogname(), server_capabilities
);
393 err
= got_gitproto_match_capabilities(&my_capabilities
,
394 NULL
, server_capabilities
, got_capabilities
,
395 nitems(got_capabilities
));
399 fprintf(stderr
, "%s: my capabilities:%s\n",
401 my_capabilities
? my_capabilities
: "");
404 if (strstr(refname
, "^{}")) {
406 fprintf(stderr
, "%s: ignoring %s\n",
407 getprogname(), refname
);
412 id
= malloc(sizeof(*id
));
414 err
= got_error_from_errno("malloc");
417 if (!got_parse_object_id(id
, id_str
, GOT_HASH_SHA1
)) {
418 err
= got_error(GOT_ERR_BAD_OBJ_ID_STR
);
421 err
= send_their_ref(ibuf
, id
, refname
);
425 err
= got_pathlist_insert(&pe
, &their_refs
, refname
, id
);
426 if (err
|| pe
== NULL
) {
435 fprintf(stderr
, "%s: remote has %s %s\n",
436 getprogname(), refname
, id_str
);
439 refname
= NULL
; /* do not free; owned by their_refs */
440 id
= NULL
; /* do not free; owned by their_refs */
443 if (!RB_EMPTY(delete_refs
)) {
444 if (my_capabilities
== NULL
||
445 strstr(my_capabilities
, GOT_CAPA_DELETE_REFS
) == NULL
) {
446 err
= got_error(GOT_ERR_CAPA_DELETE_REFS
);
451 RB_FOREACH(pe
, got_pathlist_head
, delete_refs
) {
452 const char *refname
= pe
->path
;
453 struct got_pathlist_entry
*their_pe
;
454 struct got_object_id
*their_id
= NULL
;
456 RB_FOREACH(their_pe
, got_pathlist_head
, &their_refs
) {
457 const char *their_refname
= their_pe
->path
;
458 if (got_path_cmp(refname
, their_refname
,
459 strlen(refname
), strlen(their_refname
)) == 0) {
460 their_id
= their_pe
->data
;
464 if (their_id
== NULL
) {
465 err
= got_error_fmt(GOT_ERR_NOT_REF
,
466 "%s does not exist in remote repository",
471 got_object_id_hex(their_id
, old_hashstr
,
472 sizeof(old_hashstr
));
473 got_sha1_digest_to_str(zero_id
, new_hashstr
,
474 sizeof(new_hashstr
));
475 err
= describe_refchange(&n
, &sent_my_capabilites
,
476 my_capabilities
, buf
, sizeof(buf
), refname
,
477 old_hashstr
, new_hashstr
);
480 err
= got_pkt_writepkt(fd
, buf
, n
, chattygot
);
484 fprintf(stderr
, "%s: deleting %s %s\n",
485 getprogname(), refname
, old_hashstr
);
490 RB_FOREACH(pe
, got_pathlist_head
, refs
) {
491 const char *refname
= pe
->path
;
492 struct got_object_id
*id
= pe
->data
;
493 struct got_object_id
*their_id
= NULL
;
494 struct got_pathlist_entry
*their_pe
;
496 RB_FOREACH(their_pe
, got_pathlist_head
, &their_refs
) {
497 const char *their_refname
= their_pe
->path
;
498 if (got_path_cmp(refname
, their_refname
,
499 strlen(refname
), strlen(their_refname
)) == 0) {
500 their_id
= their_pe
->data
;
505 if (got_object_id_cmp(id
, their_id
) == 0) {
508 "%s: no change for %s\n",
509 getprogname(), refname
);
513 got_object_id_hex(their_id
, old_hashstr
,
514 sizeof(old_hashstr
));
516 got_sha1_digest_to_str(zero_id
, old_hashstr
,
517 sizeof(old_hashstr
));
519 got_object_id_hex(id
, new_hashstr
, sizeof(new_hashstr
));
520 err
= describe_refchange(&n
, &sent_my_capabilites
,
521 my_capabilities
, buf
, sizeof(buf
), refname
,
522 old_hashstr
, new_hashstr
);
525 err
= got_pkt_writepkt(fd
, buf
, n
, chattygot
);
530 fprintf(stderr
, "%s: updating %s %s -> %s\n",
531 getprogname(), refname
, old_hashstr
,
534 fprintf(stderr
, "%s: creating %s %s\n",
535 getprogname(), refname
, new_hashstr
);
540 err
= got_pkt_flushpkt(fd
, chattygot
);
544 err
= send_pack_request(ibuf
);
548 err
= recv_packfd(&packfd
, ibuf
);
553 err
= send_pack_file(fd
, packfd
, ibuf
);
558 err
= got_pkt_readpkt(&n
, fd
, buf
, sizeof(buf
), chattygot
, INFTIM
);
561 if (n
>= 4 && strncmp(buf
, "ERR ", 4) == 0) {
562 err
= send_error(&buf
[4], n
- 4);
564 } else if (n
< 10 || strncmp(buf
, "unpack ok\n", 10) != 0) {
565 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
566 "unexpected message from server");
571 err
= got_pkt_readpkt(&n
, fd
, buf
, sizeof(buf
), chattygot
,
576 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
577 "unexpected message from server");
579 } else if (n
>= 4 && strncmp(buf
, "ERR ", 4) == 0) {
580 err
= send_error(&buf
[4], n
- 4);
582 } else if (strncmp(buf
, "ok ", 3) == 0) {
583 err
= send_ref_status(ibuf
, buf
+ 3, 1,
587 } else if (strncmp(buf
, "ng ", 3) == 0) {
588 err
= send_ref_status(ibuf
, buf
+ 3, 0,
593 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
594 "unexpected message from server");
600 err
= send_done(ibuf
);
602 got_pathlist_free(&their_refs
, GOT_PATHLIST_FREE_ALL
);
606 free(server_capabilities
);
611 main(int argc
, char **argv
)
613 const struct got_error
*err
= NULL
;
617 struct got_pathlist_head refs
;
618 struct got_pathlist_head delete_refs
;
619 struct got_imsg_send_request send_req
;
620 struct got_imsg_send_ref href
;
629 RB_INIT(&delete_refs
);
631 if (imsgbuf_init(&ibuf
, GOT_IMSG_FD_CHILD
) == -1) {
632 warn("imsgbuf_init");
635 imsgbuf_allow_fdpass(&ibuf
);
637 /* revoke access to most system calls */
638 if (pledge("stdio recvfd", NULL
) == -1) {
639 err
= got_error_from_errno("pledge");
640 got_privsep_send_error(&ibuf
, err
);
644 /* revoke fs access */
645 if (landlock_no_fs() == -1) {
646 err
= got_error_from_errno("landlock_no_fs");
647 got_privsep_send_error(&ibuf
, err
);
650 if (cap_enter() == -1) {
651 err
= got_error_from_errno("cap_enter");
652 got_privsep_send_error(&ibuf
, err
);
656 if ((err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0)) != 0) {
657 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
661 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
663 if (imsg
.hdr
.type
!= GOT_IMSG_SEND_REQUEST
) {
664 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
667 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
668 if (datalen
< sizeof(send_req
)) {
669 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
672 memcpy(&send_req
, imsg
.data
, sizeof(send_req
));
673 sendfd
= imsg_get_fd(&imsg
);
676 if (send_req
.verbosity
> 0)
677 chattygot
+= send_req
.verbosity
;
679 for (i
= 0; i
< send_req
.nrefs
; i
++) {
680 struct got_object_id
*id
;
682 struct got_pathlist_entry
*new;
684 if ((err
= got_privsep_recv_imsg(&imsg
, &ibuf
, 0)) != 0) {
685 if (err
->code
== GOT_ERR_PRIVSEP_PIPE
)
689 if (imsg
.hdr
.type
== GOT_IMSG_STOP
)
691 if (imsg
.hdr
.type
!= GOT_IMSG_SEND_REF
) {
692 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
695 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
696 if (datalen
< sizeof(href
)) {
697 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
700 memcpy(&href
, imsg
.data
, sizeof(href
));
701 if (datalen
- sizeof(href
) < href
.name_len
) {
702 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
705 refname
= malloc(href
.name_len
+ 1);
706 if (refname
== NULL
) {
707 err
= got_error_from_errno("malloc");
710 memcpy(refname
, imsg
.data
+ sizeof(href
), href
.name_len
);
711 refname
[href
.name_len
] = '\0';
714 * Prevent sending of references that won't make any
715 * sense outside the local repository's context.
717 if (strncmp(refname
, "refs/got/", 9) == 0 ||
718 strncmp(refname
, "refs/remotes/", 13) == 0) {
719 err
= got_error_fmt(GOT_ERR_SEND_BAD_REF
,
724 id
= malloc(sizeof(*id
));
727 err
= got_error_from_errno("malloc");
730 memcpy(id
, &href
.id
, sizeof(*id
));
732 err
= got_pathlist_insert(&new, &delete_refs
, refname
, id
);
734 err
= got_pathlist_insert(&new, &refs
, refname
, id
);
735 if (err
|| new == NULL
) {
745 err
= send_pack(sendfd
, &refs
, &delete_refs
, &ibuf
);
747 got_pathlist_free(&refs
, GOT_PATHLIST_FREE_ALL
);
748 got_pathlist_free(&delete_refs
, GOT_PATHLIST_FREE_ALL
);
749 if (sendfd
!= -1 && close(sendfd
) == -1 && err
== NULL
)
750 err
= got_error_from_errno("close");
751 if (err
!= NULL
&& err
->code
!= GOT_ERR_CANCELLED
) {
752 fprintf(stderr
, "%s: %s\n", getprogname(), err
->msg
);
753 got_privsep_send_error(&ibuf
, err
);