commit 37e766f45c2e1cc637f4b0a0be03f125e65f8ebd from: Mark Jamsek via: Thomas Adam date: Wed Sep 21 13:26:18 2022 UTC ensure got patch respects x-bit perms for new files Reported by stsp on IRC: got patch failed to set the x-bit for a new file despite got diff recording mode 755. Parse got and git diffs for this data and set file modes accordingly. Tweaked with hint from op. ok stsp@ commit - 1996dd729051837a7a27478ca4db7879691b3be9 commit + 37e766f45c2e1cc637f4b0a0be03f125e65f8ebd blob - 28231bbf9bfed76cccd642d016636c357a469dee blob + 83e7c1339c1b5f78266924cf2f94e4a9cfcae8da --- 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 - 1dac0c931a643d7ea49af837d8b4af3d5c7e7f61 blob + 7da6f3ed987765fc1335933affad2032771fd8a6 --- lib/patch.c +++ lib/patch.c @@ -72,6 +72,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]; @@ -193,6 +194,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 && @@ -831,7 +834,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 - 35166a4635c2ff0fb604cc64b1b8df2669a70358 blob + 2f96f78243ecc7610927ef5df3b037cb40f8b7d2 --- 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 blob + 1bcdb584a653439ff381dc63d0dc88145ee8fa36 --- regress/cmdline/patch.sh +++ regress/cmdline/patch.sh @@ -1759,10 +1759,97 @@ EOF ret=$? if [ $ret -ne 0 ]; then diff -u $testroot/wt/numbers $testroot/wt/numbers.expected + fi + 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