Commit 5b67f96efbfbf2e5a3f75f6ab91e45dd3013c77f

Omar Polo 2022-03-13T15:36:59

got patch: require exact match when removing files

diff --git a/lib/patch.c b/lib/patch.c
index 478ade4..71695e6 100644
--- a/lib/patch.c
+++ b/lib/patch.c
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/uio.h>
 
 #include <errno.h>
@@ -456,7 +457,15 @@ patch_file(struct got_patch *p, const char *path, FILE *tmp)
 		}
 	}
 
-	if (!feof(orig))
+	
+	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 (!feof(orig))
 		err = copy(tmp, orig, copypos, -1);
 
 done:
@@ -584,16 +593,6 @@ apply_patch(struct got_worktree *worktree, struct got_repository *repo,
 	if (err)
 		goto done;
 
-	if (p->old != NULL && p->new == NULL) {
-		/*
-		 * special case: delete a file.  don't try to match
-		 * the lines but just schedule the removal.
-		 */
-		err = got_worktree_schedule_delete(worktree, &oldpaths,
-		    0, NULL, delete_cb, delete_arg, repo, 0, 0);
-		goto done;
-	}
-
 	if (asprintf(&template, "%s/got-patch",
 	    got_worktree_get_root_path(worktree)) == -1) {
 		err = got_error_from_errno(template);
@@ -607,6 +606,12 @@ apply_patch(struct got_worktree *worktree, struct got_repository *repo,
 	if (err)
 		goto done;
 
+	if (p->old != NULL && p->new == NULL) {
+		err = got_worktree_schedule_delete(worktree, &oldpaths,
+		    0, NULL, delete_cb, delete_arg, repo, 0, 0);
+		goto done;
+	}
+
 	if (rename(tmppath, newpath) == -1) {
 		err = got_error_from_errno3("rename", tmppath, newpath);
 		goto done;
diff --git a/regress/cmdline/patch.sh b/regress/cmdline/patch.sh
index 36c9ef5..9a300a8 100755
--- a/regress/cmdline/patch.sh
+++ b/regress/cmdline/patch.sh
@@ -439,6 +439,44 @@ EOF
 		return 1
 	fi
 
+	# try to delete a file with a patch that doesn't match
+	jot 100 > $testroot/wt/numbers
+	(cd $testroot/wt && got add numbers && got commit -m 'add numbers') \
+		>/dev/null
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		test_done $testroot $ret
+		return 1
+	fi
+
+	cat <<EOF > $testroot/wt/patch
+--- numbers
++++ /dev/null
+@@ -1,9 +0,0 @@
+-1
+-2
+-3
+-4
+-5
+-6
+-7
+-8
+-9
+EOF
+
+	(cd $testroot/wt && got patch patch) > /dev/null 2> $testroot/stderr
+	ret=$?
+	if [ $ret -eq 0 ]; then # should fail
+		test_done $testroot 1
+		return 1
+	fi
+
+	echo "got: patch doesn't apply" > $testroot/stderr.expected
+	cmp -s $testroot/stderr.expected $testroot/stderr
+	ret=$?
+	if [ $ret -ne 0 ]; then
+		diff -u $testroot/stderr.expected $testroot/stderr
+	fi
 	test_done $testroot $ret
 }