git: 767c97c501ae - main - install: handle -m +X more accurately

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Thu, 19 Oct 2023 02:44:52 UTC
The branch main has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=767c97c501ae72eb3d74bc23ddf5a1d570d8f841

commit 767c97c501ae72eb3d74bc23ddf5a1d570d8f841
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2023-10-19 02:43:06 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2023-10-19 02:44:05 +0000

    install: handle -m +X more accurately
    
    As described by chmod(1), +X in the mode may be used to optionally set
    the +x bit if the file is a directory if any of the execute/search bits
    are set in the original mode.  The latter is not applicable because we
    assume -m is a fresh mask, but a functional +X could be useful in the
    former case if we're passing along a common INSTALL_MODE that's designed
    to install either 0644 or 0755 depending simply on whether it's a
    directory or not.
    
    Reviewed by:    des
    Sponsored by:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D42273
---
 usr.bin/xinstall/tests/install_test.sh | 16 ++++++++++++++++
 usr.bin/xinstall/xinstall.c            | 12 ++++++++++--
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/usr.bin/xinstall/tests/install_test.sh b/usr.bin/xinstall/tests/install_test.sh
index b42bdfd0dc60..cbe95f0dfbf3 100755
--- a/usr.bin/xinstall/tests/install_test.sh
+++ b/usr.bin/xinstall/tests/install_test.sh
@@ -481,6 +481,21 @@ set_owner_group_mode_unpriv_body() {
 	atf_check_equal "$u:$g:10$cM" "$(stat -f"%u:%g:%p" testc)"
 }
 
+atf_test_case set_optional_exec
+set_optional_exec_head() {
+	atf_set "require.user" "unprivileged"
+}
+set_optional_exec_body()
+{
+	echo "abc" > testfile.src
+
+	atf_check install -d -m ug+rX testdir
+	atf_check test -x testdir
+
+	atf_check install -m ug+rX testfile.src testfile
+	atf_check test ! -x testfile
+}
+
 atf_init_test_cases() {
 	atf_add_test_case copy_to_nonexistent
 	atf_add_test_case copy_to_nonexistent_safe
@@ -523,4 +538,5 @@ atf_init_test_cases() {
 	atf_add_test_case mkdir_simple
 	atf_add_test_case set_owner_group_mode
 	atf_add_test_case set_owner_group_mode_unpriv
+	atf_add_test_case set_optional_exec
 }
diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c
index d19bacbe8c1b..1ab9cac76f2f 100644
--- a/usr.bin/xinstall/xinstall.c
+++ b/usr.bin/xinstall/xinstall.c
@@ -189,6 +189,7 @@ main(int argc, char *argv[])
 
 	fset = 0;
 	iflags = 0;
+	set = NULL;
 	group = owner = NULL;
 	while ((ch = getopt(argc, argv, "B:bCcD:df:g:h:l:M:m:N:o:pSsT:Uv")) !=
 	     -1)
@@ -255,11 +256,10 @@ main(int argc, char *argv[])
 			break;
 		case 'm':
 			haveopt_m = 1;
+			free(set);
 			if (!(set = setmode(optarg)))
 				errx(EX_USAGE, "invalid file mode: %s",
 				     optarg);
-			mode = getmode(set, 0);
-			free(set);
 			break;
 		case 'N':
 			if (!setup_getid(optarg))
@@ -301,6 +301,14 @@ main(int argc, char *argv[])
 		usage();
 	}
 
+	/*
+	 * Default permissions based on whether we're a directory or not, since
+	 * an +X may mean that we need to set the execute bit.
+	 */
+	if (set != NULL)
+		mode = getmode(set, dodir ? S_IFDIR : 0) & ~S_IFDIR;
+	free(set);
+
 	if (getenv("DONTSTRIP") != NULL) {
 		warnx("DONTSTRIP set - will not strip installed binaries");
 		dostrip = 0;