commit - cf599b8e0000b9576e1ca1015f52f574f6df0f10
commit + 84a6474d63ab0958f05fbea78767ca7ad130af32
blob - 001117e83dbf60d55247f9d00c6ae708c726055f
blob + 5fa5bf526b069dec112a85cb6096f202814858e4
--- gotsys/gotsys.conf.5
+++ gotsys/gotsys.conf.5
can be used when public read-only access to repositories over SSH is desired.
The anonymous user has an empty password, cannot use an SSH public key, and
can only be granted read-only access.
-.\".It Ic protect Brq Ar ...
-.\"The
-.\".Cm protect
-.\"directive may be used to protect branches and tags in a repository
-.\"from being overwritten by potentially destructive client-side commands,
-.\"such as when
-.\".Cm got send -f
-.\"and
-.\".Cm git push -f
-.\"are used to change the history of a branch.
-.\".Pp
-.\"To build a set of protected branches and tags, multiple
-.\".Ic protect
-.\"directives may be specified per repository and
-.\"multiple
-.\".Ic protect
-.\"directive parameters may be specified within curly braces.
-.\".Pp
-.\"The available
-.\".Cm protect
-.\"parameters are as follows:
-.\".Bl -tag -width Ds
-.\".It Ic branch Ar name
-.\"Protect the named branch.
-.\"The branch may be created if it does not exist yet.
-.\"Attempts to delete the branch or change its history will be denied.
-.\".Pp
-.\"If the
-.\".Ar name
-.\"does not already begin with
-.\".Dq refs/heads/
-.\"it will be looked up in the
-.\".Dq refs/heads/
-.\"reference namespace.
-.\".It Ic branch Ic namespace Ar namespace
-.\"Protect the given reference namespace, assuming that references in
-.\"this namespace represent branches.
-.\"New branches may be created in the namespace.
-.\"Attempts to change the history of branches or delete them will be denied.
-.\".Pp
-.\"The
-.\".Ar namespace
-.\"argument must be absolute, starting with
-.\".Dq refs/ .
-.\".It Ic tag Ic namespace Ar namespace
-.\"Protect the given reference namespace, assuming that references in
-.\"this namespace represent tags.
-.\"New tags may be created in the namespace.
-.\"Attempts to change or delete existing tags will be denied.
-.\".Pp
-.\"The
-.\".Ar namespace
-.\"argument must be absolute, starting with
-.\".Dq refs/ .
-.\".El
-.\".Pp
-.\"The special reference namespaces
-.\".Dq refs/got/
-.\"and
-.\".Dq refs/remotes/
-.\"do not need to be listed in
-.\".Nm .
-.\"These namespaces are always protected and even attempts to create new
-.\"references in these namespaces will always be denied.
+.It Ic protect Brq Ar ...
+The
+.Cm protect
+directive may be used to protect branches and tags in a repository
+from being overwritten by potentially destructive client-side commands,
+such as when
+.Cm got send -f
+and
+.Cm git push -f
+are used to change the history of a branch.
+.Pp
+To build a set of protected branches and tags, multiple
+.Ic protect
+directives may be specified per repository and
+multiple
+.Ic protect
+directive parameters may be specified within curly braces.
+.Pp
+The available
+.Cm protect
+parameters are as follows:
+.Bl -tag -width Ds
+.It Ic branch Ar name
+Protect the named branch.
+The branch may be created if it does not exist yet.
+Attempts to delete the branch or change its history will be denied.
+.Pp
+If the
+.Ar name
+does not already begin with
+.Dq refs/heads/
+it will be looked up in the
+.Dq refs/heads/
+reference namespace.
+.It Ic branch Ic namespace Ar namespace
+Protect the given reference namespace, assuming that references in
+this namespace represent branches.
+New branches may be created in the namespace.
+Attempts to change the history of branches or delete them will be denied.
+.Pp
+The
+.Ar namespace
+argument must be absolute, starting with
+.Dq refs/ .
+.It Ic tag Ic namespace Ar namespace
+Protect the given reference namespace, assuming that references in
+this namespace represent tags.
+New tags may be created in the namespace.
+Attempts to change or delete existing tags will be denied.
+.Pp
+The
+.Ar namespace
+argument must be absolute, starting with
+.Dq refs/ .
+.El
+.Pp
+The special reference namespaces
+.Dq refs/got/
+and
+.Dq refs/remotes/
+do not need to be listed in
+.Nm .
+These namespaces are always protected and even attempts to create new
+references in these namespaces will always be denied.
.\".It Ic notify Brq Ar ...
.\"The
.\".Ic notify
blob - 707cce7d91deba214fd9735be9c66dd0b08d64fa
blob + 18b5a178440f33fea8c8aa22d775a03b49c959f0
--- gotsys/gotsys.h
+++ gotsys/gotsys.h
struct gotsys_access_rule_list access_rules;
struct got_pathlist_head protected_tag_namespaces;
+ size_t nprotected_tag_namespaces;
struct got_pathlist_head protected_branch_namespaces;
+ size_t nprotected_branch_namespaces;
struct got_pathlist_head protected_branches;
+ size_t nprotected_branches;
struct got_pathlist_head notification_refs;
struct got_pathlist_head notification_ref_namespaces;
blob - 3b62d252431acad5bdf4e042b6138ebb898c4a88
blob + f3cd5c9f1c4ab2a6c16900bf39cb72143e710b50
--- gotsys/parse.y
+++ gotsys/parse.y
if (conf_protect_ref_namespace(&new, &repo->protected_tag_namespaces,
namespace) == -1)
return -1;
+ repo->nprotected_tag_namespaces++;
RB_FOREACH(pe, got_pathlist_head, &repo->protected_branch_namespaces) {
if (strcmp(pe->path, new) == 0) {
if (conf_protect_ref_namespace(&new,
&repo->protected_branch_namespaces, namespace) == -1)
return -1;
+ repo->nprotected_branch_namespaces++;
RB_FOREACH(pe, got_pathlist_head, &repo->protected_tag_namespaces) {
if (strcmp(pe->path, new) == 0) {
yyerror("duplicate protect branch %s", branchname);
return -1;
}
+ repo->nprotected_branches++;
return 0;
}
blob - d9b5ab290a0fa6d2902a648ad02a8e43a6f9e7e3
blob + 6d37103e663e08e88c395b8e01b66320b5c4bc2f
--- gotsysd/gotsysd.h
+++ gotsysd/gotsysd.h
GOTSYSD_IMSG_SYSCONF_REPOS_DONE,
GOTSYSD_IMSG_SYSCONF_ACCESS_RULE,
GOTSYSD_IMSG_SYSCONF_ACCESS_RULES_DONE,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES_ELEM,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES_ELEM,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES_ELEM,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_REFS_DONE,
GOTSYSD_IMSG_SYSCONF_PARSE_DONE,
/* Addition of users and groups. */
/* Followed by identifier_len bytes. */
};
+
+/*
+ * Structure for sending path lists over imsg. Used with:
+ * GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES
+ * GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES
+ * GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES
+ * GOTSYSD_IMSG_SYSCONF_NOTIFY_BRANCHES
+ * GOTSYSD_IMSG_SYSCONF_NOTIFY_REF_NAMESPACES
+ */
+struct gotsysd_imsg_pathlist {
+ size_t nelem;
+ /* Followed by nelem path list elements. */
+};
+
+/*
+ * Structure for a path list element. Used with:
+ * GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES_ELEM
+ * GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES_ELEM
+ * GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES_ELEM
+ * GOTSYSD_IMSG_SYSCONF_NOTIFY_BRANCHES_ELEM
+ * GOTSYSD_IMSG_SYSCONF_NOTIFY_REF_NAMESPACES_ELEM
+ */
+struct gotsysd_imsg_pathlist_elem {
+ size_t path_len;
+ size_t data_len;
+
+ /* Followed by path_len bytes. */
+ /* Followed by data_len bytes. */
+};
+
#ifndef GOT_LIBEXECDIR
#define GOT_LIBEXECDIR /usr/libexec
#endif
struct gotsys_repolist;
struct gotsys_repo;
struct gotsys_access_rule;
+struct got_pathlist_head;
const struct got_error *gotsys_imsg_send_users(struct gotsysd_imsgev *,
struct gotsys_userlist *, int, int, int);
const struct got_error *gotsys_imsg_recv_access_rule(
struct gotsys_access_rule **, struct imsg *, struct gotsys_userlist *,
struct gotsys_grouplist *);
+const struct got_error *gotsys_imsg_recv_pathlist(size_t *, struct imsg *);
+const struct got_error *gotsys_imsg_recv_pathlist_elem(struct imsg *,
+ struct got_pathlist_head *);
struct gotsys_uidset_element;
struct gotsys_uidset;
blob - 45491d7892cffae8f83ce3db7fe01350bbdaa863
blob + 156666bf66fc9c6047015be78b05d579669ba07e
--- gotsysd/helpers.c
+++ gotsysd/helpers.c
case GOTSYSD_IMSG_SYSCONF_REPOS_DONE:
case GOTSYSD_IMSG_SYSCONF_ACCESS_RULE:
case GOTSYSD_IMSG_SYSCONF_ACCESS_RULES_DONE:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_REFS_DONE:
case GOTSYSD_IMSG_SYSCONF_PARSE_DONE:
if (proc->type != GOTSYSD_IMSG_START_PROG_READ_CONF) {
err = got_error_fmt(GOT_ERR_PRIVSEP_MSG,
case GOTSYSD_IMSG_SYSCONF_REPO:
case GOTSYSD_IMSG_SYSCONF_ACCESS_RULE:
case GOTSYSD_IMSG_SYSCONF_ACCESS_RULES_DONE:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_REFS_DONE:
case GOTSYSD_IMSG_SYSCONF_REPOS_DONE:
proc = find_proc(GOTSYSD_IMSG_START_PROG_WRITE_CONF,
1);
blob - bf8eba639405b64b6389a8f311c72d6404a7710e
blob + 4cf2c6bad5710597487e40327b1c1b47a9e65ba2
--- gotsysd/libexec/gotsys-write-conf/Makefile
+++ gotsysd/libexec/gotsys-write-conf/Makefile
PROG= gotsys-write-conf
SRCS= gotsys-write-conf.c error.c path.c hash.c pollfd.c \
- log.c imsg.c gotsys_conf.c gotsys_imsg.c opentemp.c
+ log.c imsg.c gotsys_conf.c gotsys_imsg.c opentemp.c \
+ reference_parse.c
CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib \
-I${.CURDIR}/../../../gotsys -I${.CURDIR}/../../ -I${.CURDIR}
blob - 2aa43851a7ef5318fbc9caa2089ab0e57439948f
blob + 3224ceb6ed71f00cb8c64518e60e0a64557c78f6
--- gotsysd/libexec/gotsys-write-conf/gotsys-write-conf.c
+++ gotsysd/libexec/gotsys-write-conf/gotsys-write-conf.c
#include "got_path.h"
#include "got_opentemp.h"
#include "got_object.h"
+#include "got_reference.h"
#include "gotsysd.h"
#include "gotsys.h"
static struct gotsys_conf gotsysconf;
static struct gotsys_userlist *users_cur;
static struct gotsys_repo *repo_cur;
+static struct got_pathlist_head *protected_refs_cur;
+static size_t nprotected_refs_needed;
+static size_t nprotected_refs_received;
static int gotd_conf_tmpfd = -1;
static char *gotd_conf_tmppath;
}
static const struct got_error *
+refname_is_valid(const char *refname)
+{
+ if (strncmp(refname, "refs/", 5) != 0) {
+ return got_error_fmt( GOT_ERR_BAD_REF_NAME,
+ "reference name must begin with \"refs/\": %s", refname);
+ }
+
+ if (!got_ref_name_is_valid(refname))
+ return got_error_path(refname, GOT_ERR_BAD_REF_NAME);
+
+ return NULL;
+}
+
+static const struct got_error *
+write_protected_refs(struct gotsys_repo *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_pathlist_entry *pe;
+ int ret;
+ const char *opening = "protect {";
+ const char *closing = "}";
+ char *namespace = NULL;
+
+ if (RB_EMPTY(&repo->protected_tag_namespaces) &&
+ RB_EMPTY(&repo->protected_branch_namespaces) &&
+ RB_EMPTY(&repo->protected_branches))
+ return NULL;
+
+ ret = dprintf(gotd_conf_tmpfd, "\t%s\n", opening);
+ if (ret == -1)
+ return got_error_from_errno2("dprintf", gotd_conf_tmppath);
+ if (ret != 2 + strlen(opening))
+ return got_error_fmt(GOT_ERR_IO, "short write to %s",
+ gotd_conf_tmppath);
+
+ RB_FOREACH(pe, got_pathlist_head, &repo->protected_tag_namespaces) {
+ namespace = strdup(pe->path);
+ if (namespace == NULL)
+ return got_error_from_errno("strdup");
+
+ got_path_strip_trailing_slashes(namespace);
+ err = refname_is_valid(namespace);
+ if (err)
+ goto done;
+
+ ret = dprintf(gotd_conf_tmpfd, "\t\ttag namespace \"%s\"\n",
+ namespace);
+ if (ret == -1) {
+ err = got_error_from_errno2("dprintf",
+ gotd_conf_tmppath);
+ goto done;
+ }
+ if (ret != 19 + strlen(namespace)) {
+ err = got_error_fmt(GOT_ERR_IO, "short write to %s",
+ gotd_conf_tmppath);
+ goto done;
+ }
+ free(namespace);
+ namespace = NULL;
+ }
+
+ RB_FOREACH(pe, got_pathlist_head, &repo->protected_branch_namespaces) {
+ namespace = strdup(pe->path);
+ if (namespace == NULL)
+ return got_error_from_errno("strdup");
+
+ got_path_strip_trailing_slashes(namespace);
+ err = refname_is_valid(namespace);
+ if (err)
+ goto done;
+
+ ret = dprintf(gotd_conf_tmpfd, "\t\tbranch namespace \"%s\"\n",
+ namespace);
+ if (ret == -1) {
+ err = got_error_from_errno2("dprintf",
+ gotd_conf_tmppath);
+ goto done;
+ }
+ if (ret != 22 + strlen(namespace)) {
+ err = got_error_fmt(GOT_ERR_IO, "short write to %s",
+ gotd_conf_tmppath);
+ goto done;
+ }
+ free(namespace);
+ namespace = NULL;
+ }
+
+ RB_FOREACH(pe, got_pathlist_head, &repo->protected_branches) {
+ err = refname_is_valid(pe->path);
+ if (err)
+ return err;
+ ret = dprintf(gotd_conf_tmpfd, "\t\tbranch \"%s\"\n", pe->path);
+ if (ret == -1) {
+ return got_error_from_errno2("dprintf",
+ gotd_conf_tmppath);
+ }
+ if (ret != 12 + strlen(pe->path))
+ return got_error_fmt(GOT_ERR_IO, "short write to %s",
+ gotd_conf_tmppath);
+ }
+
+ ret = dprintf(gotd_conf_tmpfd, "\t%s\n", closing);
+ if (ret == -1)
+ return got_error_from_errno2("dprintf", gotd_conf_tmppath);
+ if (ret != 2 + strlen(closing))
+ return got_error_fmt(GOT_ERR_IO, "short write to %s",
+ gotd_conf_tmppath);
+done:
+ free(namespace);
+ return NULL;
+}
+
+static const struct got_error *
write_gotd_conf(void)
{
const struct got_error *err = NULL;
}
err = write_access_rules(&repo->access_rules);
+ if (err)
+ return err;
+
+ err = write_protected_refs(repo);
if (err)
return err;
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
ssize_t n;
+ size_t npaths;
int shut = 0;
static int flush_and_exit;
"received unexpected imsg %d while in "
"state %d\n", imsg.hdr.type,
writeconf_state);
+ break;
+ }
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES:
+ if (repo_cur == NULL ||
+ writeconf_state != WRITECONF_STATE_EXPECT_REPOS ||
+ protected_refs_cur != NULL ||
+ nprotected_refs_needed != 0) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
break;
}
+ err = gotsys_imsg_recv_pathlist(&npaths, &imsg);
+ if (err)
+ break;
+ protected_refs_cur =
+ &repo_cur->protected_tag_namespaces;
+ nprotected_refs_needed = npaths;
+ nprotected_refs_received = 0;
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES:
+ if (repo_cur == NULL ||
+ writeconf_state != WRITECONF_STATE_EXPECT_REPOS ||
+ protected_refs_cur != NULL ||
+ nprotected_refs_needed != 0) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = gotsys_imsg_recv_pathlist(&npaths, &imsg);
+ if (err)
+ break;
+ protected_refs_cur =
+ &repo_cur->protected_branch_namespaces;
+ nprotected_refs_needed = npaths;
+ nprotected_refs_received = 0;
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES:
+ if (repo_cur == NULL ||
+ writeconf_state != WRITECONF_STATE_EXPECT_REPOS ||
+ protected_refs_cur != NULL ||
+ nprotected_refs_needed != 0) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = gotsys_imsg_recv_pathlist(&npaths, &imsg);
+ if (err)
+ break;
+ protected_refs_cur =
+ &repo_cur->protected_branches;
+ nprotected_refs_needed = npaths;
+ nprotected_refs_received = 0;
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES_ELEM:
+ if (protected_refs_cur == NULL ||
+ writeconf_state != WRITECONF_STATE_EXPECT_REPOS ||
+ nprotected_refs_needed == 0 ||
+ nprotected_refs_received >=
+ nprotected_refs_needed) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ /* TODO: validate refname validity */
+ err = gotsys_imsg_recv_pathlist_elem(&imsg,
+ protected_refs_cur);
+ if (err)
+ break;
+ if (++nprotected_refs_received >=
+ nprotected_refs_needed) {
+ protected_refs_cur = NULL;
+ nprotected_refs_needed = 0;
+ }
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_REFS_DONE:
+ if (repo_cur == NULL ||
+ nprotected_refs_needed != 0 ||
+ writeconf_state != WRITECONF_STATE_EXPECT_REPOS) {
+ err = got_error_fmt(GOT_ERR_PRIVSEP_MSG,
+ "received unexpected imsg %d while in "
+ "state %d\n", imsg.hdr.type,
+ writeconf_state);
+ break;
+ }
repo_cur = NULL;
break;
case GOTSYSD_IMSG_SYSCONF_REPOS_DONE:
blob - b9d26b56d86bb129468b4a03d916c5e108fb12fb
blob + e3f8794ab608fefd10b1564764fb55cb242295fa
--- gotsysd/sysconf.c
+++ gotsysd/sysconf.c
uid_t uid_start;
uid_t uid_end;
int have_anonymous_user;
+ struct got_pathlist_head *protected_refs_cur;
+ size_t *nprotected_refs_cur;
+ size_t nprotected_refs_needed;
+ size_t nprotected_refs_received;
} gotsysd_sysconf;
static struct gotsys_conf gotsysconf;
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
ssize_t n;
+ size_t npaths;
int shut = 0;
if (event & EV_READ) {
break;
}
log_debug("done receiving access rules");
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES:
+ if (gotsysd_sysconf.repo_cur == NULL ||
+ gotsysd_sysconf.protected_refs_cur != NULL ||
+ gotsysd_sysconf.nprotected_refs_needed != 0 ||
+ gotsysd_sysconf.state !=
+ SYSCONF_STATE_EXPECT_REPOS) {
+
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = gotsys_imsg_recv_pathlist(&npaths, &imsg);
+ if (err)
+ break;
+ gotsysd_sysconf.protected_refs_cur =
+ &gotsysd_sysconf.repo_cur->protected_tag_namespaces;
+ gotsysd_sysconf.nprotected_refs_cur =
+ &gotsysd_sysconf.repo_cur->nprotected_tag_namespaces;
+ gotsysd_sysconf.nprotected_refs_needed = npaths;
+ gotsysd_sysconf.nprotected_refs_received = 0;
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES:
+ if (gotsysd_sysconf.repo_cur == NULL ||
+ gotsysd_sysconf.protected_refs_cur != NULL ||
+ gotsysd_sysconf.nprotected_refs_needed != 0 ||
+ gotsysd_sysconf.state !=
+ SYSCONF_STATE_EXPECT_REPOS) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = gotsys_imsg_recv_pathlist(&npaths, &imsg);
+ if (err)
+ break;
+ gotsysd_sysconf.protected_refs_cur =
+ &gotsysd_sysconf.repo_cur->protected_branch_namespaces;
+ gotsysd_sysconf.nprotected_refs_cur =
+ &gotsysd_sysconf.repo_cur->nprotected_branch_namespaces;
+ gotsysd_sysconf.nprotected_refs_needed = npaths;
+ gotsysd_sysconf.nprotected_refs_received = 0;
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES:
+ if (gotsysd_sysconf.repo_cur == NULL ||
+ gotsysd_sysconf.protected_refs_cur != NULL ||
+ gotsysd_sysconf.nprotected_refs_needed != 0 ||
+ gotsysd_sysconf.state !=
+ SYSCONF_STATE_EXPECT_REPOS) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = gotsys_imsg_recv_pathlist(&npaths, &imsg);
+ if (err)
+ break;
+ gotsysd_sysconf.protected_refs_cur =
+ &gotsysd_sysconf.repo_cur->protected_branches;
+ gotsysd_sysconf.nprotected_refs_cur =
+ &gotsysd_sysconf.repo_cur->nprotected_branches;
+ gotsysd_sysconf.nprotected_refs_needed = npaths;
+ gotsysd_sysconf.nprotected_refs_received = 0;
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES_ELEM:
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES_ELEM:
+ if (gotsysd_sysconf.protected_refs_cur == NULL ||
+ gotsysd_sysconf.nprotected_refs_cur == NULL ||
+ gotsysd_sysconf.nprotected_refs_needed == 0 ||
+ gotsysd_sysconf.nprotected_refs_received >=
+ gotsysd_sysconf.nprotected_refs_needed ||
+ gotsysd_sysconf.state !=
+ SYSCONF_STATE_EXPECT_REPOS) {
+ err = got_error(GOT_ERR_PRIVSEP_MSG);
+ break;
+ }
+ err = gotsys_imsg_recv_pathlist_elem(&imsg,
+ gotsysd_sysconf.protected_refs_cur);
+ if (err)
+ break;
+ if (++gotsysd_sysconf.nprotected_refs_received >=
+ gotsysd_sysconf.nprotected_refs_needed) {
+ gotsysd_sysconf.protected_refs_cur = NULL;
+ *gotsysd_sysconf.nprotected_refs_cur =
+ gotsysd_sysconf.nprotected_refs_received;
+ gotsysd_sysconf.nprotected_refs_needed = 0;
+ gotsysd_sysconf.nprotected_refs_received = 0;
+ }
+ break;
+ case GOTSYSD_IMSG_SYSCONF_PROTECTED_REFS_DONE:
+ if (gotsysd_sysconf.repo_cur == NULL ||
+ gotsysd_sysconf.nprotected_refs_needed != 0 ||
+ gotsysd_sysconf.protected_refs_cur != NULL ||
+ gotsysd_sysconf.state !=
+ SYSCONF_STATE_EXPECT_REPOS) {
+ err = got_error_fmt(GOT_ERR_PRIVSEP_MSG,
+ "received unexpected imsg %d while in "
+ "state %d\n", imsg.hdr.type,
+ gotsysd_sysconf.state);
+ break;
+ }
+ log_debug("done receiving protected refs");
gotsysd_sysconf.repo_cur = NULL;
break;
case GOTSYSD_IMSG_SYSCONF_REPOS_DONE:
blob - 28bdc76a4a5c4aadfcb1362a99a3ed0a9f5c6c71
blob + 58b5c39c8106660a557c87b8ece84046cd2dbadf
--- lib/gotsys_imsg.c
+++ lib/gotsys_imsg.c
}
static const struct got_error *
+send_pathlist_elem(struct gotsysd_imsgev *iev, const char *refname,
+ int imsg_type)
+{
+ struct gotsysd_imsg_pathlist_elem ielem;
+ struct ibuf *wbuf = NULL;
+
+ memset(&ielem, 0, sizeof(ielem));
+ ielem.path_len = strlen(refname);
+
+ wbuf = imsg_create(&iev->ibuf, imsg_type, 0, 0,
+ sizeof(ielem) + ielem.path_len);
+ if (wbuf == NULL)
+ return got_error_from_errno_fmt("imsg_create %d", imsg_type);
+
+ if (imsg_add(wbuf, &ielem, sizeof(ielem)) == -1)
+ return got_error_from_errno_fmt("imsg_add %d", imsg_type);
+ if (imsg_add(wbuf, refname, ielem.path_len) == -1)
+ return got_error_from_errno_fmt("imsg_add %d", imsg_type);
+
+ imsg_close(&iev->ibuf, wbuf);
+ return gotsysd_imsg_flush(&iev->ibuf);
+}
+
+static const struct got_error *
+send_protected_refs(struct gotsysd_imsgev *iev, struct gotsys_repo *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_pathlist_entry *pe;
+ struct gotsysd_imsg_pathlist ilist;
+
+ memset(&ilist, 0, sizeof(ilist));
+
+ ilist.nelem = repo->nprotected_tag_namespaces;
+ if (ilist.nelem > 0) {
+ if (gotsysd_imsg_compose_event(iev,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES,
+ 0, -1, &ilist, sizeof(ilist)) == -1) {
+ return got_error_from_errno("imsg compose "
+ "PROTECTED_TAG_NAMESPACES");
+ }
+
+ RB_FOREACH(pe, got_pathlist_head,
+ &repo->protected_tag_namespaces) {
+ err = send_pathlist_elem(iev, pe->path,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES_ELEM);
+ if (err)
+ return err;
+ }
+ }
+
+ ilist.nelem = repo->nprotected_branch_namespaces;
+ if (ilist.nelem > 0) {
+ if (gotsysd_imsg_compose_event(iev,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES,
+ 0, -1, &ilist, sizeof(ilist)) == -1) {
+ return got_error_from_errno("imsg compose "
+ "PROTECTED_BRANCH_NAMESPACES");
+ }
+
+ RB_FOREACH(pe, got_pathlist_head,
+ &repo->protected_branch_namespaces) {
+ err = send_pathlist_elem(iev, pe->path,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCH_NAMESPACES_ELEM);
+ if (err)
+ return err;
+ }
+ }
+
+ ilist.nelem = repo->nprotected_branches;
+ if (ilist.nelem > 0) {
+ if (gotsysd_imsg_compose_event(iev,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES,
+ 0, -1, &ilist, sizeof(ilist)) == -1) {
+ return got_error_from_errno("imsg compose "
+ "PROTECTED_BRANCH_NAMESPACES");
+ }
+
+ RB_FOREACH(pe, got_pathlist_head, &repo->protected_branches) {
+ err = send_pathlist_elem(iev, pe->path,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_BRANCHES_ELEM);
+ if (err)
+ return err;
+ }
+ }
+
+ if (gotsysd_imsg_compose_event(iev,
+ GOTSYSD_IMSG_SYSCONF_PROTECTED_REFS_DONE, 0, -1, NULL, 0) == -1)
+ return got_error_from_errno("gotsysd_imsg_compose_event");
+
+ return NULL;
+}
+
+static const struct got_error *
send_repo(struct gotsysd_imsgev *iev, struct gotsys_repo *repo)
{
const struct got_error *err;
return got_error_from_errno("gotsysd_imsg_compose_event");
}
- /* TODO: send protected tags and branches */
+ err = send_protected_refs(iev, repo);
+ if (err)
+ return err;
/* TODO: send notification config */
free(identifier);
return err;
}
+
+const struct got_error *
+gotsys_imsg_recv_pathlist(size_t *npaths, struct imsg *imsg)
+{
+ struct gotsysd_imsg_pathlist ilist;
+ size_t datalen;
+
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ if (datalen != sizeof(ilist))
+ return got_error(GOT_ERR_PRIVSEP_LEN);
+ memcpy(&ilist, imsg->data, sizeof(ilist));
+
+ if (ilist.nelem == 0)
+ return got_error(GOT_ERR_PRIVSEP_LEN);
+
+ *npaths = ilist.nelem;
+ return NULL;
+}
+
+const struct got_error *
+gotsys_imsg_recv_pathlist_elem(struct imsg *imsg,
+ struct got_pathlist_head *paths)
+{
+ const struct got_error *err = NULL;
+ struct gotsysd_imsg_pathlist_elem ielem;
+ size_t datalen;
+ char *path;
+ struct got_pathlist_entry *pe;
+
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ if (datalen < sizeof(ielem))
+ return got_error(GOT_ERR_PRIVSEP_LEN);
+ memcpy(&ielem, imsg->data, sizeof(ielem));
+
+ if (datalen != sizeof(ielem) + ielem.path_len)
+ return got_error(GOT_ERR_PRIVSEP_LEN);
+
+ path = strndup(imsg->data + sizeof(ielem), ielem.path_len);
+ if (path == NULL)
+ return got_error_from_errno("strndup");
+
+ err = got_pathlist_insert(&pe, paths, path, NULL);
+ if (err || pe == NULL)
+ free(path);
+ return err;
+}
blob - 1e68e76f694a8b64e2cac3adcdd9e8140c38e3fc
blob + 9ab2945ef4f0d7684ff35c3c3ec60abe54b149c5
--- regress/gotsysd/test_gotsysd.sh
+++ regress/gotsysd/test_gotsysd.sh
fi
echo "HEAD: refs/heads/foo" > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "gotsysd failed to apply configuration" >&2
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ test_done "$testroot" "$ret"
+}
+
+test_protect_refs() {
+ local testroot=`test_init protect_refs 1`
+
+ got checkout -q $testroot/${GOTSYS_REPO} $testroot/wt >/dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got checkout failed unexpectedly" >&2
+ test_done "$testroot" 1
+ return 1
+ fi
+
+ crypted_vm_pw=`echo ${GOTSYSD_VM_PASSWORD} | encrypt | tr -d '\n'`
+ crypted_pw=`echo ${GOTSYSD_DEV_PASSWORD} | encrypt | tr -d '\n'`
+ sshkey=`cat ${GOTSYSD_SSH_PUBKEY}`
+ cat > ${testroot}/wt/gotsys.conf <<EOF
+group slackers
+
+user ${GOTSYSD_TEST_USER} {
+ password "${crypted_vm_pw}"
+ authorized key ${sshkey}
+}
+user ${GOTSYSD_DEV_USER} {
+ password "${crypted_pw}"
+ authorized key ${sshkey}
+}
+repository gotsys.git {
+ permit rw ${GOTSYSD_TEST_USER}
+ permit rw ${GOTSYSD_DEV_USER}
+}
+repository "foo" {
+ permit rw ${GOTSYSD_DEV_USER}
+ permit ro anonymous
+ head foo
+ protect branch foo
+ protect {
+ tag namespace "refs/tags"
+ }
+}
+EOF
+ (cd ${testroot}/wt && \
+ got commit -m "protect branch and tags" >/dev/null)
+ local commit_id=`git_show_head $testroot/${GOTSYS_REPO}`
+
+ got send -q -i ${GOTSYSD_SSH_KEY} -r ${testroot}/${GOTSYS_REPO}
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got send failed unexpectedly" >&2
+ test_done "$testroot" 1
+ return 1
+ fi
+
+ # Wait for gotsysd to apply the new configuration.
+ echo "$commit_id" > $testroot/stdout.expected
+ for i in 1 2 3 4 5; do
+ sleep 1
+ ssh -i ${GOTSYSD_SSH_KEY} root@${VMIP} \
+ cat /var/db/gotsysd/commit > $testroot/stdout
+ if cmp -s $testroot/stdout.expected $testroot/stdout; then
+ break;
+ fi
+ done
cmp -s $testroot/stdout.expected $testroot/stdout
ret=$?
if [ $ret -ne 0 ]; then
return 1
fi
+ # Create a new commit on branch "foo" and send it.
+ got clone -q -i ${GOTSYSD_SSH_KEY} -b foo \
+ ${GOTSYSD_DEV_USER}@${VMIP}:foo.git $testroot/foo.git
+ got checkout -q $testroot/foo.git $testroot/wt-foo > /dev/null
+ echo "tweak alpha" > $testroot/wt-foo/alpha
+ (cd $testroot/wt-foo && got commit -m 'change alpha' > /dev/null)
+ got send -q -i ${GOTSYSD_SSH_KEY} -r $testroot/foo.git -b foo
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got send failed unexpectedly" >&2
+ return 1
+ fi
+
+ # Attempt to rewrite the history of branch "foo".
+ (cd $testroot/wt-foo && got update -q -c :head:-1 > /dev/null)
+ (cd $testroot/wt-foo && got histedit -d > /dev/null)
+ got send -q -i ${GOTSYSD_SSH_KEY} -r $testroot/foo.git -b foo -f \
+ > $testroot/stdout 2> $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ echo "got send succeeded unexpectedly" >&2
+ return 1
+ fi
+
+ echo -n "" > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "gotsh: refs/heads/foo: reference is protected" \
+ > $testroot/stderr.expected
+ grep '^gotsh:' $testroot/stderr > $testroot/stderr.filtered
+ cmp -s $testroot/stderr.expected $testroot/stderr.filtered
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr.filtered
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
test_done "$testroot" "$ret"
}
run_test test_user_anonymous
run_test test_bad_gotsysconf
run_test test_set_head
+run_test test_protect_refs