commit 3fe5d0fee4a9e03753cf693bf839bf26cb872d54 from: Omar Polo via: Thomas Adam date: Thu Nov 17 21:38:13 2022 UTC got send: show server error Print the error message reported by the remote server when failing to update a branch (for e.g. because of a server-side check.) Reported by gonzalo@, with help and ok stsp@. commit - 2dd71cdd72c0c6c4ce607fdc73ae38fa898bfeb2 commit + 3fe5d0fee4a9e03753cf693bf839bf26cb872d54 blob - 8f5652bc11cd7acbd00b1be84dd5bb45e953f855 blob + dad0b7d03fbf6b57cc05881f0936f42db930e358 --- got/got.c +++ got/got.c @@ -8763,7 +8763,8 @@ struct got_send_progress_arg { static const struct got_error * send_progress(void *arg, int ncolored, int nfound, int ntrees, off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify, - int nobj_written, off_t bytes_sent, const char *refname, int success) + int nobj_written, off_t bytes_sent, const char *refname, + const char *errmsg, int success) { struct got_send_progress_arg *a = arg; char scaled_packsize[FMT_SCALED_STRSIZE]; @@ -8795,6 +8796,8 @@ send_progress(void *arg, int ncolored, int nfound, int if (a->printed_something) putchar('\n'); printf("Server has %s %s", status, refname); + if (errmsg) + printf(": %s", errmsg); a->printed_something = 1; return NULL; } blob - 19e9e4f116e70c354ec4bbff8ce7e5a1615acd1f blob + 038b51f30308b8ca6879fdbddcfd64c6c83b7594 --- include/got_send.h +++ include/got_send.h @@ -38,7 +38,7 @@ const struct got_error *got_send_connect(pid_t *, int typedef const struct got_error *(*got_send_progress_cb)(void *, int ncolored, int nfound, int ntrees, off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify, int nobj_written, off_t bytes_sent, - const char *refname, int success); + const char *refname, const char *, int success); /* * Attempt to generate a pack file and sent it to a server. blob - c9234999b21b43c91b7595360c597b1d528f69c1 blob + 44f8d7667b1d387ae50d548b69f48ae1afba27e9 --- lib/got_lib_privsep.h +++ lib/got_lib_privsep.h @@ -481,7 +481,9 @@ struct got_imsg_send_remote_ref { struct got_imsg_send_ref_status { int success; size_t name_len; + size_t errmsg_len; /* Followed by name_len data bytes. */ + /* Followed by errmsg_len data bytes. */ } __attribute__((__packed__)); /* Structure for GOT_IMSG_IDXPACK_REQUEST data. */ @@ -702,7 +704,7 @@ const struct got_error *got_privsep_recv_send_remote_r struct got_pathlist_head *, struct imsgbuf *); const struct got_error *got_privsep_send_packfd(struct imsgbuf *, int); const struct got_error *got_privsep_recv_send_progress(int *, off_t *, - int *, char **, struct imsgbuf *); + int *, char **, char **, struct imsgbuf *); const struct got_error *got_privsep_get_imsg_obj(struct got_object **, struct imsg *, struct imsgbuf *); const struct got_error *got_privsep_recv_obj(struct got_object **, blob - 097787082caaa1182b3a16d565c2bdee8e33d6de blob + 5462fb412f766dfe4181df6346d61dc9b5a584fb --- lib/privsep.c +++ lib/privsep.c @@ -964,7 +964,7 @@ got_privsep_send_packfd(struct imsgbuf *ibuf, int fd) const struct got_error * got_privsep_recv_send_progress(int *done, off_t *bytes_sent, - int *success, char **refname, struct imsgbuf *ibuf) + int *success, char **refname, char **errmsg, struct imsgbuf *ibuf) { const struct got_error *err = NULL; struct imsg imsg; @@ -975,6 +975,7 @@ got_privsep_recv_send_progress(int *done, off_t *bytes *done = 0; *success = 0; *refname = NULL; + *errmsg = NULL; err = got_privsep_recv_imsg(&imsg, ibuf, 0); if (err) @@ -995,13 +996,18 @@ got_privsep_recv_send_progress(int *done, off_t *bytes break; } memcpy(&iref_status, imsg.data, sizeof(iref_status)); - if (datalen != sizeof(iref_status) + iref_status.name_len) { + if (datalen != sizeof(iref_status) + iref_status.name_len + + iref_status.errmsg_len) { err = got_error(GOT_ERR_PRIVSEP_MSG); break; } *success = iref_status.success; *refname = strndup(imsg.data + sizeof(iref_status), iref_status.name_len); + + if (iref_status.errmsg_len != 0) + *errmsg = strndup(imsg.data + sizeof(iref_status) + + iref_status.name_len, iref_status.errmsg_len); break; case GOT_IMSG_SEND_DONE: if (datalen != 0) { blob - e2c9a83e2c62b08256e2396c00578d4f47c9aa35 blob + edb3e20183d33860e39b8ec07038fcae745ceb0f --- lib/send.c +++ lib/send.c @@ -121,7 +121,7 @@ pack_progress(void *arg, int ncolored, int nfound, int err = a->progress_cb(a->progress_arg, ncolored, nfound, ntrees, packfile_size, ncommits, nobj_total, nobj_deltify, - nobj_written, 0, NULL, 0); + nobj_written, 0, NULL, NULL, 0); if (err) return err; @@ -665,13 +665,15 @@ got_send_pack(const char *remote_name, struct got_path while (!done) { int success = 0; char *refname = NULL; + char *errmsg = NULL; + if (cancel_cb) { err = (*cancel_cb)(cancel_arg); if (err) goto done; } err = got_privsep_recv_send_progress(&done, &bytes_sent, - &success, &refname, &sendibuf); + &success, &refname, &errmsg, &sendibuf); if (err) goto done; if (refname && got_ref_name_is_valid(refname) && success && @@ -695,14 +697,16 @@ got_send_pack(const char *remote_name, struct got_path ppa.nfound, ppa.ntrees, ppa.packfile_size, ppa.ncommits, ppa.nobj_total, ppa.nobj_deltify, ppa.nobj_written, bytes_sent, - refname, success); + refname, errmsg, success); if (err) { free(refname); + free(errmsg); goto done; } bytes_sent_cur = bytes_sent; } free(refname); + free(errmsg); } done: if (sendpid != -1) { blob - 885867f78e6977f47b4133b83b2d41c0603514ef blob + 40bd3e57d66c071b282b6f8d8bd9fec8fa887d49 --- libexec/got-send-pack/got-send-pack.c +++ libexec/got-send-pack/got-send-pack.c @@ -221,10 +221,11 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn struct got_pathlist_head *refs, struct got_pathlist_head *delete_refs) { struct ibuf *wbuf; - size_t len, reflen = strlen(refname); + size_t i, len, reflen, errmsglen = 0; struct got_pathlist_entry *pe; int ref_valid = 0; - char *eol; + char *eol, *sp; + const char *errmsg = ""; eol = strchr(refname, '\n'); if (eol == NULL) { @@ -232,7 +233,28 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn "unexpected message from server"); } *eol = '\0'; + + sp = strchr(refname, ' '); + if (sp != NULL) { + *sp++ = '\0'; + errmsg = sp; + errmsglen = strlen(errmsg); + + for (i = 0; i < errmsglen; ++i) { + if (!isprint((unsigned char)errmsg[i])) { + return got_error_msg(GOT_ERR_BAD_PACKET, + "non-printable error message received " + "from the server"); + } + } + } + reflen = strlen(refname); + if (!got_ref_name_is_valid(refname)) { + return got_error_msg(GOT_ERR_BAD_PACKET, + "unexpected message from server"); + } + TAILQ_FOREACH(pe, refs, entry) { if (strcmp(refname, pe->path) == 0) { ref_valid = 1; @@ -252,7 +274,7 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn "unexpected message from server"); } - len = sizeof(struct got_imsg_send_ref_status) + reflen; + len = sizeof(struct got_imsg_send_ref_status) + reflen + errmsglen; if (len >= MAX_IMSGSIZE - IMSG_HEADER_SIZE) return got_error(GOT_ERR_NO_SPACE); @@ -266,8 +288,12 @@ send_ref_status(struct imsgbuf *ibuf, const char *refn return got_error_from_errno("imsg_add SEND_REF_STATUS"); if (imsg_add(wbuf, &reflen, sizeof(reflen)) == -1) return got_error_from_errno("imsg_add SEND_REF_STATUS"); + if (imsg_add(wbuf, &errmsglen, sizeof(errmsglen)) == -1) + return got_error_from_errno("imsg_add SEND_REF_STATUS"); if (imsg_add(wbuf, refname, reflen) == -1) return got_error_from_errno("imsg_add SEND_REF_STATUS"); + if (imsg_add(wbuf, errmsg, errmsglen) == -1) + return got_error_from_errno("imsg_add SEND_REF_STATUS"); wbuf->fd = -1; imsg_close(ibuf, wbuf); blob - f989b0dbd6a3ca710184d881f574e55a5eb83e78 blob + 2bfbfe22cdf4a0255e78921e2da2bfcc76ffb03c --- regress/cmdline/send.sh +++ regress/cmdline/send.sh @@ -1511,6 +1511,69 @@ EOF git_fsck "$testroot" "$testroot/repo-clone" ret=$? test_done "$testroot" "$ret" +} + +test_send_rejected() { + local testroot=`test_init send_rejected` + local testurl=ssh://127.0.0.1/$testroot + local commit_id=`git_show_head $testroot/repo` + + if ! got clone -q "$testurl/repo" "$testroot/repo-clone"; then + echo "got clone command failed unexpectedly" >&2 + test_done "$testroot" 1 + return 1 + fi + + mkdir "$testroot/repo-clone/hooks" + cat <<'EOF' >$testroot/repo-clone/hooks/update +case "$1" in +*master*) + echo "rejecting push on master branch" + exit 1 + ;; +esac +exit 0 +EOF + chmod +x "$testroot/repo-clone/hooks/update" + + cat > $testroot/repo/.git/got.conf <$testroot/repo/alpha + git_commit "$testroot/repo" -m "modified alpha" + + got send -q -r "$testroot/repo" >$testroot/stdout 2>$testroot/stderr + ret=$? + if [ $ret -ne 0 ]; then + echo "got send command failed unexpectedly" >&2 + test_done "$testroot" $ret + return 1 + fi + + touch "$testroot/stdout.expected" + if ! cmp -s "$testroot/stdout.expected" "$testroot/stdout"; then + diff -u "$testroot/stdout.expected" "$testroot/stdout" + test_done "$testroot" 1 + return 1 + fi + + cat <$testroot/stderr.expected +rejecting push on master branch +error: hook declined to update refs/heads/master +EOF + + if ! cmp -s "$testroot/stderr.expected" "$testroot/stderr"; then + diff -u "$testroot/stderr.expected" "$testroot/stderr" + test_done "$testroot" 1 + return 1 + fi + + test_done "$testroot" 0 } test_parseargs "$@" @@ -1526,3 +1589,4 @@ run_test test_send_all_branches run_test test_send_to_empty_repo run_test test_send_and_fetch_config run_test test_send_config +run_test test_send_rejected