commit aba9c984d1420b77b23320520e70b27ecc83acf7 from: Stefan Sperling date: Sun Sep 08 15:14:34 2019 UTC obtain repo format version and commit author name/email from .git/config commit - 4b55f4595673e9079751e4fe3365b412972a4c11 commit + aba9c984d1420b77b23320520e70b27ecc83acf7 blob - 8c1fca65ca8a824451198211eb32ae34620b0f8f blob + 4d887779da3647bcf38fded4c8db1d120fb3aaaf --- got/Makefile +++ got/Makefile @@ -8,7 +8,7 @@ SRCS= got.c blame.c commit_graph.c delta.c diff.c \ object_idset.c object_parse.c opentemp.c path.c pack.c \ privsep.c reference.c repository.c sha1.c worktree.c \ inflate.c buf.c worklist.c rcsutil.c diff3.c lockfile.c \ - deflate.c object_create.c gitconfig.c + deflate.c object_create.c MAN = ${PROG}.1 got-worktree.5 git-repository.5 CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib blob - 8ff5e0925c6e1d39cd9c22786a291f25996cb555 blob + ba35bfd0b7a92102f226ca60b973aafe8a4083e7 --- got/got.1 +++ got/got.1 @@ -90,7 +90,14 @@ The .Cm got import command requires the .Ev GOT_AUTHOR -environment variable to be set. +environment variable to be set, +unless Git's +.Dv user.name +and +.Dv user.email configuration settings can be +obtained from the repository's +.Pa .git/config +file. .Pp The options for .Cm got import @@ -690,7 +697,14 @@ The .Cm got commit command requires the .Ev GOT_AUTHOR -environment variable to be set. +environment variable to be set, +unless Git's +.Dv user.name +and +.Dv user.email configuration settings can be +obtained from the repository's +.Pa .git/config +file. .Pp The options for .Cm got commit @@ -1204,7 +1218,7 @@ The author's name and email address for and .Cm got import , for example: -.An Flan Hacker Aq Mt flan_hacker@openbsd.org . +.Dq An Flan Hacker Aq Mt flan_hacker@openbsd.org . Because .Xr git 1 may fail to parse commits without an email address in author data, @@ -1212,6 +1226,13 @@ may fail to parse commits without an email address in attempts to reject .Ev GOT_AUTHOR environment variables with a missing email address. +Git's +.Dv user.name +and +.Dv user.email configuration settings in the repository's +.Pa .git/config +file will override the value of +.Ev GOT_AUTHOR . .It Ev VISUAL , EDITOR The editor spawned by .Cm got commit , blob - 4c8177b93f67a544c333230c5af5ce699c45e6ab blob + 5f3779846731a64630d792b8c821b712956c9160 --- got/got.c +++ got/got.c @@ -492,11 +492,21 @@ import_progress(void *arg, const char *path) } static const struct got_error * -get_author(const char **author) +get_author(char **author, struct got_repository *repo) { - const char *got_author; + const struct got_error *err = NULL; + const char *got_author, *gitconfig_name, *gitconfig_email; *author = NULL; + + gitconfig_name = got_repo_get_gitconfig_author_name(repo); + gitconfig_email = got_repo_get_gitconfig_author_email(repo); + if (gitconfig_name && gitconfig_email) { + if (asprintf(author, "%s <%s>", + gitconfig_name, gitconfig_email) == -1) + return got_error_from_errno("asprintf"); + return NULL; + } got_author = getenv("GOT_AUTHOR"); if (got_author == NULL) { @@ -504,7 +514,9 @@ get_author(const char **author) return got_error(GOT_ERR_COMMIT_NO_AUTHOR); } - *author = got_author; + *author = strdup(got_author); + if (*author == NULL) + return got_error_from_errno("strdup"); /* * Really dumb email address check; we're only doing this to @@ -512,18 +524,26 @@ get_author(const char **author) */ while (*got_author && *got_author != '<') got_author++; - if (*got_author != '<') - return got_error(GOT_ERR_COMMIT_NO_EMAIL); + if (*got_author != '<') { + err = got_error(GOT_ERR_COMMIT_NO_EMAIL); + goto done; + } while (*got_author && *got_author != '@') got_author++; - if (*got_author != '@') - return got_error(GOT_ERR_COMMIT_NO_EMAIL); + if (*got_author != '@') { + err = got_error(GOT_ERR_COMMIT_NO_EMAIL); + goto done; + } while (*got_author && *got_author != '>') got_author++; if (*got_author != '>') - return got_error(GOT_ERR_COMMIT_NO_EMAIL); - - return NULL; + err = got_error(GOT_ERR_COMMIT_NO_EMAIL); +done: + if (err) { + free(*author); + *author = NULL; + } + return err; } static const struct got_error * @@ -531,8 +551,7 @@ cmd_import(int argc, char *argv[]) { const struct got_error *error = NULL; char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL; - char *editor = NULL; - const char *author; + char *editor = NULL, *author = NULL; const char *branch_name = "master"; char *refname = NULL, *id_str = NULL; struct got_repository *repo = NULL; @@ -581,16 +600,13 @@ cmd_import(int argc, char *argv[]) argv += optind; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath fattr flock proc exec unveil", + if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " + "unveil", NULL) == -1) err(1, "pledge"); #endif if (argc != 1) usage_import(); - - error = get_author(&author); - if (error) - return error; if (repo_path == NULL) { repo_path = getcwd(NULL, 0); @@ -601,6 +617,10 @@ cmd_import(int argc, char *argv[]) error = got_repo_open(&repo, repo_path); if (error) goto done; + + error = get_author(&author, repo); + if (error) + return error; if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) { error = got_error_from_errno("asprintf"); @@ -684,6 +704,7 @@ done: free(refname); free(new_commit_id); free(id_str); + free(author); if (branch_ref) got_ref_close(branch_ref); if (head_ref) @@ -3659,8 +3680,7 @@ add_tag(struct got_repository *repo, const char *tag_n struct got_object_id *commit_id = NULL, *tag_id = NULL; char *label = NULL, *commit_id_str = NULL; struct got_reference *ref = NULL; - char *refname = NULL, *tagmsg = NULL; - const char *tagger; + char *refname = NULL, *tagmsg = NULL, *tagger = NULL; /* * Don't let the user create a tag named '-'. @@ -3670,7 +3690,7 @@ add_tag(struct got_repository *repo, const char *tag_n if (tag_name[0] == '-' && tag_name[1] == '\0') return got_error(GOT_ERR_BAD_REF_NAME); - err = get_author(&tagger); + err = get_author(&tagger, repo); if (err) return err; @@ -3733,6 +3753,7 @@ done: free(commit_id_str); free(refname); free(tagmsg); + free(tagger); return err; } @@ -4384,9 +4405,8 @@ cmd_commit(int argc, char *argv[]) char *cwd = NULL, *id_str = NULL; struct got_object_id *id = NULL; const char *logmsg = NULL; - const char *author; - struct collect_commit_logmsg_arg cl_arg; - char *editor = NULL; + struct collect_commit_logmsg_arg cl_arg; + char *editor = NULL, *author = NULL; int ch, rebase_in_progress, histedit_in_progress; struct got_pathlist_head paths; @@ -4412,10 +4432,6 @@ cmd_commit(int argc, char *argv[]) "unveil", NULL) == -1) err(1, "pledge"); #endif - error = get_author(&author); - if (error) - return error; - cwd = getcwd(NULL, 0); if (cwd == NULL) { error = got_error_from_errno("getcwd"); @@ -4442,6 +4458,10 @@ cmd_commit(int argc, char *argv[]) if (error != NULL) goto done; + error = get_author(&author, repo); + if (error) + return error; + /* * unveil(2) traverses exec(2); if an editor is used we have * to apply unveil after the log message has been written. @@ -4495,6 +4515,7 @@ done: free(cwd); free(id_str); free(editor); + free(author); return error; } blob - 97f04731b6ad9e1bccd53406696dab9612512659 blob + f8e9822b47d297dd837ffbfe9f018be2143c3b44 --- include/got_error.h +++ include/got_error.h @@ -123,6 +123,7 @@ #define GOT_ERR_PATCH_CHOICE 107 #define GOT_ERR_COMMIT_NO_EMAIL 108 #define GOT_ERR_TAG_EXISTS 109 +#define GOT_ERR_GIT_REPO_FORMAT 110 static const struct got_error { int code; @@ -252,6 +253,7 @@ static const struct got_error { "no email address; an email address is required for compatibility " "with Git" }, { GOT_ERR_TAG_EXISTS,"specified tag already exists" }, + { GOT_ERR_GIT_REPO_FORMAT,"unknown git repository format version" }, }; /* blob - 16cdca51a48b0e15d4516534fe4efec46fe384af blob + f2ecc7472a8da5185c853683082cf7806c36d290 --- include/got_repository.h +++ include/got_repository.h @@ -31,6 +31,12 @@ const char *got_repo_get_path(struct got_repository *) */ const char *got_repo_get_path_git_dir(struct got_repository *); +/* Obtain the commit author name if parsed from gitconfig, else NULL. */ +const char *got_repo_get_gitconfig_author_name(struct got_repository *); + +/* Obtain the commit author email if parsed from gitconfig, else NULL. */ +const char *got_repo_get_gitconfig_author_email(struct got_repository *); + /* * Obtain paths to various directories within a repository. * The caller must dispose of a path with free(3). blob - 754cbc5403b1c92e536fca4f373dea3a300c73d4 blob + 1ddfb97d543989190d1cf894c45ed4ed1424f1aa --- lib/gitconfig.c +++ lib/gitconfig.c @@ -307,7 +307,7 @@ conf_parse(struct got_gitconfig *conf, int trans, char } const struct got_error * -got_gitconfig_open(struct got_gitconfig **conf, const char *gitconfig_path) +got_gitconfig_open(struct got_gitconfig **conf, int fd) { unsigned int i; @@ -318,7 +318,7 @@ got_gitconfig_open(struct got_gitconfig **conf, const for (i = 0; i < nitems((*conf)->bindings); i++) LIST_INIT(&(*conf)->bindings[i]); TAILQ_INIT(&(*conf)->trans_queue); - return got_gitconfig_reinit(*conf, gitconfig_path); + return got_gitconfig_reinit(*conf, fd); } static void @@ -390,23 +390,16 @@ conf_begin(struct got_gitconfig *conf) /* Open the config file and map it into our address space, then parse it. */ const struct got_error * -got_gitconfig_reinit(struct got_gitconfig *conf, const char *gitconfig_path) +got_gitconfig_reinit(struct got_gitconfig *conf, int fd) { const struct got_error *err = NULL; - int fd, trans; + int trans; size_t sz; char *new_conf_addr = 0; struct stat st; - fd = open(gitconfig_path, O_RDONLY, 0); - if (fd == -1) { - if (errno != ENOENT) - return got_error_from_errno2("open", gitconfig_path); - return NULL; - } - if (fstat(fd, &st)) { - err = got_error_from_errno2("fstat", gitconfig_path); + err = got_error_from_errno("fstat"); goto fail; } @@ -421,8 +414,6 @@ got_gitconfig_reinit(struct got_gitconfig *conf, const err = got_error_from_errno("read"); goto fail; } - close(fd); - fd = -1; trans = conf_begin(conf); @@ -438,8 +429,6 @@ got_gitconfig_reinit(struct got_gitconfig *conf, const fail: free(new_conf_addr); - if (fd != -1) - close(fd); return err; } blob - 828787b0e4bac8fc800346d343d9896bcd3e3801 blob + 4d61ab3964722d849b94e3048f1cb6a15d2edc3f --- lib/got_lib_gitconfig.h +++ lib/got_lib_gitconfig.h @@ -39,15 +39,17 @@ struct got_gitconfig_list { struct got_gitconfig; void got_gitconfig_free_list(struct got_gitconfig_list *); -struct got_gitconfig_list *got_gitconfig_get_list(struct got_gitconfig *, char *, char *); +struct got_gitconfig_list *got_gitconfig_get_list(struct got_gitconfig *, + char *, char *); struct got_gitconfig_list *got_gitconfig_get_tag_list(struct got_gitconfig *, char *); -int got_gitconfig_get_num(struct got_gitconfig *, char *, char *, int); -char *got_gitconfig_get_str(struct got_gitconfig *, char *, char *); -const struct got_error *got_gitconfig_open(struct got_gitconfig **, - const char *); +int got_gitconfig_get_num(struct got_gitconfig *, char *, char *, + int); +char *got_gitconfig_get_str(struct got_gitconfig *, char *, + char *); +const struct got_error *got_gitconfig_open(struct got_gitconfig **, int); void got_gitconfig_close(struct got_gitconfig *); int got_gitconfig_match_num(struct got_gitconfig *, char *, char *, int); -const struct got_error *got_gitconfig_reinit(struct got_gitconfig *, const char *); +const struct got_error *got_gitconfig_reinit(struct got_gitconfig *, int); int got_gitconfig_remove(struct got_gitconfig *, int, char *, char *); int got_gitconfig_remove_section(struct got_gitconfig *, int, char *); int got_gitconfig_set(struct got_gitconfig *, int, char *, char *, char *, int, int); blob - 1ad8832b391a09f83bd2201d0c1400a6c47b6f39 blob + f1e7f2ad14684a954b1bc1e9b4a6195cc963c7c3 --- lib/got_lib_privsep.h +++ lib/got_lib_privsep.h @@ -41,6 +41,7 @@ #define GOT_PROG_READ_BLOB got-read-blob #define GOT_PROG_READ_TAG got-read-tag #define GOT_PROG_READ_PACK got-read-pack +#define GOT_PROG_READ_GITCONFIG got-read-gitconfig #define GOT_STRINGIFY(x) #x #define GOT_STRINGVAL(x) GOT_STRINGIFY(x) @@ -58,6 +59,8 @@ GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_TAG) #define GOT_PATH_PROG_READ_PACK \ GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_PACK) +#define GOT_PATH_PROG_READ_GITCONFIG \ + GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_GITCONFIG) struct got_privsep_child { int imsg_fd; @@ -102,6 +105,14 @@ enum got_imsg_type { /* Message sending file descriptor to a temporary file. */ GOT_IMSG_TMPFD, + + /* Messages related to gitconfig files. */ + GOT_IMSG_GITCONFIG_PARSE_REQUEST, + GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST, + GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST, + GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST, + GOT_IMSG_GITCONFIG_INT_VAL, + GOT_IMSG_GITCONFIG_STR_VAL, }; /* Structure for GOT_IMSG_ERROR. */ @@ -264,3 +275,20 @@ const struct got_error *got_privsep_init_pack_child(st const struct got_error *got_privsep_send_packed_obj_req(struct imsgbuf *, int, struct got_object_id *); const struct got_error *got_privsep_send_pack_child_ready(struct imsgbuf *); + +const struct got_error *got_privsep_send_gitconfig_parse_req(struct imsgbuf *, + int); +const struct got_error * + got_privsep_send_gitconfig_repository_format_version_req(struct imsgbuf *); +const struct got_error *got_privsep_send_gitconfig_author_name_req( + struct imsgbuf *); +const struct got_error *got_privsep_send_gitconfig_author_email_req( + struct imsgbuf *); +const struct got_error *got_privsep_send_gitconfig_str(struct imsgbuf *, + const char *); +const struct got_error *got_privsep_recv_gitconfig_str(char **, + struct imsgbuf *); +const struct got_error *got_privsep_send_gitconfig_int(struct imsgbuf *, int); +const struct got_error *got_privsep_recv_gitconfig_int(int *, struct imsgbuf *); + +void got_privsep_exec_child(int[2], const char *, const char *); blob - 23ca2d585f593cbd7176990ce665928bea219089 blob + 71d6c0578341427205b4086dc64fde982ec030e4 --- lib/got_lib_repository.h +++ lib/got_lib_repository.h @@ -42,7 +42,9 @@ struct got_repository { struct got_object_cache tagcache; /* Settings read from Git configuration files. */ - struct got_gitconfig *gitconfig; + int gitconfig_repository_format_version; + char *gitconfig_author_name; + char *gitconfig_author_email; }; const struct got_error*got_repo_cache_object(struct got_repository *, blob - e5c043ce567c8eb883622b2a78b71bb9ba64227d blob + ca9b1470b78030edb637ec17878e01fe010a17b1 --- lib/object.c +++ lib/object.c @@ -179,30 +179,6 @@ get_packfile_path(char **path_packfile, struct got_pac return got_error(GOT_ERR_NO_SPACE); return NULL; -} - -static void -exec_privsep_child(int imsg_fds[2], const char *path, const char *repo_path) -{ - if (close(imsg_fds[0]) != 0) { - fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno)); - _exit(1); - } - - if (dup2(imsg_fds[1], GOT_IMSG_FD_CHILD) == -1) { - fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno)); - _exit(1); - } - if (closefrom(GOT_IMSG_FD_CHILD + 1) == -1) { - fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno)); - _exit(1); - } - - if (execl(path, path, repo_path, (char *)NULL) == -1) { - fprintf(stderr, "%s: %s: %s\n", getprogname(), path, - strerror(errno)); - _exit(1); - } } static const struct got_error * @@ -272,7 +248,7 @@ start_pack_privsep_child(struct got_pack *pack, struct goto done; } else if (pid == 0) { set_max_datasize(); - exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_PACK, + got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_PACK, pack->path_packfile); /* not reached */ } @@ -392,7 +368,7 @@ read_object_header_privsep(struct got_object **obj, st if (pid == -1) return got_error_from_errno("fork"); else if (pid == 0) { - exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_OBJECT, + got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_OBJECT, repo->path); /* not reached */ } @@ -557,7 +533,7 @@ read_commit_privsep(struct got_commit_object **commit, if (pid == -1) return got_error_from_errno("fork"); else if (pid == 0) { - exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_COMMIT, + got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_COMMIT, repo->path); /* not reached */ } @@ -736,7 +712,7 @@ read_tree_privsep(struct got_tree_object **tree, int o if (pid == -1) return got_error_from_errno("fork"); else if (pid == 0) { - exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_TREE, + got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_TREE, repo->path); /* not reached */ } @@ -957,7 +933,7 @@ read_blob_privsep(uint8_t **outbuf, size_t *size, size if (pid == -1) return got_error_from_errno("fork"); else if (pid == 0) { - exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_BLOB, + got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_BLOB, repo->path); /* not reached */ } @@ -1293,7 +1269,7 @@ read_tag_privsep(struct got_tag_object **tag, int obj_ if (pid == -1) return got_error_from_errno("fork"); else if (pid == 0) { - exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_TAG, + got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_TAG, repo->path); /* not reached */ } blob - f1ce37f56adc95e212cca7ec18ec0d69629deac9 blob + f9961bfb6a787156474e30da32622cdb054366e6 --- lib/privsep.c +++ lib/privsep.c @@ -1211,11 +1211,152 @@ got_privsep_send_packed_obj_req(struct imsgbuf *ibuf, &iobj, sizeof(iobj)) == -1) return got_error_from_errno("imsg_compose " "PACKED_OBJECT_REQUEST"); + + return flush_imsg(ibuf); +} + +const struct got_error * +got_privsep_send_gitconfig_parse_req(struct imsgbuf *ibuf, int fd) +{ + const struct got_error *err = NULL; + + if (imsg_compose(ibuf, GOT_IMSG_GITCONFIG_PARSE_REQUEST, 0, 0, fd, + NULL, 0) == -1) { + err = got_error_from_errno("imsg_compose " + "GITCONFIG_PARSE_REQUEST"); + close(fd); + return err; + } + + return flush_imsg(ibuf); +} + +const struct got_error * +got_privsep_send_gitconfig_repository_format_version_req(struct imsgbuf *ibuf) +{ + if (imsg_compose(ibuf, + GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST, 0, 0, -1, + NULL, 0) == -1) + return got_error_from_errno("imsg_compose " + "GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST"); + + return flush_imsg(ibuf); +} + +const struct got_error * +got_privsep_send_gitconfig_author_name_req(struct imsgbuf *ibuf) +{ + if (imsg_compose(ibuf, + GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST, 0, 0, -1, NULL, 0) == -1) + return got_error_from_errno("imsg_compose " + "GITCONFIG_AUTHOR_NAME_REQUEST"); + + return flush_imsg(ibuf); +} + +const struct got_error * +got_privsep_send_gitconfig_author_email_req(struct imsgbuf *ibuf) +{ + if (imsg_compose(ibuf, + GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST, 0, 0, -1, NULL, 0) == -1) + return got_error_from_errno("imsg_compose " + "GITCONFIG_AUTHOR_EMAIL_REQUEST"); + + return flush_imsg(ibuf); +} + +const struct got_error * +got_privsep_send_gitconfig_str(struct imsgbuf *ibuf, const char *value) +{ + size_t len = value ? strlen(value) + 1 : 0; + + if (imsg_compose(ibuf, GOT_IMSG_GITCONFIG_STR_VAL, 0, 0, -1, + value, len) == -1) + return got_error_from_errno("imsg_compose GITCONFIG_STR_VAL"); + + return flush_imsg(ibuf); +} + +const struct got_error * +got_privsep_recv_gitconfig_str(char **str, struct imsgbuf *ibuf) +{ + const struct got_error *err = NULL; + struct imsg imsg; + size_t datalen; + const size_t min_datalen = 0; + + *str = NULL; + + err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen); + if (err) + return err; + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + + switch (imsg.hdr.type) { + case GOT_IMSG_GITCONFIG_STR_VAL: + if (datalen == 0) + break; + *str = malloc(datalen); + if (*str == NULL) { + err = got_error_from_errno("malloc"); + break; + } + if (strlcpy(*str, imsg.data, datalen) >= datalen) + err = got_error(GOT_ERR_NO_SPACE); + break; + default: + err = got_error(GOT_ERR_PRIVSEP_MSG); + break; + } + imsg_free(&imsg); + return err; +} + +const struct got_error * +got_privsep_send_gitconfig_int(struct imsgbuf *ibuf, int value) +{ + if (imsg_compose(ibuf, GOT_IMSG_GITCONFIG_INT_VAL, 0, 0, -1, + &value, sizeof(value)) == -1) + return got_error_from_errno("imsg_compose GITCONFIG_INT_VAL"); + return flush_imsg(ibuf); } const struct got_error * +got_privsep_recv_gitconfig_int(int *val, struct imsgbuf *ibuf) +{ + const struct got_error *err = NULL; + struct imsg imsg; + size_t datalen; + const size_t min_datalen = + MIN(sizeof(struct got_imsg_error), sizeof(int)); + + *val = 0; + + err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen); + if (err) + return err; + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + + switch (imsg.hdr.type) { + case GOT_IMSG_GITCONFIG_INT_VAL: + if (datalen != sizeof(*val)) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + memcpy(val, imsg.data, sizeof(*val)); + break; + default: + err = got_error(GOT_ERR_PRIVSEP_MSG); + break; + } + + imsg_free(&imsg); + return err; +} + +const struct got_error * got_privsep_unveil_exec_helpers(void) { const char *helpers[] = { @@ -1225,6 +1366,7 @@ got_privsep_unveil_exec_helpers(void) GOT_PATH_PROG_READ_TREE, GOT_PATH_PROG_READ_BLOB, GOT_PATH_PROG_READ_TAG, + GOT_PATH_PROG_READ_GITCONFIG, }; int i; @@ -1236,3 +1378,27 @@ got_privsep_unveil_exec_helpers(void) return NULL; } + +void +got_privsep_exec_child(int imsg_fds[2], const char *path, const char *repo_path) +{ + if (close(imsg_fds[0]) != 0) { + fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno)); + _exit(1); + } + + if (dup2(imsg_fds[1], GOT_IMSG_FD_CHILD) == -1) { + fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno)); + _exit(1); + } + if (closefrom(GOT_IMSG_FD_CHILD + 1) == -1) { + fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno)); + _exit(1); + } + + if (execl(path, path, repo_path, (char *)NULL) == -1) { + fprintf(stderr, "%s: %s: %s\n", getprogname(), path, + strerror(errno)); + _exit(1); + } +} blob - dbee69ba57b1497c455672f6741600c07c1f5af1 blob + 454319ad653bfd26ee86f6f74aa4302c19018868 --- lib/repository.c +++ lib/repository.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,6 @@ #include "got_lib_sha1.h" #include "got_lib_object_cache.h" #include "got_lib_repository.h" -#include "got_lib_gitconfig.h" #ifndef nitems #define nitems(_a) (sizeof(_a) / sizeof((_a)[0])) @@ -88,6 +88,19 @@ got_repo_get_path_git_dir(struct got_repository *repo) return repo->path_git_dir; } +const char * +got_repo_get_gitconfig_author_name(struct got_repository *repo) +{ + return repo->gitconfig_author_name; +} + +/* Obtain the commit author email address parsed from gitconfig. */ +const char * +got_repo_get_gitconfig_author_email(struct got_repository *repo) +{ + return repo->gitconfig_author_email; +} + int got_repo_is_bare(struct got_repository *repo) { @@ -333,7 +346,112 @@ done: repo->path = NULL; free(repo->path_git_dir); repo->path_git_dir = NULL; + } + return err; +} + +static const struct got_error * +read_gitconfig(struct got_repository *repo) +{ + const struct got_error *err = NULL, *child_err = NULL; + char *gitconfig_path = NULL; + int fd = -1; + int imsg_fds[2] = { -1, -1 }; + pid_t pid; + struct imsgbuf *ibuf; + + /* TODO: Read settings from ~/.gitconfig as well? */ + + /* Read repository's .git/config file. */ + err = get_path_gitconfig(&gitconfig_path, repo); + if (err) + return err; + + fd = open(gitconfig_path, O_RDONLY); + if (fd == -1) { + if (errno == ENOENT) { + free(gitconfig_path); + return NULL; + } + err = got_error_from_errno2("open", gitconfig_path); + free(gitconfig_path); + return err; + } + + ibuf = calloc(1, sizeof(*ibuf)); + if (ibuf == NULL) { + err = got_error_from_errno("calloc"); + goto done; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) { + err = got_error_from_errno("socketpair"); + goto done; + } + + pid = fork(); + if (pid == -1) { + err = got_error_from_errno("fork"); + goto done; + } else if (pid == 0) { + got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_GITCONFIG, + repo->path); + /* not reached */ + } + + if (close(imsg_fds[1]) == -1) { + err = got_error_from_errno("close"); + goto done; } + imsg_fds[1] = -1; + imsg_init(ibuf, imsg_fds[0]); + + err = got_privsep_send_gitconfig_parse_req(ibuf, fd); + if (err) + goto done; + fd = -1; + + err = got_privsep_send_gitconfig_repository_format_version_req(ibuf); + if (err) + goto done; + + err = got_privsep_recv_gitconfig_int( + &repo->gitconfig_repository_format_version, ibuf); + if (err) + goto done; + + err = got_privsep_send_gitconfig_author_name_req(ibuf); + if (err) + goto done; + + err = got_privsep_recv_gitconfig_str(&repo->gitconfig_author_name, + ibuf); + if (err) + goto done; + + err = got_privsep_send_gitconfig_author_email_req(ibuf); + if (err) + goto done; + + err = got_privsep_recv_gitconfig_str(&repo->gitconfig_author_email, + ibuf); + if (err) + goto done; + + imsg_clear(ibuf); + err = got_privsep_send_stop(imsg_fds[0]); + child_err = got_privsep_wait_for_child(pid); + if (child_err && err == NULL) + err = child_err; +done: + if (imsg_fds[0] != -1 && close(imsg_fds[0]) == -1 && err == NULL) + err = got_error_from_errno("close"); + if (imsg_fds[1] != -1 && close(imsg_fds[1]) == -1 && err == NULL) + err = got_error_from_errno("close"); + if (fd != -1 && close(fd) == -1 && err == NULL) + err = got_error_from_errno2("close", gitconfig_path); + free(gitconfig_path); + free(ibuf); return err; } @@ -342,7 +460,7 @@ got_repo_open(struct got_repository **repop, const cha { struct got_repository *repo = NULL; const struct got_error *err = NULL; - char *abspath, *gitconfig_path = NULL; + char *abspath; int i, tried_root = 0; *repop = NULL; @@ -409,24 +527,17 @@ got_repo_open(struct got_repository **repop, const cha } } while (path); - err = get_path_gitconfig(&gitconfig_path, repo); + err = read_gitconfig(repo); if (err) goto done; - -#ifdef notyet - err = got_gitconfig_open(&repo->gitconfig, gitconfig_path); - if (err) - goto done; -#else - repo->gitconfig = NULL; -#endif + if (repo->gitconfig_repository_format_version != 0) + err = got_error_path(path, GOT_ERR_GIT_REPO_FORMAT); done: if (err) got_repo_close(repo); else *repop = repo; free(abspath); - free(gitconfig_path); return err; } @@ -470,8 +581,9 @@ got_repo_close(struct got_repository *repo) err == NULL) err = got_error_from_errno("close"); } - if (repo->gitconfig) - got_gitconfig_close(repo->gitconfig); + + free(repo->gitconfig_author_name); + free(repo->gitconfig_author_email); free(repo); return err; blob - f56d5677dd571b5ac1902a52d0f39389be75da6f blob + a4c900b7f50d8d643524becc373f46951d405243 --- libexec/Makefile +++ libexec/Makefile @@ -1,4 +1,4 @@ SUBDIR = got-read-blob got-read-commit got-read-object got-read-tree \ - got-read-tag got-read-pack + got-read-tag got-read-pack got-read-gitconfig .include blob - /dev/null blob + f49bf06548491001d3b2fbc4da15ac568171dc17 (mode 644) --- /dev/null +++ libexec/got-read-gitconfig/Makefile @@ -0,0 +1,13 @@ +.PATH:${.CURDIR}/../../lib + +.include "../../got-version.mk" + +PROG= got-read-gitconfig +SRCS= got-read-gitconfig.c error.c inflate.c object_parse.c \ + path.c privsep.c sha1.c gitconfig.c + +CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib +LDADD = -lutil -lz +DPADD = ${LIBZ} ${LIBUTIL} + +.include blob - /dev/null blob + c14f37dd21a67e7027328b8a776e99a036726c7c (mode 644) --- /dev/null +++ libexec/got-read-gitconfig/got-read-gitconfig.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "got_error.h" +#include "got_object.h" + +#include "got_lib_delta.h" +#include "got_lib_object.h" +#include "got_lib_privsep.h" +#include "got_lib_gitconfig.h" + +static volatile sig_atomic_t sigint_received; + +static void +catch_sigint(int signo) +{ + sigint_received = 1; +} + +static const struct got_error * +gitconfig_num_request(struct imsgbuf *ibuf, struct got_gitconfig *gitconfig, + char *section, char *tag, int def) +{ + int value; + + if (gitconfig == NULL) + return got_error(GOT_ERR_PRIVSEP_MSG); + + value = got_gitconfig_get_num(gitconfig, section, tag, def); + return got_privsep_send_gitconfig_int(ibuf, value); +} + +static const struct got_error * +gitconfig_str_request(struct imsgbuf *ibuf, struct got_gitconfig *gitconfig, + char *section, char *tag) +{ + char *value; + + if (gitconfig == NULL) + return got_error(GOT_ERR_PRIVSEP_MSG); + + value = got_gitconfig_get_str(gitconfig, section, tag); + return got_privsep_send_gitconfig_str(ibuf, value); +} + +int +main(int argc, char *argv[]) +{ + const struct got_error *err = NULL; + struct imsgbuf ibuf; + size_t datalen; + struct got_gitconfig *gitconfig = NULL; +#if 0 + static int attached; + + while (!attached) + sleep(1); +#endif + signal(SIGINT, catch_sigint); + + imsg_init(&ibuf, GOT_IMSG_FD_CHILD); + +#ifndef PROFILE + /* revoke access to most system calls */ + if (pledge("stdio recvfd", NULL) == -1) { + err = got_error_from_errno("pledge"); + got_privsep_send_error(&ibuf, err); + return 1; + } +#endif + + for (;;) { + struct imsg imsg; + + memset(&imsg, 0, sizeof(imsg)); + imsg.fd = -1; + + if (sigint_received) { + err = got_error(GOT_ERR_CANCELLED); + break; + } + + err = got_privsep_recv_imsg(&imsg, &ibuf, 0); + if (err) { + if (err->code == GOT_ERR_PRIVSEP_PIPE) + err = NULL; + break; + } + + if (imsg.hdr.type == GOT_IMSG_STOP) + break; + + switch (imsg.hdr.type) { + case GOT_IMSG_GITCONFIG_PARSE_REQUEST: + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + if (datalen != 0) { + err = got_error(GOT_ERR_PRIVSEP_LEN); + break; + } + if (imsg.fd == -1){ + err = got_error(GOT_ERR_PRIVSEP_NO_FD); + break; + } + + if (gitconfig) + got_gitconfig_close(gitconfig); + err = got_gitconfig_open(&gitconfig, imsg.fd); + break; + case GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST: + err = gitconfig_num_request(&ibuf, gitconfig, "core", + "repositoryformatversion", 0); + break; + case GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST: + err = gitconfig_str_request(&ibuf, gitconfig, "user", + "name"); + break; + case GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST: + err = gitconfig_str_request(&ibuf, gitconfig, "user", + "email"); + break; + default: + err = got_error(GOT_ERR_PRIVSEP_MSG); + break; + } + + if (imsg.fd != -1) { + if (close(imsg.fd) == -1 && err == NULL) + err = got_error_from_errno("close"); + } + + imsg_free(&imsg); + if (err) + break; + } + + imsg_clear(&ibuf); + if (err) { + if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) { + fprintf(stderr, "%s: %s\n", getprogname(), err->msg); + got_privsep_send_error(&ibuf, err); + } + } + if (close(GOT_IMSG_FD_CHILD) != 0 && err == NULL) + err = got_error_from_errno("close"); + return err ? 1 : 0; +} blob - 451b7e6f6979220b6f28c544d8bac7eeb6380112 blob + 55340c74b234a5573505c25f1137cc947e5d43d5 --- regress/cmdline/commit.sh +++ regress/cmdline/commit.sh @@ -606,7 +606,45 @@ function test_commit_tree_entry_sorting { ret="$?" test_done "$testroot" "$ret" } + +function test_commit_gitconfig_author { + local testroot=`test_init commit_gitconfig_author` + got checkout $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/repo && git config user.name 'Flan Luck') + (cd $testroot/repo && git config user.email 'flan_luck@openbsd.org') + + echo "modified alpha" > $testroot/wt/alpha + (cd $testroot/wt && got commit -m 'test gitconfig author' > /dev/null) + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/repo && got log -l1 | grep ^from: > $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + echo "from: Flan Luck " \ + > $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + run_test test_commit_basic run_test test_commit_new_subdir run_test test_commit_subdir @@ -622,3 +660,4 @@ run_test test_commit_selected_paths run_test test_commit_outside_refs_heads run_test test_commit_no_email run_test test_commit_tree_entry_sorting +run_test test_commit_gitconfig_author blob - c4fa14f3feb7a9f4a7b01276e5b724b478cab932 blob + 85f9a8965833c7ec25df706646762df09e50327b --- tog/Makefile +++ tog/Makefile @@ -8,7 +8,7 @@ SRCS= tog.c blame.c commit_graph.c delta.c diff.c \ object_idset.c object_parse.c opentemp.c path.c pack.c \ privsep.c reference.c repository.c sha1.c worktree.c \ utf8.c inflate.c buf.c worklist.c rcsutil.c diff3.c \ - lockfile.c deflate.c object_create.c gitconfig.c + lockfile.c deflate.c object_create.c MAN = ${PROG}.1 CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib