prefix_filename: return newly allocated string
authorJeff King <peff@peff.net>
Tue, 21 Mar 2017 01:28:49 +0000 (21:28 -0400)
committerJunio C Hamano <gitster@pobox.com>
Tue, 21 Mar 2017 18:18:41 +0000 (11:18 -0700)
The prefix_filename() function returns a pointer to static
storage, which makes it easy to use dangerously. We already
fixed one buggy caller in hash-object recently, and the
calls in apply.c are suspicious (I didn't dig in enough to
confirm that there is a bug, but we call the function once
in apply_all_patches() and then again indirectly from
parse_chunk()).

Let's make it harder to get wrong by allocating the return
value. For simplicity, we'll do this even when the prefix is
empty (and we could just return the original file pointer).
That will cause us to allocate sometimes when we wouldn't
otherwise need to, but this function isn't called in
performance critical code-paths (and it already _might_
allocate on any given call, so a caller that cares about
performance is questionable anyway).

The downside is that the callers need to remember to free()
the result to avoid leaking. Most of them already used
xstrdup() on the result, so we know they are OK. The
remainder have been converted to use free() as appropriate.

I considered retaining a prefix_filename_unsafe() for cases
where we know the static lifetime is OK (and handling the
cleanup is awkward). This is only a handful of cases,
though, and it's not worth the mental energy in worrying
about whether the "unsafe" variant is OK to use in any
situation.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
15 files changed:
abspath.c
apply.c
builtin/config.c
builtin/hash-object.c
builtin/log.c
builtin/mailinfo.c
builtin/merge-file.c
builtin/rev-parse.c
builtin/worktree.c
cache.h
diff-no-index.c
diff.c
parse-options.c
setup.c
worktree.c
index c6f480993d7cb404ac5d275db86be6f64a325a6b..4addd1fde0a3df34b33599ac2f1ed72a90bd23ed 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -246,20 +246,18 @@ char *absolute_pathdup(const char *path)
        return strbuf_detach(&sb, NULL);
 }
 
-const char *prefix_filename(const char *pfx, const char *arg)
+char *prefix_filename(const char *pfx, const char *arg)
 {
-       static struct strbuf path = STRBUF_INIT;
+       struct strbuf path = STRBUF_INIT;
        size_t pfx_len = pfx ? strlen(pfx) : 0;
 
 #ifndef GIT_WINDOWS_NATIVE
        if (!pfx_len || is_absolute_path(arg))
-               return arg;
-       strbuf_reset(&path);
+               return xstrdup(arg);
        strbuf_add(&path, pfx, pfx_len);
        strbuf_addstr(&path, arg);
 #else
        /* don't add prefix to absolute paths, but still replace '\' by '/' */
-       strbuf_reset(&path);
        if (is_absolute_path(arg))
                pfx_len = 0;
        else if (pfx_len)
@@ -267,5 +265,5 @@ const char *prefix_filename(const char *pfx, const char *arg)
        strbuf_addstr(&path, arg);
        convert_slashes(path.buf + pfx_len);
 #endif
-       return path.buf;
+       return strbuf_detach(&path, NULL);
 }
diff --git a/apply.c b/apply.c
index b8bd5a4befa410ffa96ea638c2eb59f51c2b2c96..e6dbab26ad54b9a3af06f6adad928c6e1526db58 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -2046,7 +2046,7 @@ static void prefix_one(struct apply_state *state, char **name)
        char *old_name = *name;
        if (!old_name)
                return;
-       *name = xstrdup(prefix_filename(state->prefix, *name));
+       *name = prefix_filename(state->prefix, *name);
        free(old_name);
 }
 
@@ -4805,6 +4805,7 @@ int apply_all_patches(struct apply_state *state,
 
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
+               char *to_free = NULL;
                int fd;
 
                if (!strcmp(arg, "-")) {
@@ -4814,19 +4815,21 @@ int apply_all_patches(struct apply_state *state,
                        errs |= res;
                        read_stdin = 0;
                        continue;
-               } else if (0 < state->prefix_length)
-                       arg = prefix_filename(state->prefix, arg);
+               } else
+                       arg = to_free = prefix_filename(state->prefix, arg);
 
                fd = open(arg, O_RDONLY);
                if (fd < 0) {
                        error(_("can't open patch '%s': %s"), arg, strerror(errno));
                        res = -128;
+                       free(to_free);
                        goto end;
                }
                read_stdin = 0;
                set_default_whitespace_mode(state);
                res = apply_patch(state, fd, arg, options);
                close(fd);
+               free(to_free);
                if (res < 0)
                        goto end;
                errs |= res;
index 74f6c34d11ec720b7faf8212b9b61ec0648cf4aa..4f49a0edb9071f4f63e97dfae902ff7a0aa51ee9 100644 (file)
@@ -527,8 +527,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        else if (given_config_source.file) {
                if (!is_absolute_path(given_config_source.file) && prefix)
                        given_config_source.file =
-                               xstrdup(prefix_filename(prefix,
-                                                       given_config_source.file));
+                               prefix_filename(prefix, given_config_source.file);
        }
 
        if (respect_includes == -1)
index 2ea36909d2443dc704c035d57840f804fb6f6401..bbeaf20bcca1ae1e9bfa55b5a8e4adbed83c00e8 100644 (file)
@@ -145,7 +145,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
                char *to_free = NULL;
 
                if (prefix)
-                       arg = to_free = xstrdup(prefix_filename(prefix, arg));
+                       arg = to_free = prefix_filename(prefix, arg);
                hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,
                            flags, literally);
                free(to_free);
