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

From:
Mark Jamsek <mark@jamsek.com>
Subject:
got patch: check x-bit for new files
To:
Game of Trees <gameoftrees@openbsd.org>
Date:
Wed, 21 Sep 2022 20:39:52 +1000

Download raw body.

Thread
Quick recap from IRC:

I recently shared a diff with a new file that had the x-bit set, and
stsp reported that `got patch` failed to set this when applying the
patch.

The problem was, we only carry over file modes from currently versioned
files on disk.  The below diff makes `got patch` parse `{got,git} diff`
patches for file perms so that we apply the x-bit to new files. Includes
regress for both got and git diffs.

diff /home/mark/src/got
commit - 6f04a73d17761092a3a7ab07632cd1a89d4bfd21
path + /home/mark/src/got
blob - 28d77bb457c59dd0fa56e815055a611a3c6fdd07
file + lib/got_lib_privsep.h
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -641,6 +641,7 @@ struct got_imsg_remotes {
  */
 struct got_imsg_patch {
 	int	git;
+	int	xbit;
 	char	old[PATH_MAX];
 	char	new[PATH_MAX];
 	char	cid[41];
blob - 4b795cc39551259dc64fe67a92381bb2c0591143
file + lib/patch.c
--- lib/patch.c
+++ lib/patch.c
@@ -74,6 +74,7 @@ struct got_patch_hunk {
 
 STAILQ_HEAD(got_patch_hunk_head, got_patch_hunk);
 struct got_patch {
+	int	 xbit;
 	char	*old;
 	char	*new;
 	char	 cid[41];
@@ -196,6 +197,8 @@ recv_patch(struct imsgbuf *ibuf, int *done, struct got
 	if (*patch.blob != '\0')
 		strlcpy(p->blob, patch.blob, sizeof(p->blob));
 
+	p->xbit = patch.xbit;
+
 	/* automatically set strip=1 for git-style diffs */
 	if (strip == -1 && patch.git &&
 	    (*patch.old == '\0' || !strncmp(patch.old, "a/", 2)) &&
@@ -833,7 +836,8 @@ apply_patch(int *overlapcnt, struct got_worktree *work
 			goto done;
 		}
 		mode = sb.st_mode;
-	}
+	} else if (p->xbit)
+		mode |= (S_IXUSR | S_IXGRP | S_IXOTH);
 
 	err = got_opentemp_named(&tmppath, &tmpfile, template);
 	if (err)
blob - 73996be84412da8b6da8967a3b861c8e404900d5
file + libexec/got-read-patch/got-read-patch.c
--- libexec/got-read-patch/got-read-patch.c
+++ libexec/got-read-patch/got-read-patch.c
@@ -62,7 +62,7 @@ struct imsgbuf ibuf;
 
 static const struct got_error *
 send_patch(const char *oldname, const char *newname, const char *commitid,
-    const char *blob, int git)
+    const char *blob, const int xbit, int git)
 {
 	struct got_imsg_patch p;
 
@@ -80,6 +80,7 @@ send_patch(const char *oldname, const char *newname, c
 	if (blob != NULL)
 		strlcpy(p.blob, blob, sizeof(p.blob));
 
+	p.xbit = xbit;
 	p.git = git;
 	if (imsg_compose(&ibuf, GOT_IMSG_PATCH, 0, 0, -1, &p, sizeof(p)) == -1)
 		return got_error_from_errno("imsg_compose GOT_IMSG_PATCH");
@@ -127,6 +128,18 @@ filename(const char *at, char **name)
 	return NULL;
 }
 
+static int
+filexbit(const char *line)
+{
+	char *m;
+
+	m = strchr(line, '(');
+	if (m && !strncmp(m + 1, "mode ", 5))
+		return strncmp(m + 6, "755", 3) == 0;
+
+	return 0;
+}
+
 static const struct got_error *
 blobid(const char *line, char **blob, int git)
 {
@@ -199,7 +212,7 @@ find_diff(int *done, int *next, FILE *fp, int git, con
 	char	*line = NULL;
 	size_t	 linesize = 0;
 	ssize_t	 linelen;
-	int	 create, rename = 0;
+	int	 create, rename = 0, xbit = 0;
 
 	*done = 0;
 	*next = 0;
@@ -218,6 +231,8 @@ find_diff(int *done, int *next, FILE *fp, int git, con
 		} else if (!strncmp(line, "+++ ", 4)) {
 			free(new);
 			err = filename(line+4, &new);
+		} else if (!strncmp(line, "blob + ", 7)) {
+			xbit = filexbit(line);
 		} else if (!git && !strncmp(line, "blob - ", 7)) {
 			free(blob);
 			err = blobid(line + 7, &blob, git);
@@ -226,6 +241,8 @@ find_diff(int *done, int *next, FILE *fp, int git, con
 			err = filename(line + 10, &new);
 		} else if (git && !strncmp(line, "similarity index 100%", 21))
 			rename = 1;
+		else if (git && !strncmp(line, "new file mode 100", 17))
+			xbit = strncmp(line + 17, "755", 3) == 0;
 		else if (git && !strncmp(line, "index ", 6)) {
 			free(blob);
 			err = blobid(line + 6, &blob, git);
@@ -248,7 +265,7 @@ find_diff(int *done, int *next, FILE *fp, int git, con
 		if (rename && old != NULL && new != NULL) {
 			*done = 1;
 			err = send_patch(old, new, commitid,
-			    blob, git);
+			    blob, xbit, git);
 			break;
 		}
 
@@ -259,7 +276,7 @@ find_diff(int *done, int *next, FILE *fp, int git, con
 				err = got_error(GOT_ERR_PATCH_MALFORMED);
 			else
 				err = send_patch(old, new, commitid,
-				    blob, git);
+				    blob, xbit, git);
 
 			if (err)
 				break;
blob - 1854b36c0fa1e88ead530f05e4b4011f623a055c
file + regress/cmdline/patch.sh
--- regress/cmdline/patch.sh
+++ regress/cmdline/patch.sh
@@ -1763,6 +1763,93 @@ EOF
 	test_done $testroot $ret
 }
 
+test_patch_newfile_xbit_got_diff() {
+	local testroot=`test_init patch_newfile_xbit`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done $testroot $ret
+		return 1
+	fi
+
+	cat <<EOF > $testroot/wt/patch
+blob - /dev/null
+blob + abcdef0123456789abcdef012345678901234567 (mode 755)
+--- /dev/null
++++ xfile
+@@ -0,0 +1,1 @@
++xfile
+EOF
+
+	(cd $testroot/wt && got patch patch) > /dev/null
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done $testroot $ret
+		return 1
+	fi
+
+	if [ ! -x $testroot/wt/xfile ]; then
+		echo "failed to set xbit on newfile" >&2
+		test_done $testroot 1
+		return 1
+	fi
+
+	echo xfile > $testroot/wt/xfile.expected
+	cmp -s $testroot/wt/xfile $testroot/wt/xfile.expected
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo "fail"
+		diff -u $testroot/wt/xfile $testroot/wt/xfile.expected
+	fi
+
+	test_done $testroot $ret
+}
+
+test_patch_newfile_xbit_git_diff() {
+	local testroot=`test_init patch_newfile_xbit`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done $testroot $ret
+		return 1
+	fi
+
+	cat <<EOF > $testroot/wt/patch
+diff --git a/xfile b/xfile
+new file mode 100755
+index 00000000..abcdef01
+--- /dev/null
++++ b/xfile
+@@ -0,0 +1,1 @@
++xfile
+EOF
+
+	(cd $testroot/wt && got patch patch) > /dev/null
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done $testroot $ret
+		return 1
+	fi
+
+	if [ ! -x $testroot/wt/xfile ]; then
+		echo "failed to set xbit on newfile" >&2
+		test_done $testroot 1
+		return 1
+	fi
+
+	echo xfile > $testroot/wt/xfile.expected
+	cmp -s $testroot/wt/xfile $testroot/wt/xfile.expected
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		echo "fail"
+		diff -u $testroot/wt/xfile $testroot/wt/xfile.expected
+	fi
+
+	test_done $testroot $ret
+}
+
 test_parseargs "$@"
 run_test test_patch_basic
 run_test test_patch_dont_apply
@@ -1789,3 +1876,5 @@ run_test test_patch_merge_base_provided
 run_test test_patch_merge_conflict
 run_test test_patch_merge_unknown_blob
 run_test test_patch_merge_reverse
+run_test test_patch_newfile_xbit_got_diff
+run_test test_patch_newfile_xbit_git_diff

-- 
Mark Jamsek <fnc.bsdbox.org>
GPG: F2FF 13DE 6A06 C471 CA80  E6E2 2930 DC66 86EE CF68