"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
unveil vs. editor
To:
gameoftrees@openbsd.org
Date:
Thu, 28 Mar 2024 11:27:11 +0100

Download raw body.

Thread
  • Stefan Sperling:

    unveil vs. editor

During a conversation with Theo at g2k19 I somehow became convinced
that Got should avoid calling unveil before launching an editor.

The code I had written initially didn't make this assumption. So I
already knew at the time that running an editor would work just fine
with unveil already set up. But I was cautioned about future changes
to how unveil would operate.
Either the design of unveil wasn't quite finalized yet at that point,
or I misunderstood what Theo was trying to get across (no surprise,
that happens quite often ;)

Either way, this removes all the lies about unveil from got/got.c.
The issue persists in cvg/cvg.c but that can be fixed later (cvg has
no regression tests yet).

Tests are passing and some manual testing suggests interactive use
of the editor still works as expected.

ok?

-----------------------------------------------
 
 call unveil earlier in 'got import'
 
 We now know that unveil(2) will never traverse exec.
 No need to wait with unveil until the editor has been run.
 
diff 87890bc26c1c6958bd64bb9d46fbc29ba6a92d95 b01d8dca1ccae2fac6fc8a6b5ab03d47d0cb9e2e
commit - 87890bc26c1c6958bd64bb9d46fbc29ba6a92d95
commit + b01d8dca1ccae2fac6fc8a6b5ab03d47d0cb9e2e
blob - eb068e10c4f52020498b05f1af70fa1d2f6d5ee8
blob + be364f33ae558187761d281438ee798b41d5c990
--- got/got.c
+++ got/got.c
@@ -834,6 +834,29 @@ cmd_import(int argc, char *argv[])
 	if (error)
 		goto done;
 
+	path_dir = realpath(argv[0], NULL);
+	if (path_dir == NULL) {
+		error = got_error_from_errno2("realpath", argv[0]);
+		goto done;
+	}
+	got_path_strip_trailing_slashes(path_dir);
+
+	error = get_editor(&editor);
+	if (error)
+		goto done;
+
+	if (unveil(path_dir, "r") != 0) {
+		error = got_error_from_errno2("unveil", path_dir);
+		goto done;
+	}
+	if (unveil(editor, "x") != 0) {
+		error = got_error_from_errno2("unveil", editor);
+		goto done;
+	}
+	error = apply_unveil(got_repo_get_path(repo), 0, NULL);
+	if (error)
+		goto done;
+
 	error = get_author(&author, repo, NULL);
 	if (error)
 		return error;
@@ -872,21 +895,7 @@ cmd_import(int argc, char *argv[])
 		goto done;
 	}
 
-	path_dir = realpath(argv[0], NULL);
-	if (path_dir == NULL) {
-		error = got_error_from_errno2("realpath", argv[0]);
-		goto done;
-	}
-	got_path_strip_trailing_slashes(path_dir);
-
-	/*
-	 * unveil(2) traverses exec(2); if an editor is used we have
-	 * to apply unveil after the log message has been written.
-	 */
 	if (logmsg == NULL || *logmsg == '\0') {
-		error = get_editor(&editor);
-		if (error)
-			goto done;
 		free(logmsg);
 		error = collect_import_msg(&logmsg, &logmsg_path, editor,
 		    path_dir, refname);
@@ -898,20 +907,6 @@ cmd_import(int argc, char *argv[])
 		}
 	}
 
