From: Mark Jamsek Subject: got patch: check x-bit for new files To: Game of Trees Date: Wed, 21 Sep 2022 20:39:52 +1000 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 < $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 < $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 GPG: F2FF 13DE 6A06 C471 CA80 E6E2 2930 DC66 86EE CF68