Commit 1c4cdd89292391629861b9a13688e94879efe2ea

Stefan Sperling 2021-06-20T21:07:54

fix bogus 'permission denied' error when a file at work tree root is removed ok naddy

diff --git a/lib/worktree.c b/lib/worktree.c
index 77e9ecb..8fda195 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -2104,7 +2104,7 @@ static const struct got_error *
 remove_ondisk_file(const char *root_path, const char *path)
 {
 	const struct got_error *err = NULL;
-	char *ondisk_path = NULL;
+	char *ondisk_path = NULL, *parent = NULL;
 
 	if (asprintf(&ondisk_path, "%s/%s", root_path, path) == -1)
 		return got_error_from_errno("asprintf");
@@ -2114,23 +2114,28 @@ remove_ondisk_file(const char *root_path, const char *path)
 			err = got_error_from_errno2("unlink", ondisk_path);
 	} else {
 		size_t root_len = strlen(root_path);
-		do {
-			char *parent;
-			err = got_path_dirname(&parent, ondisk_path);
-			if (err)
-				break;
+		err = got_path_dirname(&parent, ondisk_path);
+		if (err)
+			goto done;
+		while (got_path_cmp(parent, root_path,
+		    strlen(parent), root_len) != 0) {
 			free(ondisk_path);
 			ondisk_path = parent;
+			parent = NULL;
 			if (rmdir(ondisk_path) == -1) {
 				if (errno != ENOTEMPTY)
 					err = got_error_from_errno2("rmdir",
 					    ondisk_path);
 				break;
 			}
-		} while (got_path_cmp(ondisk_path, root_path,
-		    strlen(ondisk_path), root_len) != 0);
+			err = got_path_dirname(&parent, ondisk_path);
+			if (err)
+				break;
+		}
 	}
+done:
 	free(ondisk_path);
+	free(parent);
 	return err;
 }
 
diff --git a/regress/cmdline/update.sh b/regress/cmdline/update.sh
index c90dd13..fa03988 100755
--- a/regress/cmdline/update.sh
+++ b/regress/cmdline/update.sh
@@ -98,7 +98,8 @@ test_update_adds_file() {
 test_update_deletes_file() {
 	local testroot=`test_init update_deletes_file`
 
-	got checkout $testroot/repo $testroot/wt > /dev/null
+	mkdir $testroot/wtparent
+	got checkout $testroot/repo $testroot/wtparent/wt > /dev/null
 	ret="$?"
 	if [ "$ret" != "0" ]; then
 		test_done "$testroot" "$ret"
@@ -113,7 +114,11 @@ test_update_deletes_file() {
 	git_show_head $testroot/repo >> $testroot/stdout.expected
 	echo >> $testroot/stdout.expected
 
-	(cd $testroot/wt && got update > $testroot/stdout)
+	# verify that no error occurs if the work tree's parent
+	# directory is not writable
+	chmod u-w $testroot/wtparent
+	(cd $testroot/wtparent/wt && got update > $testroot/stdout)
+	chmod u+w $testroot/wtparent
 
 	cmp -s $testroot/stdout.expected $testroot/stdout
 	ret="$?"
@@ -123,7 +128,7 @@ test_update_deletes_file() {
 		return 1
 	fi
 
-	if [ -e $testroot/wt/beta ]; then
+	if [ -e $testroot/wtparent/wt/beta ]; then
 		echo "removed file beta still exists on disk" >&2
 		test_done "$testroot" "1"
 		return 1