index bfdc7a23d36473a271a1296b67ed0c850407c6bc..670229cbb4c2a80e7389bc9d65f92455d1d34427 100644 (file)
@@ -1084,7 +1084,7 @@ static const char *set_outdir(const char *prefix, const char *output_directory)
        if (!output_directory)
                return prefix;
 
-       return xstrdup(prefix_filename(prefix, output_directory));
+       return prefix_filename(prefix, output_directory);
 }
 
 static const char * const builtin_format_patch_usage[] = {
index 681f07f54d792af1be9fe9ed4636d8ce947cd75b..cfb667a594c8452b8f4259bb3da1976965906f95 100644 (file)
 static const char mailinfo_usage[] =
        "git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
 
-static char *prefix_copy(const char *prefix, const char *filename)
-{
-       if (!prefix || is_absolute_path(filename))
-               return xstrdup(filename);
-       return xstrdup(prefix_filename(prefix, filename));
-}
-
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
        const char *def_charset;
@@ -60,8 +53,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
        mi.input = stdin;
        mi.output = stdout;
 
-       msgfile = prefix_copy(prefix, argv[1]);
-       patchfile = prefix_copy(prefix, argv[2]);
+       msgfile = prefix_filename(prefix, argv[1]);
+       patchfile = prefix_filename(prefix, argv[2]);
 
        status = !!mailinfo(&mi, msgfile, patchfile);
        clear_mailinfo(&mi);
index 63cd94358770326e109df8f2b628aa6b8c97bc40..47dde7c39c922c77bf388e87db27618d09f5bb74 100644 (file)
@@ -65,11 +65,18 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        }
 
        for (i = 0; i < 3; i++) {
-               const char *fname = prefix_filename(prefix, argv[i]);
+               char *fname;
+               int ret;
+
                if (!names[i])
                        names[i] = argv[i];
-               if (read_mmfile(mmfs + i, fname))
+
+               fname = prefix_filename(prefix, argv[i]);
+               ret = read_mmfile(mmfs + i, fname);
+               free(fname);
+               if (ret)
                        return -1;
+
                if (mmfs[i].size > MAX_XDIFF_SIZE ||
                    buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
                        return error("Cannot merge binary files: %s",
@@ -86,7 +93,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 
        if (ret >= 0) {
                const char *filename = argv[0];
-               const char *fpath = prefix_filename(prefix, argv[0]);
+               char *fpath = prefix_filename(prefix, argv[0]);
                FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
 
                if (!f)
@@ -98,6 +105,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                else if (fclose(f))
                        ret = error_errno("Could not close %s", filename);
                free(result.ptr);
+               free(fpath);
        }
 
        if (ret > 127)
index c8035331e21579df39c6a76aca8a8299c6b9b5df..7cd01c28190560ed3a3e81aa025b5f9f35417858 100644 (file)
@@ -228,7 +228,9 @@ static int show_file(const char *arg, int output_prefix)
        if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
                if (output_prefix) {
                        const char *prefix = startup_info->prefix;
-                       show(prefix_filename(prefix, arg));
+                       char *fname = prefix_filename(prefix, arg);
+                       show(fname);
+                       free(fname);
                } else
                        show(arg);
                return 1;
index e38325e44bb567a63b622418a573f2296c91eea3..9993ded41aaaaa9a9291bf51b9949a97418a5e76 100644 (file)
@@ -318,7 +318,8 @@ static int add(int ac, const char **av, const char *prefix)
 {
        struct add_opts opts;
        const char *new_branch_force = NULL;
-       const char *path, *branch;
+       char *path;
+       const char *branch;
        struct option options[] = {
                OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
diff --git a/cache.h b/cache.h
index 0b53aef0ed4b761aed47e8e930e1237666bac287..aa6a0fb91a9ccf8ba8c2415405d2f4f1a650033a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -537,10 +537,10 @@ extern char *prefix_path_gently(const char *prefix, int len, int *remaining, con
  * not have to interact with index entry; i.e. name of a random file
  * on the filesystem.
  *
- * The return value may point to static storage which will be overwritten by
- * further calls.
+ * The return value is always a newly allocated string (even if the
+ * prefix was empty).
  */
-extern const char *prefix_filename(const char *prefix, const char *path);
+extern char *prefix_filename(const char *prefix, const char *path);
 
 extern int check_filename(const char *prefix, const char *name);
 extern void verify_filename(const char *prefix,
index 5f7317ced9ec1ab06d61d04fdb2c3526d794249a..79229382b06cefc98aa8926065f0fb999ad1da2e 100644 (file)
@@ -266,7 +266,7 @@ void diff_no_index(struct rev_info *revs,
                         */
                        p = file_from_standard_input;
                else if (prefix)
-                       p = xstrdup(prefix_filename(prefix, p));
+                       p = prefix_filename(prefix, p);
                paths[i] = p;
        }
 
diff --git a/diff.c b/diff.c
index 70870b4b6963d55472b7f9c6232f9a194bddd83a..58cb72d7e72a85bad9ae9fe8a198fef1cb1b15f8 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -4023,8 +4023,7 @@ int diff_opt_parse(struct diff_options *options,
        else if (!strcmp(arg, "--pickaxe-regex"))
                options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
        else if ((argcount = short_opt('O', av, &optarg))) {
-               const char *path = prefix_filename(prefix, optarg);
-               options->orderfile = xstrdup(path);
+               options->orderfile = prefix_filename(prefix, optarg);
                return argcount;
        }
        else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
@@ -4071,13 +4070,14 @@ int diff_opt_parse(struct diff_options *options,
        else if (!strcmp(arg, "--no-function-context"))
                DIFF_OPT_CLR(options, FUNCCONTEXT);
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
-               const char *path = prefix_filename(prefix, optarg);
+               char *path = prefix_filename(prefix, optarg);
                options->file = fopen(path, "w");
                if (!options->file)
                        die_errno("Could not open '%s'", path);
                options->close_file = 1;
                if (options->use_color != GIT_COLOR_ALWAYS)
                        options->use_color = GIT_COLOR_NEVER;
+               free(path);
                return argcount;
        } else
                return 0;
index ba6cc30b26aebdcd1a9a14b6e7a2ad4a481ab128..a23a1e67f04f8072bea357cbed34fdc1b39ab7ae 100644 (file)
@@ -40,7 +40,7 @@ static void fix_filename(const char *prefix, const char **file)
        if (!file || !*file || !prefix || is_absolute_path(*file)
            || !strcmp("-", *file))
                return;
-       *file = xstrdup(prefix_filename(prefix, *file));
+       *file = prefix_filename(prefix, *file);
 }
 
 static int opt_command_mode_error(const struct option *opt,
diff --git a/setup.c b/setup.c
index a76379e0ceaa688322da8f07c02350cfbcba7ccf..5c7946d2b45f067049e11347cca1cedc9790e228 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -135,6 +135,7 @@ int path_inside_repo(const char *prefix, const char *path)
 int check_filename(const char *prefix, const char *arg)
 {
        const char *name;
+       char *to_free = NULL;
        struct stat st;
 
        if (starts_with(arg, ":/")) {
@@ -142,13 +143,17 @@ int check_filename(const char *prefix, const char *arg)
                        return 1;
                name = arg + 2;
        } else if (prefix)
-               name = prefix_filename(prefix, arg);
+               name = to_free = prefix_filename(prefix, arg);
        else
                name = arg;
-       if (!lstat(name, &st))
+       if (!lstat(name, &st)) {
+               free(to_free);
                return 1; /* file exists */
-       if (errno == ENOENT || errno == ENOTDIR)
+       }
+       if (errno == ENOENT || errno == ENOTDIR) {
+               free(to_free);
                return 0; /* file does not exist */
+       }
        die_errno("failed to stat '%s'", arg);
 }
 
index 42dd3d52b0963445a304062feaba79a64ecca0ef..bae787cf8d7e968e8d5118bf54f17112c0229bf5 100644 (file)
@@ -250,16 +250,19 @@ struct worktree *find_worktree(struct worktree **list,
 {
        struct worktree *wt;
        char *path;
+       char *to_free = NULL;
 
        if ((wt = find_worktree_by_suffix(list, arg)))
                return wt;
 
-       arg = prefix_filename(prefix, arg);
+       if (prefix)
+               arg = to_free = prefix_filename(prefix, arg);
        path = real_pathdup(arg, 1);
        for (; *list; list++)
                if (!fspathcmp(path, real_path((*list)->path)))
                        break;
        free(path);
+       free(to_free);
        return *list;
 }