-	if (unveil(path_dir, "r") != 0) {
-		error = got_error_from_errno2("unveil", path_dir);
-		if (logmsg_path)
-			preserve_logmsg = 1;
-		goto done;
-	}
-
-	error = apply_unveil(got_repo_get_path(repo), 0, NULL);
-	if (error) {
-		if (logmsg_path)
-			preserve_logmsg = 1;
-		goto done;
-	}
-
 	error = got_repo_import(&new_commit_id, path_dir, logmsg,
 	    author, &ignores, repo, import_progress, NULL);
 	if (error) {

-----------------------------------------------
 
 call unveil earlier in 'got commit'
 
 We now know that unveil(2) will never traverse exec.
 No need to wait with unveil until the editor has been run.
 
diff b01d8dca1ccae2fac6fc8a6b5ab03d47d0cb9e2e e9005b2dca2feab760702c966d0c9b8ee754a693
commit - b01d8dca1ccae2fac6fc8a6b5ab03d47d0cb9e2e
commit + e9005b2dca2feab760702c966d0c9b8ee754a693
blob - be364f33ae558187761d281438ee798b41d5c990
blob + a3d939c4058c0a946db7bdf78bfdf2c4cb46dffe
--- got/got.c
+++ got/got.c
@@ -9150,10 +9150,6 @@ done:
 
 	if (fd != -1 && close(fd) == -1 && err == NULL)
 		err = got_error_from_errno2("close", a->logmsg_path);
-
-	/* Editor is done; we can now apply unveil(2) */
-	if (err == NULL)
-		err = apply_unveil(a->repo_path, 0, a->worktree_path);
 	if (err) {
 		free(*logmsg);
 		*logmsg = NULL;
@@ -9423,15 +9419,18 @@ cmd_commit(int argc, char *argv[])
 	if (author == NULL)
 		author = committer;
 
-	/*
-	 * unveil(2) traverses exec(2); if an editor is used we have
-	 * to apply unveil after the log message has been written.
-	 */
-	if (logmsg == NULL || strlen(logmsg) == 0)
+	if (logmsg == NULL || strlen(logmsg) == 0) {
 		error = get_editor(&editor);
-	else
-		error = apply_unveil(got_repo_get_path(repo), 0,
-		    got_worktree_get_root_path(worktree));
+		if (error)
+			goto done;
+		if (unveil(editor, "x") != 0) {
+			error = got_error_from_errno2("unveil", editor);
+			goto done;
+		}
+	}
+
+	error = apply_unveil(got_repo_get_path(repo), 0,
+	    got_worktree_get_root_path(worktree));
 	if (error)
 		goto done;
 

-----------------------------------------------
 
 call unveil earlier in 'got histedit'
 
 We now know that unveil(2) will never traverse exec.
 No need to wait with unveil until the editor has been run.
 
diff e9005b2dca2feab760702c966d0c9b8ee754a693 d84895356b2dbc532cec559f78c7cc9012ed2c6d
commit - e9005b2dca2feab760702c966d0c9b8ee754a693
commit + d84895356b2dbc532cec559f78c7cc9012ed2c6d
blob - a3d939c4058c0a946db7bdf78bfdf2c4cb46dffe
blob + 1894bb3e86cc2dba58804185cb071b246b95f9f5
--- got/got.c
+++ got/got.c
@@ -11975,10 +11975,10 @@ get_folded_commits(struct got_histedit_list_entry *hle
 
 static const struct got_error *
 histedit_edit_logmsg(struct got_histedit_list_entry *hle,
-    struct got_repository *repo)
+    const char *editor, struct got_repository *repo)
 {
 	char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
-	char *logmsg = NULL, *new_msg = NULL, *editor = NULL;
+	char *logmsg = NULL, *new_msg = NULL;
 	const struct got_error *err = NULL;
 	struct got_commit_object *commit = NULL;
 	int logmsg_len;
@@ -12041,10 +12041,6 @@ histedit_edit_logmsg(struct got_histedit_list_entry *h
 	}
 	fd = -1;
 
-	err = get_editor(&editor);
-	if (err)
-		goto done;
-
 	err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg,
 	    logmsg_len, 0);
 	if (err) {
@@ -12063,7 +12059,6 @@ done:
 	free(logmsg_path);
 	free(logmsg);
 	free(orig_logmsg);
-	free(editor);
 	if (commit)
 		got_object_commit_close(commit);
 	return err;
@@ -12204,19 +12199,14 @@ histedit_check_script(struct got_histedit_list *histed
 
 static const struct got_error *
 histedit_run_editor(struct got_histedit_list *histedit_cmds,
-    const char *path, struct got_object_id_queue *commits,
-    struct got_repository *repo)
+    const char *editor, const char *path,
+    struct got_object_id_queue *commits, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct stat st, st2;
 	struct timespec timeout;
-	char *editor;
 	FILE *f = NULL;
 
-	err = get_editor(&editor);
-	if (err)
-		return err;
-
 	if (stat(path, &st) == -1) {
 		err = got_error_from_errno2("stat", path);
 		goto done;
@@ -12256,20 +12246,19 @@ histedit_run_editor(struct got_histedit_list *histedit
 done:
 	if (f && fclose(f) == EOF && err == NULL)
 		err = got_error_from_errno("fclose");
-	free(editor);
 	return err;
 }
 
 static const struct got_error *
 histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
-    struct got_object_id_queue *, const char *, const char *,
+    struct got_object_id_queue *, const char *, const char *, const char *,
     struct got_repository *);
 
 static const struct got_error *
 histedit_edit_script(struct got_histedit_list *histedit_cmds,
     struct got_object_id_queue *commits, const char *branch_name,
     int edit_logmsg_only, int fold_only, int drop_only, int edit_only,
-    struct got_repository *repo)
+    const char *editor, struct got_repository *repo)
 {
 	const struct got_error *err;
 	FILE *f = NULL;
@@ -12297,13 +12286,14 @@ histedit_edit_script(struct got_histedit_list *histedi
 			goto done;
 		}
 		f = NULL;
-		err = histedit_run_editor(histedit_cmds, path, commits, repo);
+		err = histedit_run_editor(histedit_cmds, editor, path,
+		    commits, repo);
 		if (err) {
 			if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
 			    err->code != GOT_ERR_HISTEDIT_CMD)
 				goto done;
 			err = histedit_edit_list_retry(histedit_cmds, err,
-			    commits, path, branch_name, repo);
+			    commits, editor, path, branch_name, repo);
 		}
 	}
 done:
@@ -12380,7 +12370,8 @@ done:
 static const struct got_error *
 histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
     const struct got_error *edit_err, struct got_object_id_queue *commits,
-    const char *path, const char *branch_name, struct got_repository *repo)
+    const char *editor, const char *path, const char *branch_name,
+    struct got_repository *repo)
 {
 	const struct got_error *err = NULL, *prev_err = edit_err;
 	int resp = ' ';
@@ -12393,8 +12384,8 @@ histedit_edit_list_retry(struct got_histedit_list *his
 			resp = getchar();
 		if (resp == 'c') {
 			histedit_free_list(histedit_cmds);
-			err = histedit_run_editor(histedit_cmds, path, commits,
-			    repo);
+			err = histedit_run_editor(histedit_cmds, editor, path,
+			    commits, repo);
 			if (err) {
 				if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
 				    err->code != GOT_ERR_HISTEDIT_CMD)
@@ -12407,7 +12398,7 @@ histedit_edit_list_retry(struct got_histedit_list *his
 		} else if (resp == 'r') {
 			histedit_free_list(histedit_cmds);
 			err = histedit_edit_script(histedit_cmds,
-			    commits, branch_name, 0, 0, 0, 0, repo);
+			    commits, branch_name, 0, 0, 0, 0, editor, repo);
 			if (err) {
 				if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
 				    err->code != GOT_ERR_HISTEDIT_CMD)
@@ -12497,7 +12488,8 @@ static const struct got_error *
 histedit_commit(struct got_pathlist_head *merged_paths,
     struct got_worktree *worktree, struct got_fileindex *fileindex,
     struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
-    const char *committer, int allow_conflict, struct got_repository *repo)
+    const char *committer, int allow_conflict, const char *editor,
+    struct got_repository *repo)
 {
 	const struct got_error *err;
 	struct got_commit_object *commit;
@@ -12505,7 +12497,7 @@ histedit_commit(struct got_pathlist_head *merged_paths
 
 	if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
 	    && hle->logmsg == NULL) {
-		err = histedit_edit_logmsg(hle, repo);
+		err = histedit_edit_logmsg(hle, editor, repo);
 		if (err)
 			return err;
 	}
@@ -12603,6 +12595,7 @@ cmd_histedit(int argc, char *argv[])
 	int drop_only = 0, edit_logmsg_only = 0, fold_only = 0, edit_only = 0;
 	int allow_conflict = 0, list_backups = 0, delete_backups = 0;
 	const char *edit_script_path = NULL;
+	char *editor = NULL;
 	struct got_object_id_queue commits;
 	struct got_pathlist_head merged_paths;
 	const struct got_object_id_queue *parent_ids;
@@ -12758,15 +12751,6 @@ cmd_histedit(int argc, char *argv[])
 	else if (argc != 0)
 		usage_histedit();
 
-	/*
-	 * This command cannot apply unveil(2) in all cases because the
-	 * user may choose to run an editor to edit the histedit script
-	 * and to edit individual commit log messages.
-	 * unveil(2) traverses exec(2); if an editor is used we have to
-	 * apply unveil after edit script and log messages have been written.
-	 * XXX TODO: Make use of unveil(2) where possible.
-	 */
-
 	cwd = getcwd(NULL, 0);
 	if (cwd == NULL) {
 		error = got_error_from_errno("getcwd");
@@ -12804,16 +12788,34 @@ cmd_histedit(int argc, char *argv[])
 		    GOT_WORKTREE_HISTEDIT_BACKUP_REF_PREFIX,
 		    argc == 1 ? argv[0] : NULL, delete_backups, repo);
 		goto done; /* nothing else to do */
+	} else {
+		error = get_gitconfig_path(&gitconfig_path);
+		if (error)
+			goto done;
+		error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
+		    gitconfig_path, pack_fds);
+		if (error != NULL)
+			goto done;
+		error = get_editor(&editor);
+		if (error)
+			goto done;
+		if (unveil(editor, "x") != 0) {
+			error = got_error_from_errno2("unveil", editor);
+			goto done;
+		}
+		if (edit_script_path) {
+			if (unveil(edit_script_path, "r") != 0) {
+				error = got_error_from_errno2("unveil",
+				    edit_script_path);
+				goto done;
+			}
+		}
+		error = apply_unveil(got_repo_get_path(repo), 0,
+		    got_worktree_get_root_path(worktree));
+		if (error)
+			goto done;
 	}
 
-	error = get_gitconfig_path(&gitconfig_path);
-	if (error)
-		goto done;
-	error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
-	    gitconfig_path, pack_fds);
-	if (error != NULL)
-		goto done;
-
 	if (worktree != NULL && !list_backups && !delete_backups) {
 		error = worktree_has_logmsg_ref("histedit", worktree, repo);
 		if (error)
@@ -13009,7 +13011,7 @@ cmd_histedit(int argc, char *argv[])
 				branch_name += 11;
 			error = histedit_edit_script(&histedit_cmds, &commits,
 			    branch_name, edit_logmsg_only, fold_only,
-			    drop_only, edit_only, repo);
+			    drop_only, edit_only, editor, repo);
 			if (error) {
 				got_worktree_histedit_abort(worktree, fileindex,
 				    repo, branch, base_commit_id,
@@ -13071,7 +13073,7 @@ cmd_histedit(int argc, char *argv[])
 				if (have_changes) {
 					error = histedit_commit(NULL, worktree,
 					    fileindex, tmp_branch, hle,
-					    committer, allow_conflict, repo);
+					    committer, allow_conflict, editor, repo);
 					if (error)
 						goto done;
 				} else {
@@ -13142,13 +13144,13 @@ cmd_histedit(int argc, char *argv[])
 				goto done;
 			continue;
 		} else if (hle->cmd->code == GOT_HISTEDIT_MESG) {
-			error = histedit_edit_logmsg(hle, repo);
+			error = histedit_edit_logmsg(hle, editor, repo);
 			if (error)
 				goto done;
 		}
 
 		error = histedit_commit(&merged_paths, worktree, fileindex,
-		    tmp_branch, hle, committer, allow_conflict, repo);
+		    tmp_branch, hle, committer, allow_conflict, editor, repo);
 		got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
 		if (error)
 			goto done;
