Commit 017406073fd742cc052672dd2f2689838e76df3f

Stefan Sperling 2020-11-04T20:18:53

avoid got_repo_map_path() in 'got blame' 'got blame' does not need access to the work tree. So far the work tree was completely hidden with unveil(). We must now expose the work tree while resolving the path, so unveil() calls are shuffled around slightly. Failing realpath() calls would mess with path resolution otherwise. There's a bug in got_worktree_resolve_path() where it failed to canonicalize a path constructed from a user-specified path that does not exist on disk. Note that this path falls into strncmp() a few lines down. I am fixing this by adding canonicalization. Generally, joining paths with asprintf() and comparing paths with strncmp() is fragile. A more general solution might be needed but I am leaving that for another day. ok naddy

diff --git a/got/got.c b/got/got.c
index 3d41cd3..e27c03f 100644
--- a/got/got.c
+++ b/got/got.c
@@ -4503,24 +4503,26 @@ cmd_blame(int argc, char *argv[])
 	if (error != NULL)
 		goto done;
 
-	error = apply_unveil(got_repo_get_path(repo), 1, NULL);
-	if (error)
-		goto done;
-
 	if (worktree) {
 		const char *prefix = got_worktree_get_path_prefix(worktree);
-		char *p, *worktree_subdir = cwd +
-		    strlen(got_worktree_get_root_path(worktree));
-		if (asprintf(&p, "%s%s%s%s%s",
-		    prefix, (strcmp(prefix, "/") != 0) ? "/" : "",
-		    worktree_subdir, worktree_subdir[0] ? "/" : "",
-		    path) == -1) {
+		char *p;
+
+		error = got_worktree_resolve_path(&p, worktree, path);
+		if (error)
+			goto done;
+		if (asprintf(&in_repo_path, "%s%s%s", prefix,
+		    (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
+		    p) == -1) {
 			error = got_error_from_errno("asprintf");
+			free(p);
 			goto done;
 		}
-		error = got_repo_map_path(&in_repo_path, repo, p, 0);
 		free(p);
+		error = apply_unveil(got_repo_get_path(repo), 1, NULL);
 	} else {
+		error = apply_unveil(got_repo_get_path(repo), 1, NULL);
+		if (error)
+			goto done;
 		error = got_repo_map_path(&in_repo_path, repo, path, 1);
 	}
 	if (error)
diff --git a/lib/worktree.c b/lib/worktree.c
index 3d3b938..544e96b 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -3625,6 +3625,8 @@ got_worktree_resolve_path(char **wt_path, struct got_worktree *worktree,
 	char *resolved = NULL, *cwd = NULL, *path = NULL;
 	size_t len;
 	struct stat sb;
+	char *abspath = NULL;
+	char canonpath[PATH_MAX];
 
 	*wt_path = NULL;
 
@@ -3645,8 +3647,6 @@ got_worktree_resolve_path(char **wt_path, struct got_worktree *worktree,
 		 * But we can make the path absolute, assuming it is relative
 		 * to the current working directory, and then canonicalize it.
 		 */
-		char *abspath = NULL;
-		char canonpath[PATH_MAX];
 		if (!got_path_is_absolute(arg)) {
 			if (asprintf(&abspath, "%s/%s", cwd, arg) == -1) {
 				err = got_error_from_errno("asprintf");
@@ -3670,10 +3670,19 @@ got_worktree_resolve_path(char **wt_path, struct got_worktree *worktree,
 				err = got_error_from_errno2("realpath", arg);
 				goto done;
 			}
-			if (asprintf(&resolved, "%s/%s", cwd, arg) == -1) {
+			if (asprintf(&abspath, "%s/%s", cwd, arg) == -1) {
 				err = got_error_from_errno("asprintf");
 				goto done;
 			}
+			err = got_canonpath(abspath, canonpath,
+			    sizeof(canonpath));
+			if (err)
+				goto done;
+			resolved = strdup(canonpath);
+			if (resolved == NULL) {
+				err = got_error_from_errno("strdup");
+				goto done;
+			}
 		}
 	}
 
@@ -3703,6 +3712,7 @@ got_worktree_resolve_path(char **wt_path, struct got_worktree *worktree,
 		len--;
 	}
 done:
+	free(abspath);
 	free(resolved);
 	free(cwd);
 	if (err == NULL)