2 * Copyright (c) 2022 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <sys/types.h>
18 #include <sys/queue.h>
33 #include "got_error.h"
34 #include "got_serve.h"
36 #include "got_version.h"
37 #include "got_reference.h"
39 #include "got_lib_pkt.h"
40 #include "got_lib_dial.h"
41 #include "got_lib_gitproto.h"
42 #include "got_lib_sha1.h"
43 #include "got_lib_poll.h"
48 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
51 static const struct got_capability read_capabilities
[] = {
52 { GOT_CAPA_AGENT
, "got/" GOT_VERSION_STR
},
53 { GOT_CAPA_OFS_DELTA
, NULL
},
54 { GOT_CAPA_SIDE_BAND_64K
, NULL
},
57 static const struct got_capability write_capabilities
[] = {
58 { GOT_CAPA_AGENT
, "got/" GOT_VERSION_STR
},
59 { GOT_CAPA_OFS_DELTA
, NULL
},
60 { GOT_CAPA_REPORT_STATUS
, NULL
},
61 { GOT_CAPA_NO_THIN
, NULL
},
63 { GOT_CAPA_DELETE_REFS
, NULL
},
67 static const struct got_error
*
68 parse_command(char **command
, char **repo_path
, const char *gitcmd
)
70 const struct got_error
*err
= NULL
;
71 size_t len
, cmdlen
, pathlen
;
72 char *path0
= NULL
, *path
, *abspath
= NULL
, *canonpath
= NULL
;
80 if (len
>= strlen(GOT_SERVE_CMD_SEND
) &&
81 strncmp(gitcmd
, GOT_SERVE_CMD_SEND
,
82 strlen(GOT_SERVE_CMD_SEND
)) == 0)
83 cmdlen
= strlen(GOT_SERVE_CMD_SEND
);
84 else if (len
>= strlen(GOT_SERVE_CMD_FETCH
) &&
85 strncmp(gitcmd
, GOT_SERVE_CMD_FETCH
,
86 strlen(GOT_SERVE_CMD_FETCH
)) == 0)
87 cmdlen
= strlen(GOT_SERVE_CMD_FETCH
);
89 return got_error(GOT_ERR_BAD_PACKET
);
91 if (len
<= cmdlen
+ 1 || gitcmd
[cmdlen
] != ' ')
92 return got_error(GOT_ERR_BAD_PACKET
);
94 if (memchr(&gitcmd
[cmdlen
+ 1], '\0', len
- cmdlen
) == NULL
)
95 return got_error(GOT_ERR_BAD_PATH
);
97 /* Forbid linefeeds in paths, like Git does. */
98 if (memchr(&gitcmd
[cmdlen
+ 1], '\n', len
- cmdlen
) != NULL
)
99 return got_error(GOT_ERR_BAD_PATH
);
101 path0
= strdup(&gitcmd
[cmdlen
+ 1]);
103 return got_error_from_errno("strdup");
105 pathlen
= strlen(path
);
108 * Git clients send a shell command.
109 * Trim spaces and quotes around the path.
111 while (path
[0] == '\'' || path
[0] == '\"' || path
[0] == ' ') {
115 while (pathlen
> 0 &&
116 (path
[pathlen
- 1] == '\'' || path
[pathlen
- 1] == '\"' ||
117 path
[pathlen
- 1] == ' ')) {
118 path
[pathlen
- 1] = '\0';
122 /* Deny an empty repository path. */
123 if (path
[0] == '\0' || got_path_is_root_dir(path
)) {
124 err
= got_error(GOT_ERR_NOT_GIT_REPO
);
128 if (asprintf(&abspath
, "/%s", path
) == -1) {
129 err
= got_error_from_errno("asprintf");
132 pathlen
= strlen(abspath
);
133 canonpath
= malloc(pathlen
);
134 if (canonpath
== NULL
) {
135 err
= got_error_from_errno("malloc");
138 err
= got_canonpath(abspath
, canonpath
, pathlen
);
143 while (relpath
[0] == '/')
145 *repo_path
= strdup(relpath
);
146 if (*repo_path
== NULL
) {
147 err
= got_error_from_errno("strdup");
150 *command
= strndup(gitcmd
, cmdlen
);
151 if (*command
== NULL
)
152 err
= got_error_from_errno("strndup");
164 static const struct got_error
*
165 append_read_capabilities(size_t *capalen
, size_t len
, const char *symrefstr
,
166 uint8_t *buf
, size_t bufsize
)
168 struct got_capability capa
[nitems(read_capabilities
) + 1];
171 memcpy(&capa
, read_capabilities
, sizeof(read_capabilities
));
173 capa
[nitems(read_capabilities
)].key
= "symref";
174 capa
[nitems(read_capabilities
)].value
= symrefstr
;
175 ncapa
= nitems(capa
);
177 ncapa
= nitems(read_capabilities
);
179 return got_gitproto_append_capabilities(capalen
, buf
, len
,
180 bufsize
, capa
, ncapa
);
183 static const struct got_error
*
184 send_ref(int outfd
, uint8_t *id
, const char *refname
, int send_capabilities
,
185 int client_is_reading
, const char *symrefstr
, int chattygot
)
187 const struct got_error
*err
= NULL
;
188 char hex
[SHA1_DIGEST_STRING_LENGTH
];
189 char buf
[GOT_PKT_MAX
];
190 size_t len
, capalen
= 0;
192 if (got_sha1_digest_to_str(id
, hex
, sizeof(hex
)) == NULL
)
193 return got_error(GOT_ERR_BAD_OBJ_ID
);
195 len
= snprintf(buf
, sizeof(buf
), "%s %s", hex
, refname
);
196 if (len
>= sizeof(buf
))
197 return got_error(GOT_ERR_NO_SPACE
);
199 if (send_capabilities
) {
200 if (client_is_reading
) {
201 err
= append_read_capabilities(&capalen
, len
,
202 symrefstr
, buf
, sizeof(buf
));
204 err
= got_gitproto_append_capabilities(&capalen
,
205 buf
, len
, sizeof(buf
), write_capabilities
,
206 nitems(write_capabilities
));
213 if (len
+ 1 >= sizeof(buf
))
214 return got_error(GOT_ERR_NO_SPACE
);
219 return got_pkt_writepkt(outfd
, buf
, len
, chattygot
);
222 static const struct got_error
*
223 send_zero_refs(int outfd
, int chattygot
)
225 const struct got_error
*err
= NULL
;
226 char buf
[GOT_PKT_MAX
];
227 uint8_t zero
[SHA1_DIGEST_LENGTH
];
228 char hex
[SHA1_DIGEST_STRING_LENGTH
];
229 size_t len
, capalen
= 0;
231 memset(&zero
, 0, sizeof(zero
));
233 if (got_sha1_digest_to_str(zero
, hex
, sizeof(hex
)) == NULL
)
234 return got_error(GOT_ERR_BAD_OBJ_ID
);
236 len
= snprintf(buf
, sizeof(buf
), "%s capabilities^{}", hex
);
237 if (len
>= sizeof(buf
))
238 return got_error(GOT_ERR_NO_SPACE
);
240 err
= got_gitproto_append_capabilities(&capalen
, buf
, len
,
241 sizeof(buf
), read_capabilities
, nitems(read_capabilities
));
245 return got_pkt_writepkt(outfd
, buf
, len
, chattygot
);
249 echo_error(const struct got_error
*err
, int outfd
, int chattygot
)
251 char buf
[4 + GOT_ERR_MAX_MSG_SIZE
];
255 * Echo the error to the client on a pkt-line.
256 * The client should then terminate its session.
258 buf
[0] = 'E'; buf
[1] = 'R'; buf
[2] = 'R'; buf
[3] = ' '; buf
[4] = '\0';
259 len
= strlcat(buf
, err
->msg
, sizeof(buf
));
260 err
= got_pkt_writepkt(outfd
, buf
, len
, chattygot
);
264 static const struct got_error
*
265 announce_refs(int outfd
, struct imsgbuf
*ibuf
, int client_is_reading
,
266 const char *repo_path
, int chattygot
)
268 const struct got_error
*err
= NULL
;
271 struct gotd_imsg_list_refs lsref
;
272 struct gotd_imsg_reflist ireflist
;
273 struct gotd_imsg_ref iref
;
274 struct gotd_imsg_symref isymref
;
276 int have_nrefs
= 0, sent_capabilities
= 0;
277 char *symrefname
= NULL
, *symreftarget
= NULL
, *symrefstr
= NULL
;
278 char *refname
= NULL
;
280 memset(&imsg
, 0, sizeof(imsg
));
281 memset(&lsref
, 0, sizeof(lsref
));
283 if (strlcpy(lsref
.repo_name
, repo_path
, sizeof(lsref
.repo_name
)) >=
284 sizeof(lsref
.repo_name
))
285 return got_error(GOT_ERR_NO_SPACE
);
286 lsref
.client_is_reading
= client_is_reading
;
288 if (imsg_compose(ibuf
, GOTD_IMSG_LIST_REFS
, 0, 0, -1,
289 &lsref
, sizeof(lsref
)) == -1)
290 return got_error_from_errno("imsg_compose LIST_REFS");
292 err
= gotd_imsg_flush(ibuf
);
296 while (!have_nrefs
|| nrefs
> 0) {
297 err
= gotd_imsg_poll_recv(&imsg
, ibuf
, 0);
300 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
301 switch (imsg
.hdr
.type
) {
302 case GOTD_IMSG_ERROR
:
303 err
= gotd_imsg_recv_error(NULL
, &imsg
);
305 case GOTD_IMSG_REFLIST
:
306 if (have_nrefs
|| nrefs
> 0) {
307 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
310 if (datalen
!= sizeof(ireflist
)) {
311 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
314 memcpy(&ireflist
, imsg
.data
, sizeof(ireflist
));
315 nrefs
= ireflist
.nrefs
;
318 err
= send_zero_refs(outfd
, chattygot
);
321 if (!have_nrefs
|| nrefs
== 0) {
322 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
325 if (datalen
< sizeof(iref
)) {
326 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
329 memcpy(&iref
, imsg
.data
, sizeof(iref
));
330 if (datalen
!= sizeof(iref
) + iref
.name_len
) {
331 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
334 refname
= malloc(iref
.name_len
+ 1);
335 if (refname
== NULL
) {
336 err
= got_error_from_errno("malloc");
339 memcpy(refname
, imsg
.data
+ sizeof(iref
),
341 refname
[iref
.name_len
] = '\0';
342 err
= send_ref(outfd
, iref
.id
, refname
,
343 !sent_capabilities
, client_is_reading
,
349 sent_capabilities
= 1;
353 case GOTD_IMSG_SYMREF
:
354 if (!have_nrefs
|| nrefs
== 0) {
355 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
358 if (datalen
< sizeof(isymref
)) {
359 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
362 memcpy(&isymref
, imsg
.data
, sizeof(isymref
));
363 if (datalen
!= sizeof(isymref
) + isymref
.name_len
+
364 isymref
.target_len
) {
365 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
370 * For now, we only announce one symbolic ref,
371 * as part of our capability advertisement.
373 if (sent_capabilities
|| symrefstr
!= NULL
||
374 symrefname
!= NULL
|| symreftarget
!= NULL
)
377 symrefname
= malloc(isymref
.name_len
+ 1);
378 if (symrefname
== NULL
) {
379 err
= got_error_from_errno("malloc");
382 memcpy(symrefname
, imsg
.data
+ sizeof(isymref
),
384 symrefname
[isymref
.name_len
] = '\0';
386 symreftarget
= malloc(isymref
.target_len
+ 1);
387 if (symreftarget
== NULL
) {
388 err
= got_error_from_errno("malloc");
392 imsg
.data
+ sizeof(isymref
) + isymref
.name_len
,
394 symreftarget
[isymref
.target_len
] = '\0';
396 if (asprintf(&symrefstr
, "%s:%s", symrefname
,
397 symreftarget
) == -1) {
398 err
= got_error_from_errno("asprintf");
401 err
= send_ref(outfd
, isymref
.target_id
, symrefname
,
402 !sent_capabilities
, client_is_reading
, symrefstr
,
408 sent_capabilities
= 1;
413 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
420 err
= got_pkt_flushpkt(outfd
, chattygot
);
430 static const struct got_error
*
431 parse_want_line(char **common_capabilities
, uint8_t *id
, char *buf
, size_t len
)
433 const struct got_error
*err
;
434 char *id_str
= NULL
, *client_capabilities
= NULL
;
436 err
= got_gitproto_parse_want_line(&id_str
,
437 &client_capabilities
, buf
, len
);
441 if (!got_parse_sha1_digest(id
, id_str
)) {
442 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
443 "want-line with bad object ID");
447 if (client_capabilities
) {
448 err
= got_gitproto_match_capabilities(common_capabilities
,
449 NULL
, client_capabilities
, read_capabilities
,
450 nitems(read_capabilities
));
456 free(client_capabilities
);
460 static const struct got_error
*
461 parse_have_line(uint8_t *id
, char *buf
, size_t len
)
463 const struct got_error
*err
;
466 err
= got_gitproto_parse_have_line(&id_str
, buf
, len
);
470 if (!got_parse_sha1_digest(id
, id_str
)) {
471 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
472 "have-line with bad object ID");
480 static const struct got_error
*
481 send_capability(struct got_capability
*capa
, struct imsgbuf
*ibuf
)
483 const struct got_error
*err
= NULL
;
484 struct gotd_imsg_capability icapa
;
488 memset(&icapa
, 0, sizeof(icapa
));
490 icapa
.key_len
= strlen(capa
->key
);
491 len
= sizeof(icapa
) + icapa
.key_len
;
493 icapa
.value_len
= strlen(capa
->value
);
494 len
+= icapa
.value_len
;
497 wbuf
= imsg_create(ibuf
, GOTD_IMSG_CAPABILITY
, 0, 0, len
);
499 err
= got_error_from_errno("imsg_create CAPABILITY");
503 if (imsg_add(wbuf
, &icapa
, sizeof(icapa
)) == -1)
504 return got_error_from_errno("imsg_add CAPABILITY");
505 if (imsg_add(wbuf
, capa
->key
, icapa
.key_len
) == -1)
506 return got_error_from_errno("imsg_add CAPABILITY");
508 if (imsg_add(wbuf
, capa
->value
, icapa
.value_len
) == -1)
509 return got_error_from_errno("imsg_add CAPABILITY");
513 imsg_close(ibuf
, wbuf
);
518 static const struct got_error
*
519 send_capabilities(int *use_sidebands
, int *report_status
,
520 char *capabilities_str
, struct imsgbuf
*ibuf
)
522 const struct got_error
*err
= NULL
;
523 struct gotd_imsg_capabilities icapas
;
524 struct got_capability
*capa
= NULL
;
527 err
= got_gitproto_split_capabilities_str(&capa
, &ncapa
,
532 icapas
.ncapabilities
= ncapa
;
533 if (imsg_compose(ibuf
, GOTD_IMSG_CAPABILITIES
, 0, 0, -1,
534 &icapas
, sizeof(icapas
)) == -1) {
535 err
= got_error_from_errno("imsg_compose IMSG_CAPABILITIES");
539 for (i
= 0; i
< ncapa
; i
++) {
540 err
= send_capability(&capa
[i
], ibuf
);
544 strcmp(capa
[i
].key
, GOT_CAPA_SIDE_BAND_64K
) == 0)
547 strcmp(capa
[i
].key
, GOT_CAPA_REPORT_STATUS
) == 0)
555 static const struct got_error
*
556 forward_flushpkt(struct imsgbuf
*ibuf
)
558 if (imsg_compose(ibuf
, GOTD_IMSG_FLUSH
, 0, 0, -1, NULL
, 0) == -1)
559 return got_error_from_errno("imsg_compose FLUSH");
561 return gotd_imsg_flush(ibuf
);
564 static const struct got_error
*
565 recv_ack(struct imsg
*imsg
, uint8_t *expected_id
)
567 struct gotd_imsg_ack iack
;
570 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
571 if (datalen
!= sizeof(iack
))
572 return got_error(GOT_ERR_PRIVSEP_LEN
);
574 memcpy(&iack
, imsg
->data
, sizeof(iack
));
575 if (memcmp(iack
.object_id
, expected_id
, SHA1_DIGEST_LENGTH
) != 0)
576 return got_error(GOT_ERR_BAD_OBJ_ID
);
581 static const struct got_error
*
582 recv_nak(struct imsg
*imsg
, uint8_t *expected_id
)
584 struct gotd_imsg_ack inak
;
587 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
588 if (datalen
!= sizeof(inak
))
589 return got_error(GOT_ERR_PRIVSEP_LEN
);
591 memcpy(&inak
, imsg
->data
, sizeof(inak
));
592 if (memcmp(inak
.object_id
, expected_id
, SHA1_DIGEST_LENGTH
) != 0)
593 return got_error(GOT_ERR_BAD_OBJ_ID
);
599 static const struct got_error
*
600 recv_want(int *use_sidebands
, int outfd
, struct imsgbuf
*ibuf
,
601 char *buf
, size_t len
, int expect_capabilities
, int chattygot
)
603 const struct got_error
*err
;
604 struct gotd_imsg_want iwant
;
605 char *capabilities_str
;
609 memset(&iwant
, 0, sizeof(iwant
));
610 memset(&imsg
, 0, sizeof(imsg
));
612 err
= parse_want_line(&capabilities_str
, iwant
.object_id
, buf
, len
);
616 if (capabilities_str
) {
617 if (!expect_capabilities
) {
618 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
619 "unexpected capability announcement received");
622 err
= send_capabilities(use_sidebands
, NULL
, capabilities_str
,
629 if (imsg_compose(ibuf
, GOTD_IMSG_WANT
, 0, 0, -1,
630 &iwant
, sizeof(iwant
)) == -1) {
631 err
= got_error_from_errno("imsg_compose WANT");
635 err
= gotd_imsg_flush(ibuf
);
640 * Wait for an ACK, or an error in case the desired object
643 while (!done
&& err
== NULL
) {
644 err
= gotd_imsg_poll_recv(&imsg
, ibuf
, 0);
647 switch (imsg
.hdr
.type
) {
648 case GOTD_IMSG_ERROR
:
649 err
= gotd_imsg_recv_error(NULL
, &imsg
);
652 err
= recv_ack(&imsg
, iwant
.object_id
);
658 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
665 free(capabilities_str
);
669 static const struct got_error
*
670 send_ack(int outfd
, uint8_t *id
, int chattygot
)
672 char hex
[SHA1_DIGEST_STRING_LENGTH
];
673 char buf
[GOT_PKT_MAX
];
676 if (got_sha1_digest_to_str(id
, hex
, sizeof(hex
)) == NULL
)
677 return got_error(GOT_ERR_BAD_OBJ_ID
);
679 len
= snprintf(buf
, sizeof(buf
), "ACK %s\n", hex
);
680 if (len
>= sizeof(buf
))
681 return got_error(GOT_ERR_NO_SPACE
);
683 return got_pkt_writepkt(outfd
, buf
, len
, chattygot
);
686 static const struct got_error
*
687 send_nak(int outfd
, int chattygot
)
692 len
= snprintf(buf
, sizeof(buf
), "NAK\n");
693 if (len
>= sizeof(buf
))
694 return got_error(GOT_ERR_NO_SPACE
);
696 return got_pkt_writepkt(outfd
, buf
, len
, chattygot
);
699 static const struct got_error
*
700 recv_have(int *have_ack
, int outfd
, struct imsgbuf
*ibuf
, char *buf
,
701 size_t len
, int chattygot
)
703 const struct got_error
*err
;
704 struct gotd_imsg_have ihave
;
708 memset(&ihave
, 0, sizeof(ihave
));
709 memset(&imsg
, 0, sizeof(imsg
));
711 err
= parse_have_line(ihave
.object_id
, buf
, len
);
715 if (imsg_compose(ibuf
, GOTD_IMSG_HAVE
, 0, 0, -1,
716 &ihave
, sizeof(ihave
)) == -1)
717 return got_error_from_errno("imsg_compose HAVE");
719 err
= gotd_imsg_flush(ibuf
);
724 * Wait for an ACK or a NAK, indicating whether a common
725 * commit object has been found.
727 while (!done
&& err
== NULL
) {
728 err
= gotd_imsg_poll_recv(&imsg
, ibuf
, 0);
731 switch (imsg
.hdr
.type
) {
732 case GOTD_IMSG_ERROR
:
733 err
= gotd_imsg_recv_error(NULL
, &imsg
);
736 err
= recv_ack(&imsg
, ihave
.object_id
);
740 err
= send_ack(outfd
, ihave
.object_id
,
749 err
= recv_nak(&imsg
, ihave
.object_id
);
755 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
765 static const struct got_error
*
766 recv_done(int *packfd
, int outfd
, struct imsgbuf
*ibuf
, int chattygot
)
768 const struct got_error
*err
;
773 if (imsg_compose(ibuf
, GOTD_IMSG_DONE
, 0, 0, -1, NULL
, 0) == -1)
774 return got_error_from_errno("imsg_compose DONE");
776 err
= gotd_imsg_flush(ibuf
);
780 while (*packfd
== -1 && err
== NULL
) {
781 err
= gotd_imsg_poll_recv(&imsg
, ibuf
, 0);
785 switch (imsg
.hdr
.type
) {
786 case GOTD_IMSG_ERROR
:
787 err
= gotd_imsg_recv_error(NULL
, &imsg
);
789 case GOTD_IMSG_PACKFILE_PIPE
:
793 err
= got_error(GOT_ERR_PRIVSEP_NO_FD
);
796 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
806 static const struct got_error
*
807 relay_progress_reports(struct imsgbuf
*ibuf
, int outfd
, int chattygot
)
809 const struct got_error
*err
= NULL
;
810 int pack_starting
= 0;
811 struct gotd_imsg_packfile_progress iprog
;
812 char buf
[GOT_PKT_MAX
];
815 int p_deltify
= 0, n
;
816 const char *eol
= "\r";
818 memset(&imsg
, 0, sizeof(imsg
));
820 while (!pack_starting
&& err
== NULL
) {
821 err
= gotd_imsg_poll_recv(&imsg
, ibuf
, 0);
825 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
826 switch (imsg
.hdr
.type
) {
827 case GOTD_IMSG_ERROR
:
828 err
= gotd_imsg_recv_error(NULL
, &imsg
);
830 case GOTD_IMSG_PACKFILE_READY
:
834 case GOTD_IMSG_PACKFILE_PROGRESS
:
835 if (datalen
!= sizeof(iprog
)) {
836 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
839 memcpy(&iprog
, imsg
.data
, sizeof(iprog
));
840 if (iprog
.nobj_total
> 0) {
841 p_deltify
= (iprog
.nobj_deltify
* 100) /
844 buf
[0] = GOT_SIDEBAND_PROGRESS_INFO
;
845 n
= snprintf(&buf
[1], sizeof(buf
) - 1,
846 "%d commits colored, "
852 if (n
>= sizeof(buf
) - 1)
854 err
= got_pkt_writepkt(outfd
, buf
, 1 + n
, chattygot
);
857 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
867 static const struct got_error
*
868 serve_read(int infd
, int outfd
, int gotd_sock
, const char *repo_path
,
871 const struct got_error
*err
= NULL
;
872 char buf
[GOT_PKT_MAX
];
876 STATE_EXPECT_MORE_WANT
,
881 enum protostate curstate
= STATE_EXPECT_WANT
;
882 int have_ack
= 0, use_sidebands
= 0, seen_have
= 0;
884 size_t pack_chunksize
;
886 imsg_init(&ibuf
, gotd_sock
);
888 err
= announce_refs(outfd
, &ibuf
, 1, repo_path
, chattygot
);
892 while (curstate
!= STATE_DONE
) {
895 err
= got_pkt_readpkt(&n
, infd
, buf
, sizeof(buf
), chattygot
);
899 if (curstate
!= STATE_EXPECT_MORE_WANT
&&
900 curstate
!= STATE_EXPECT_HAVE
) {
901 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
902 "unexpected flush packet received");
905 err
= forward_flushpkt(&ibuf
);
908 if (curstate
== STATE_EXPECT_HAVE
&& !have_ack
) {
909 err
= send_nak(outfd
, chattygot
);
913 if (curstate
== STATE_EXPECT_MORE_WANT
)
914 curstate
= STATE_EXPECT_HAVE
;
916 curstate
= STATE_EXPECT_DONE
;
917 } else if (n
>= 5 && strncmp(buf
, "want ", 5) == 0) {
918 if (curstate
!= STATE_EXPECT_WANT
&&
919 curstate
!= STATE_EXPECT_MORE_WANT
) {
920 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
921 "unexpected 'want' packet");
924 err
= recv_want(&use_sidebands
, outfd
, &ibuf
, buf
, n
,
925 curstate
== STATE_EXPECT_WANT
? 1 : 0, chattygot
);
928 if (curstate
== STATE_EXPECT_WANT
)
929 curstate
= STATE_EXPECT_MORE_WANT
;
930 } else if (n
>= 5 && strncmp(buf
, "have ", 5) == 0) {
931 if (curstate
!= STATE_EXPECT_HAVE
) {
932 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
933 "unexpected 'have' packet");
936 err
= recv_have(&have_ack
, outfd
, &ibuf
, buf
, n
,
941 } else if (n
== 5 && strncmp(buf
, "done\n", 5) == 0) {
942 if (curstate
!= STATE_EXPECT_HAVE
&&
943 curstate
!= STATE_EXPECT_DONE
) {
944 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
945 "unexpected 'done' packet");
948 err
= recv_done(&packfd
, outfd
, &ibuf
, chattygot
);
951 curstate
= STATE_DONE
;
954 err
= got_error(GOT_ERR_BAD_PACKET
);
960 err
= send_nak(outfd
, chattygot
);
966 err
= relay_progress_reports(&ibuf
, outfd
, chattygot
);
969 pack_chunksize
= GOT_SIDEBAND_64K_PACKFILE_DATA_MAX
;
971 pack_chunksize
= sizeof(buf
);
976 r
= read(packfd
, use_sidebands
? &buf
[1] : buf
,
979 err
= got_error_from_errno("read");
982 err
= got_pkt_flushpkt(outfd
, chattygot
);
987 buf
[0] = GOT_SIDEBAND_PACKFILE_DATA
;
988 err
= got_pkt_writepkt(outfd
, buf
, 1 + r
, chattygot
);
992 err
= got_poll_write_full(outfd
, buf
, r
);
994 if (err
->code
== GOT_ERR_EOF
)
1002 if (packfd
!= -1 && close(packfd
) == -1 && err
== NULL
)
1003 err
= got_error_from_errno("close");
1005 echo_error(err
, outfd
, chattygot
);
1009 static const struct got_error
*
1010 parse_ref_update_line(char **common_capabilities
, char **refname
,
1011 uint8_t *old_id
, uint8_t *new_id
, char *buf
, size_t len
)
1013 const struct got_error
*err
;
1014 char *old_id_str
= NULL
, *new_id_str
= NULL
;
1015 char *client_capabilities
= NULL
;
1019 err
= got_gitproto_parse_ref_update_line(&old_id_str
, &new_id_str
,
1020 refname
, &client_capabilities
, buf
, len
);
1024 if (!got_parse_sha1_digest(old_id
, old_id_str
) ||
1025 !got_parse_sha1_digest(new_id
, new_id_str
)) {
1026 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
1027 "ref-update with bad object ID");
1030 if (!got_ref_name_is_valid(*refname
)) {
1031 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
1032 "ref-update with bad reference name");
1036 if (client_capabilities
) {
1037 err
= got_gitproto_match_capabilities(common_capabilities
,
1038 NULL
, client_capabilities
, write_capabilities
,
1039 nitems(write_capabilities
));
1046 free(client_capabilities
);
1054 static const struct got_error
*
1055 recv_ref_update(int *report_status
, int outfd
, struct imsgbuf
*ibuf
,
1056 char *buf
, size_t len
, int expect_capabilities
, int chattygot
)
1058 const struct got_error
*err
;
1059 struct gotd_imsg_ref_update iref
;
1061 char *capabilities_str
= NULL
, *refname
= NULL
;
1065 memset(&iref
, 0, sizeof(iref
));
1066 memset(&imsg
, 0, sizeof(imsg
));
1068 err
= parse_ref_update_line(&capabilities_str
, &refname
,
1069 iref
.old_id
, iref
.new_id
, buf
, len
);
1073 if (capabilities_str
) {
1074 if (!expect_capabilities
) {
1075 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
1076 "unexpected capability announcement received");
1079 err
= send_capabilities(NULL
, report_status
, capabilities_str
,
1085 iref
.name_len
= strlen(refname
);
1086 len
= sizeof(iref
) + iref
.name_len
;
1087 wbuf
= imsg_create(ibuf
, GOTD_IMSG_REF_UPDATE
, 0, 0, len
);
1089 err
= got_error_from_errno("imsg_create REF_UPDATE");
1093 if (imsg_add(wbuf
, &iref
, sizeof(iref
)) == -1)
1094 return got_error_from_errno("imsg_add REF_UPDATE");
1095 if (imsg_add(wbuf
, refname
, iref
.name_len
) == -1)
1096 return got_error_from_errno("imsg_add REF_UPDATE");
1098 imsg_close(ibuf
, wbuf
);
1100 err
= gotd_imsg_flush(ibuf
);
1104 /* Wait for ACK or an error. */
1105 while (!done
&& err
== NULL
) {
1106 err
= gotd_imsg_poll_recv(&imsg
, ibuf
, 0);
1109 switch (imsg
.hdr
.type
) {
1110 case GOTD_IMSG_ERROR
:
1111 err
= gotd_imsg_recv_error(NULL
, &imsg
);
1114 err
= recv_ack(&imsg
, iref
.new_id
);
1120 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
1127 free(capabilities_str
);
1132 static const struct got_error
*
1133 recv_packfile(struct imsg
*imsg
, int infd
)
1135 const struct got_error
*err
= NULL
;
1138 char buf
[GOT_PKT_MAX
];
1141 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1143 return got_error(GOT_ERR_PRIVSEP_MSG
);
1146 return got_error(GOT_ERR_PRIVSEP_NO_FD
);
1149 while (!pack_done
) {
1152 err
= got_poll_fd(infd
, POLLIN
, 1);
1154 if (err
->code
!= GOT_ERR_TIMEOUT
)
1158 r
= read(infd
, buf
, sizeof(buf
));
1160 err
= got_error_from_errno("read");
1165 * Git clients hang up their side of the
1166 * connection after sending the pack file.
1175 /* Detect gotd(8) closing the pack pipe when done. */
1176 err
= got_poll_fd(packfd
, POLLOUT
, 1);
1178 if (err
->code
!= GOT_ERR_EOF
)
1184 /* Write pack data and/or detect pipe being closed. */
1185 err
= got_poll_write_full(packfd
, buf
, r
);
1187 if (err
->code
== GOT_ERR_EOF
)
1198 static const struct got_error
*
1199 report_unpack_status(struct imsg
*imsg
, int outfd
, int chattygot
)
1201 const struct got_error
*err
= NULL
;
1202 struct gotd_imsg_packfile_status istatus
;
1203 char buf
[GOT_PKT_MAX
];
1204 size_t datalen
, len
;
1205 char *reason
= NULL
;
1207 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1208 if (datalen
< sizeof(istatus
))
1209 return got_error(GOT_ERR_PRIVSEP_LEN
);
1210 memcpy(&istatus
, imsg
->data
, sizeof(istatus
));
1211 if (datalen
!= sizeof(istatus
) + istatus
.reason_len
)
1212 return got_error(GOT_ERR_PRIVSEP_LEN
);
1214 reason
= malloc(istatus
.reason_len
+ 1);
1215 if (reason
== NULL
) {
1216 err
= got_error_from_errno("malloc");
1219 memcpy(reason
, imsg
->data
+ sizeof(istatus
), istatus
.reason_len
);
1220 reason
[istatus
.reason_len
] = '\0';
1223 len
= snprintf(buf
, sizeof(buf
), "unpack ok\n");
1225 len
= snprintf(buf
, sizeof(buf
), "unpack %s\n", reason
);
1226 if (len
>= sizeof(buf
)) {
1227 err
= got_error(GOT_ERR_NO_SPACE
);
1231 err
= got_pkt_writepkt(outfd
, buf
, len
, chattygot
);
1237 static const struct got_error
*
1238 recv_ref_update_ok(struct imsg
*imsg
, int outfd
, int chattygot
)
1240 const struct got_error
*err
= NULL
;
1241 struct gotd_imsg_ref_update_ok iok
;
1242 size_t datalen
, len
;
1243 char buf
[GOT_PKT_MAX
];
1244 char *refname
= NULL
;
1246 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1247 if (datalen
< sizeof(iok
))
1248 return got_error(GOT_ERR_PRIVSEP_LEN
);
1249 memcpy(&iok
, imsg
->data
, sizeof(iok
));
1250 if (datalen
!= sizeof(iok
) + iok
.name_len
)
1251 return got_error(GOT_ERR_PRIVSEP_LEN
);
1253 memcpy(&iok
, imsg
->data
, sizeof(iok
));
1255 refname
= malloc(iok
.name_len
+ 1);
1256 if (refname
== NULL
)
1257 return got_error_from_errno("malloc");
1258 memcpy(refname
, imsg
->data
+ sizeof(iok
), iok
.name_len
);
1259 refname
[iok
.name_len
] = '\0';
1261 len
= snprintf(buf
, sizeof(buf
), "ok %s\n", refname
);
1262 if (len
>= sizeof(buf
)) {
1263 err
= got_error(GOT_ERR_NO_SPACE
);
1267 err
= got_pkt_writepkt(outfd
, buf
, len
, chattygot
);
1273 static const struct got_error
*
1274 recv_ref_update_ng(struct imsg
*imsg
, int outfd
, int chattygot
)
1276 const struct got_error
*err
= NULL
;
1277 struct gotd_imsg_ref_update_ng ing
;
1278 size_t datalen
, len
;
1279 char buf
[GOT_PKT_MAX
];
1280 char *refname
= NULL
, *reason
= NULL
;
1282 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
1283 if (datalen
< sizeof(ing
))
1284 return got_error(GOT_ERR_PRIVSEP_LEN
);
1285 memcpy(&ing
, imsg
->data
, sizeof(ing
));
1286 if (datalen
!= sizeof(ing
) + ing
.name_len
+ ing
.reason_len
)
1287 return got_error(GOT_ERR_PRIVSEP_LEN
);
1289 memcpy(&ing
, imsg
->data
, sizeof(ing
));
1291 refname
= malloc(ing
.name_len
+ 1);
1292 if (refname
== NULL
)
1293 return got_error_from_errno("malloc");
1294 memcpy(refname
, imsg
->data
+ sizeof(ing
), ing
.name_len
);
1295 refname
[ing
.name_len
] = '\0';
1297 reason
= malloc(ing
.reason_len
+ 1);
1298 if (reason
== NULL
) {
1299 err
= got_error_from_errno("malloc");
1302 memcpy(refname
, imsg
->data
+ sizeof(ing
) + ing
.name_len
,
1304 refname
[ing
.reason_len
] = '\0';
1306 len
= snprintf(buf
, sizeof(buf
), "ng %s %s\n", refname
, reason
);
1307 if (len
>= sizeof(buf
)) {
1308 err
= got_error(GOT_ERR_NO_SPACE
);
1312 err
= got_pkt_writepkt(outfd
, buf
, len
, chattygot
);
1319 static const struct got_error
*
1320 serve_write(int infd
, int outfd
, int gotd_sock
, const char *repo_path
,
1323 const struct got_error
*err
= NULL
;
1324 char buf
[GOT_PKT_MAX
];
1325 struct imsgbuf ibuf
;
1327 STATE_EXPECT_REF_UPDATE
,
1328 STATE_EXPECT_MORE_REF_UPDATES
,
1329 STATE_EXPECT_PACKFILE
,
1330 STATE_PACKFILE_RECEIVED
,
1333 enum protostate curstate
= STATE_EXPECT_REF_UPDATE
;
1335 int report_status
= 0;
1337 imsg_init(&ibuf
, gotd_sock
);
1338 memset(&imsg
, 0, sizeof(imsg
));
1340 err
= announce_refs(outfd
, &ibuf
, 0, repo_path
, chattygot
);
1344 while (curstate
!= STATE_EXPECT_PACKFILE
) {
1347 err
= got_pkt_readpkt(&n
, infd
, buf
, sizeof(buf
), chattygot
);
1351 if (curstate
!= STATE_EXPECT_MORE_REF_UPDATES
) {
1352 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
1353 "unexpected flush packet received");
1356 err
= forward_flushpkt(&ibuf
);
1359 curstate
= STATE_EXPECT_PACKFILE
;
1360 } else if (n
>= (SHA1_DIGEST_STRING_LENGTH
* 2) + 2) {
1361 if (curstate
!= STATE_EXPECT_REF_UPDATE
&&
1362 curstate
!= STATE_EXPECT_MORE_REF_UPDATES
) {
1363 err
= got_error_msg(GOT_ERR_BAD_PACKET
,
1364 "unexpected ref-update packet");
1367 if (curstate
== STATE_EXPECT_REF_UPDATE
) {
1368 err
= recv_ref_update(&report_status
,
1369 outfd
, &ibuf
, buf
, n
, 1, chattygot
);
1371 err
= recv_ref_update(NULL
, outfd
, &ibuf
,
1372 buf
, n
, 0, chattygot
);
1376 curstate
= STATE_EXPECT_MORE_REF_UPDATES
;
1378 err
= got_error(GOT_ERR_BAD_PACKET
);
1383 while (curstate
!= STATE_PACKFILE_RECEIVED
) {
1384 err
= gotd_imsg_poll_recv(&imsg
, &ibuf
, 0);
1387 switch (imsg
.hdr
.type
) {
1388 case GOTD_IMSG_ERROR
:
1389 err
= gotd_imsg_recv_error(NULL
, &imsg
);
1391 case GOTD_IMSG_RECV_PACKFILE
:
1392 err
= recv_packfile(&imsg
, infd
);
1394 if (err
->code
!= GOT_ERR_EOF
)
1397 * EOF is reported when the client hangs up,
1398 * which can happen with Git clients.
1399 * The socket should stay half-open so we
1400 * can still send our reports if requested.
1404 curstate
= STATE_PACKFILE_RECEIVED
;
1407 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
1416 while (curstate
!= STATE_REFS_UPDATED
&& err
== NULL
) {
1417 err
= gotd_imsg_poll_recv(&imsg
, &ibuf
, 0);
1420 switch (imsg
.hdr
.type
) {
1421 case GOTD_IMSG_ERROR
:
1422 err
= gotd_imsg_recv_error(NULL
, &imsg
);
1424 case GOTD_IMSG_PACKFILE_STATUS
:
1427 err
= report_unpack_status(&imsg
, outfd
, chattygot
);
1429 case GOTD_IMSG_REF_UPDATE_OK
:
1432 err
= recv_ref_update_ok(&imsg
, outfd
, chattygot
);
1434 case GOTD_IMSG_REF_UPDATE_NG
:
1437 err
= recv_ref_update_ng(&imsg
, outfd
, chattygot
);
1439 case GOTD_IMSG_REFS_UPDATED
:
1440 curstate
= STATE_REFS_UPDATED
;
1441 err
= got_pkt_flushpkt(outfd
, chattygot
);
1444 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
1453 echo_error(err
, outfd
, chattygot
);
1457 const struct got_error
*
1458 got_serve(int infd
, int outfd
, const char *gitcmd
, int gotd_sock
, int chattygot
)
1460 const struct got_error
*err
= NULL
;
1461 char *command
= NULL
, *repo_path
= NULL
;
1463 err
= parse_command(&command
, &repo_path
, gitcmd
);
1467 if (strcmp(command
, GOT_SERVE_CMD_FETCH
) == 0)
1468 err
= serve_read(infd
, outfd
, gotd_sock
, repo_path
, chattygot
);
1469 else if (strcmp(command
, GOT_SERVE_CMD_SEND
) == 0)
1470 err
= serve_write(infd
, outfd
, gotd_sock
, repo_path
, chattygot
);
1472 err
= got_error(GOT_ERR_BAD_PACKET
);