@@ -13183,6 +13185,7 @@ cmd_histedit(int argc, char *argv[])
 		    branch, repo);
 done:
 	free(cwd);
+	free(editor);
 	free(committer);
 	free(gitconfig_path);
 	got_object_id_queue_free(&commits);

-----------------------------------------------
 
 call unveil earlier in 'got tag'
 
 We now know that unveil(2) will never traverse exec.
 No need to wait with unveil until the editor has been run.
 
diff d84895356b2dbc532cec559f78c7cc9012ed2c6d a9f25ba432efbc09d7f521f9a95bee860d168b54
commit - d84895356b2dbc532cec559f78c7cc9012ed2c6d
commit + a9f25ba432efbc09d7f521f9a95bee860d168b54
blob - 1894bb3e86cc2dba58804185cb071b246b95f9f5
blob + 0685c98825a1a25a87beda8b3db3f24546a7fab3
--- got/got.c
+++ got/got.c
@@ -7547,11 +7547,10 @@ done:
 
 static const struct got_error *
 get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
-    const char *tag_name, const char *repo_path)
+    const char *tag_name, const char *editor, const char *repo_path)
 {
 	const struct got_error *err = NULL;
 	char *template = NULL, *initial_content = NULL;
-	char *editor = NULL;
 	int initial_content_len;
 	int fd = -1;
 
@@ -7582,15 +7581,11 @@ get_tag_message(char **tagmsg, char **tagmsg_path, con
 	}
 	fd = -1;
 
-	err = get_editor(&editor);
-	if (err)
-		goto done;
 	err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content,
 	    initial_content_len, 1);
 done:
 	free(initial_content);
 	free(template);
