Returning a shared buffer invites very subtle bugs due to reentrancy or
multi-threading, as demonstrated by the previous patch.
There was an unfinished effort to abolish this [1].
Let's finally rid of `real_path()`, using `strbuf_realpath()` instead.
This patch uses a local `strbuf` for most places where `real_path()` was
previously called.
However, two places return the value of `real_path()` to the caller. For
them, a `static` local `strbuf` was added, effectively pushing the
problem one level higher:
read_gitfile_gently()
get_superproject_working_tree()
[1] https://lore.kernel.org/git/1480964316-99305-1-git-send-email-bmwill@google.com/
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Alexandr Miloslavskiy committedMar 10, 2020 at 13:11 UTC3d7747e318532a36a263c61cdf92f2decb6424ff
13 files changed+59-24
abspath.c
+1-7
index 9857985329..d34026bfeb 100644--- a/abspath.c+++ b/abspath.c@@ -206,12 +206,6 @@ error_out: * Resolve `path` into an absolute, cleaned-up path. The return value * comes from a shared buffer. */-const char *real_path(const char *path)-{- static struct strbuf realpath = STRBUF_INIT;- return strbuf_realpath(&realpath, path, 1);-}- const char *real_path_if_valid(const char *path) { static struct strbuf realpath = STRBUF_INIT;@@ -233,7 +227,7 @@ char *real_pathdup(const char *path, int die_on_error) /* * Use this to get an absolute path from a relative one. If you want- * to resolve links, you should use real_path.+ * to resolve links, you should use strbuf_realpath. */ const char *absolute_path(const char *path) {
builtin/clone.c
+5-1
index 1ad26f4d8c..488bdb0741 100644--- a/builtin/clone.c+++ b/builtin/clone.c@@ -420,6 +420,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, struct dir_iterator *iter; int iter_status; unsigned int flags;+ struct strbuf realpath = STRBUF_INIT; mkdir_if_missing(dest->buf, 0777);@@ -454,7 +455,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, if (unlink(dest->buf) && errno != ENOENT) die_errno(_("failed to unlink '%s'"), dest->buf); if (!option_no_hardlinks) {- if (!link(real_path(src->buf), dest->buf))+ strbuf_realpath(&realpath, src->buf, 1);+ if (!link(realpath.buf, dest->buf)) continue; if (option_local > 0) die_errno(_("failed to create link '%s'"), dest->buf);@@ -468,6 +470,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, strbuf_setlen(src, src_len); die(_("failed to iterate over '%s'"), src->buf); }++ strbuf_release(&realpath); } static void clone_local(const char *src_repo, const char *dest_repo)
index 24f22800f3..d99db35668 100644--- a/builtin/worktree.c+++ b/builtin/worktree.c@@ -258,7 +258,7 @@ static int add_worktree(const char *path, const char *refname, const struct add_opts *opts) { struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;- struct strbuf sb = STRBUF_INIT;+ struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT; const char *name; struct child_process cp = CHILD_PROCESS_INIT; struct argv_array child_env = ARGV_ARRAY_INIT;@@ -330,9 +330,11 @@ static int add_worktree(const char *path, const char *refname, strbuf_reset(&sb); strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);- write_file(sb.buf, "%s", real_path(sb_git.buf));+ strbuf_realpath(&realpath, sb_git.buf, 1);+ write_file(sb.buf, "%s", realpath.buf);+ strbuf_realpath(&realpath, get_git_common_dir(), 1); write_file(sb_git.buf, "gitdir: %s/worktrees/%s",- real_path(get_git_common_dir()), name);+ realpath.buf, name); /* * This is to keep resolve_ref() happy. We need a valid HEAD * or is_git_directory() will reject the directory. Any value which@@ -418,6 +420,7 @@ done: strbuf_release(&sb_repo); strbuf_release(&sb_git); strbuf_release(&sb_name);+ strbuf_release(&realpath); return ret; }
cache.h
-1
index 8cee257d3d..f6937793ec 100644--- a/cache.h+++ b/cache.h@@ -1314,7 +1314,6 @@ static inline int is_absolute_path(const char *path) int is_directory(const char *); char *strbuf_realpath(struct strbuf *resolved, const char *path, int die_on_error);-const char *real_path(const char *path); const char *real_path_if_valid(const char *path); char *real_pathdup(const char *path, int die_on_error); const char *absolute_path(const char *path);
editor.c
+9-2
index f079abbf11..91989ee8a1 100644--- a/editor.c+++ b/editor.c@@ -54,7 +54,8 @@ static int launch_specified_editor(const char *editor, const char *path, return error("Terminal is dumb, but EDITOR unset"); if (strcmp(editor, ":")) {- const char *args[] = { editor, real_path(path), NULL };+ struct strbuf realpath = STRBUF_INIT;+ const char *args[] = { editor, NULL, NULL }; struct child_process p = CHILD_PROCESS_INIT; int ret, sig; int print_waiting_for_editor = advice_waiting_for_editor && isatty(2);@@ -75,16 +76,22 @@ static int launch_specified_editor(const char *editor, const char *path, fflush(stderr); }+ strbuf_realpath(&realpath, path, 1);+ args[1] = realpath.buf;+ p.argv = args; p.env = env; p.use_shell = 1; p.trace2_child_class = "editor";- if (start_command(&p) < 0)+ if (start_command(&p) < 0) {+ strbuf_release(&realpath); return error("unable to start editor '%s'", editor);+ } sigchain_push(SIGINT, SIG_IGN); sigchain_push(SIGQUIT, SIG_IGN); ret = finish_command(&p);+ strbuf_release(&realpath); sig = ret - 128; sigchain_pop(SIGINT); sigchain_pop(SIGQUIT);
environment.c
+6-1
index c436de31ee..10c9061c43 100644--- a/environment.c+++ b/environment.c@@ -254,8 +254,11 @@ static int git_work_tree_initialized; */ void set_git_work_tree(const char *new_work_tree) {+ struct strbuf realpath = STRBUF_INIT;+ if (git_work_tree_initialized) {- new_work_tree = real_path(new_work_tree);+ strbuf_realpath(&realpath, new_work_tree, 1);+ new_work_tree = realpath.buf; if (strcmp(new_work_tree, the_repository->worktree)) die("internal error: work tree has already been set\n" "Current worktree: %s\nNew worktree: %s",@@ -264,6 +267,8 @@ void set_git_work_tree(const char *new_work_tree) } git_work_tree_initialized = 1; repo_set_worktree(the_repository, new_work_tree);++ strbuf_release(&realpath); } const char *get_git_work_tree(void)
path.c
+1-1
index c5a8fe4f0c..0a42ceb3fb 100644--- a/path.c+++ b/path.c@@ -723,7 +723,7 @@ static struct passwd *getpw_str(const char *username, size_t len) * then it is a newly allocated string. Returns NULL on getpw failure or * if path is NULL. *- * If real_home is true, real_path($HOME) is used in the expansion.+ * If real_home is true, strbuf_realpath($HOME) is used in the expansion. */ char *expand_user_path(const char *path, int real_home) {
setup.c
+12-3
index fa4317e707..1ae3f20301 100644--- a/setup.c+++ b/setup.c@@ -32,6 +32,7 @@ static int abspath_part_inside_repo(char *path) char *path0; int off; const char *work_tree = get_git_work_tree();+ struct strbuf realpath = STRBUF_INIT; if (!work_tree) return -1;@@ -60,8 +61,10 @@ static int abspath_part_inside_repo(char *path) path++; if (*path == '/') { *path = '\0';- if (fspathcmp(real_path(path0), work_tree) == 0) {+ strbuf_realpath(&realpath, path0, 1);+ if (fspathcmp(realpath.buf, work_tree) == 0) { memmove(path0, path + 1, len - (path - path0));+ strbuf_release(&realpath); return 0; } *path = '/';@@ -69,11 +72,14 @@ static int abspath_part_inside_repo(char *path) } /* check whole path */- if (fspathcmp(real_path(path0), work_tree) == 0) {+ strbuf_realpath(&realpath, path0, 1);+ if (fspathcmp(realpath.buf, work_tree) == 0) { *path0 = '\0';+ strbuf_release(&realpath); return 0; }+ strbuf_release(&realpath); return -1; }@@ -619,6 +625,7 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) struct stat st; int fd; ssize_t len;+ static struct strbuf realpath = STRBUF_INIT; if (stat(path, &st)) { /* NEEDSWORK: discern between ENOENT vs other errors */@@ -669,7 +676,9 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) error_code = READ_GITFILE_ERR_NOT_A_REPO; goto cleanup_return; }- path = real_path(dir);++ strbuf_realpath(&realpath, dir, 1);+ path = realpath.buf; cleanup_return: if (return_error_code)