commit 2be5e1a234aeec2c4e8d6b84f0405034ed8664fa from: Omar Polo date: Wed Mar 16 18:23:03 2022 UTC got patch: keep permissions after patching a file ok stsp@ commit - b22138f5dbdf02a4800b44caa215bfa102ee321b commit + 2be5e1a234aeec2c4e8d6b84f0405034ed8664fa blob - a56994ab50aea3ed8cbe907c4d8fa2cc65f6ae14 blob + 6e4f19aa3e9703b1e7a3b57f3d76fe41a2b801ac --- got/got.c +++ got/got.c @@ -7253,7 +7253,7 @@ cmd_patch(int argc, char *argv[]) goto done; #ifndef PROFILE - if (pledge("stdio rpath wpath cpath proc exec sendfd flock", + if (pledge("stdio rpath wpath cpath fattr proc exec sendfd flock", NULL) == -1) err(1, "pledge"); #endif blob - 93a9570fa38f33af30ac5d909b03d23f7c63eda3 blob + 8fc9edf2163e5db80ebfc2641f66fd8d288fa8bd --- lib/patch.c +++ lib/patch.c @@ -388,10 +388,12 @@ apply_hunk(FILE *tmp, struct got_patch_hunk *h, long * } static const struct got_error * -patch_file(struct got_patch *p, const char *path, FILE *tmp, int nop) +patch_file(struct got_patch *p, const char *path, FILE *tmp, int nop, + mode_t *mode) { const struct got_error *err = NULL; struct got_patch_hunk *h; + struct stat sb; size_t i; long lineno = 0; FILE *orig; @@ -418,6 +420,12 @@ patch_file(struct got_patch *p, const char *path, FILE goto done; } + if (fstat(fileno(orig), &sb) == -1) { + err = got_error_from_errno("fstat"); + goto done; + } + *mode = sb.st_mode; + copypos = 0; STAILQ_FOREACH(h, &p->head, entries) { if (h->lines == NULL) @@ -466,15 +474,9 @@ patch_file(struct got_patch *p, const char *path, FILE } } - - if (p->new == NULL) { - struct stat sb; - - if (fstat(fileno(orig), &sb) == -1) - err = got_error_from_errno("fstat"); - else if (sb.st_size != copypos) - err = got_error(GOT_ERR_PATCH_DONT_APPLY); - } else if (!nop && !feof(orig)) + if (p->new == NULL && sb.st_size != copypos) + err = got_error(GOT_ERR_PATCH_DONT_APPLY); + else if (!nop && !feof(orig)) err = copy(tmp, orig, copypos, -1); done: @@ -596,6 +598,7 @@ apply_patch(struct got_worktree *worktree, struct got_ char *oldpath = NULL, *newpath = NULL; char *tmppath = NULL, *template = NULL; FILE *tmp = NULL; + mode_t mode = GOT_DEFAULT_FILE_MODE; TAILQ_INIT(&oldpaths); TAILQ_INIT(&newpaths); @@ -628,7 +631,7 @@ apply_patch(struct got_worktree *worktree, struct got_ err = got_opentemp_named(&tmppath, &tmp, template); if (err) goto done; - err = patch_file(p, oldpath, tmp, nop); + err = patch_file(p, oldpath, tmp, nop, &mode); if (err) goto done; @@ -641,6 +644,11 @@ apply_patch(struct got_worktree *worktree, struct got_ goto done; } + if (fchmod(fileno(tmp), mode) == -1) { + err = got_error_from_errno2("chmod", newpath); + goto done; + } + if (rename(tmppath, newpath) == -1) { err = got_error_from_errno3("rename", tmppath, newpath); goto done; blob - 41f290c529e09c20b73b0a6752c3351664a9559d blob + 6d468d0334318dc22a5552f720bf777276c909d8 --- regress/cmdline/patch.sh +++ regress/cmdline/patch.sh @@ -965,6 +965,47 @@ EOF diff -u $testroot/stdout.expected $testroot/stdout fi test_done $testroot $ret +} + +test_patch_preserve_perm() { + local testroot=`test_init patch_preserve_perm` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret=$? + if [ $ret -ne 0 ]; then + test_done $testroot $ret + return 1 + fi + + chmod +x $testroot/wt/alpha + (cd $testroot/wt && got commit -m 'alpha executable') > /dev/null + ret=$? + if [ $ret -ne 0 ]; then + test_done $testroot $ret + return 1 + fi + + cat < $testroot/wt/patch +--- alpha ++++ alpha +@@ -1 +1,2 @@ + alpha ++was edited +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/alpha ]; then + echo "alpha is no more executable!" >&2 + test_done $testroot 1 + return 1 + fi + test_done $testroot 0 } test_parseargs "$@" @@ -982,3 +1023,4 @@ run_test test_patch_equals_for_context run_test test_patch_rename run_test test_patch_illegal_status run_test test_patch_nop +run_test test_patch_preserve_perm