-	free(editor);
 
 	if (fd != -1 && close(fd) == -1 && err == NULL)
 		err = got_error_from_errno2("close", *tagmsg_path);
@@ -7605,7 +7600,7 @@ done:
 static const struct got_error *
 add_tag(struct got_repository *repo, const char *tagger,
     const char *tag_name, const char *commit_arg, const char *tagmsg_arg,
-    const char *signer_id, int verbosity)
+    const char *signer_id, const char *editor, int verbosity)
 {
 	const struct got_error *err = NULL;
 	struct got_object_id *commit_id = NULL, *tag_id = NULL;
@@ -7654,20 +7649,13 @@ add_tag(struct got_repository *repo, const char *tagge
 
 	if (tagmsg_arg == NULL) {
 		err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
-		    tag_name, got_repo_get_path(repo));
+		    tag_name, editor, got_repo_get_path(repo));
 		if (err) {
 			if (err->code != GOT_ERR_COMMIT_MSG_EMPTY &&
 			    tagmsg_path != NULL)
 				preserve_tagmsg = 1;
 			goto done;
 		}
-		/* Editor is done; we can now apply unveil(2) */
-		err = got_sigs_apply_unveil();
-		if (err)
-			goto done;
-		err = apply_unveil(got_repo_get_path(repo), 0, NULL);
-		if (err)
-			goto done;
 	}
 
 	err = got_object_tag_create(&tag_id, tag_name, commit_id,
@@ -7726,7 +7714,7 @@ cmd_tag(int argc, char *argv[])
 	struct got_worktree *worktree = NULL;
 	char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL;
 	char *gitconfig_path = NULL, *tagger = NULL, *keyword_idstr = NULL;
-	char *allowed_signers = NULL, *revoked_signers = NULL;
+	char *allowed_signers = NULL, *revoked_signers = NULL, *editor = NULL;
 	const char *signer_id = NULL;
 	const char *tag_name = NULL, *commit_id_arg = NULL, *tagmsg = NULL;
 	int ch, do_list = 0, verify_tags = 0, verbosity = 0;
@@ -7885,16 +7873,23 @@ cmd_tag(int argc, char *argv[])
 		if (signer_id == NULL)
 			signer_id = get_signer_id(repo, worktree);
 
-		if (tagmsg) {
-			if (signer_id) {
-				error = got_sigs_apply_unveil();
-				if (error)
-					goto done;
+		if (tagmsg == NULL) {
+			error = get_editor(&editor);
+			if (error)
+				goto done;
+			if (unveil(editor, "x") != 0) {
+				error = got_error_from_errno2("unveil", editor);
+				goto done;
 			}
-			error = apply_unveil(got_repo_get_path(repo), 0, NULL);
+		}
+		if (signer_id) {
+			error = got_sigs_apply_unveil();
 			if (error)
 				goto done;
 		}
+		error = apply_unveil(got_repo_get_path(repo), 0, NULL);
+		if (error)
+			goto done;
 
 		if (commit_id_arg == NULL) {
 			struct got_reference *head_ref;
@@ -7928,7 +7923,7 @@ cmd_tag(int argc, char *argv[])
 
 		error = add_tag(repo, tagger, tag_name,
 		    commit_id_str ? commit_id_str : commit_id_arg, tagmsg,
-		    signer_id, verbosity);
+		    signer_id, editor, verbosity);
 	}
 done:
 	if (repo) {
@@ -7945,6 +7940,7 @@ done:
 			error = pack_err;
 	}
 	free(cwd);
+	free(editor);
 	free(repo_path);
 	free(gitconfig_path);
 	free(commit_id_str);