Merge branch 'js/realpath-pathdup-fix'
authorJunio C Hamano <gitster@pobox.com>
Mon, 13 Mar 2017 06:21:33 +0000 (23:21 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 13 Mar 2017 06:21:33 +0000 (23:21 -0700)
Git v2.12 was shipped with an embarrassing breakage where various
operations that verify paths given from the user stopped dying when
seeing an issue, and instead later triggering segfault.

* js/realpath-pathdup-fix:
real_pathdup(): fix callsites that wanted it to die on error
t1501: demonstrate NULL pointer access with invalid GIT_WORK_TREE

1  2 
cache.h
setup.c
worktree.c
diff --combined cache.h
index 283ab8fb40ecb79d470b5e7984dcd81a98cf3ff6,c26c45f899d2be6e1618d31881c6be9a212a9077..8c0e6442076eb91571dc0c5cf55db478ab47d2ad
+++ b/cache.h
@@@ -1045,6 -1045,9 +1045,6 @@@ static inline int is_empty_tree_oid(con
        return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
  }
  
 -
 -int git_mkstemp(char *path, size_t n, const char *template);
 -
  /* set default permissions by passing mode arguments to open(2) */
  int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
  int git_mkstemp_mode(char *pattern, int mode);
@@@ -1069,9 -1072,8 +1069,9 @@@ int adjust_shared_perm(const char *path
  
  /*
   * Create the directory containing the named path, using care to be
 - * somewhat safe against races.  Return one of the scld_error values
 - * to indicate success/failure.
 + * somewhat safe against races. Return one of the scld_error values to
 + * indicate success/failure. On error, set errno to describe the
 + * problem.
   *
   * SCLD_VANISHED indicates that one of the ancestor directories of the
   * path existed at one point during the function call and then
@@@ -1095,49 -1097,6 +1095,49 @@@ enum scld_error 
  enum scld_error safe_create_leading_directories(char *path);
  enum scld_error safe_create_leading_directories_const(const char *path);
  
 +/*
 + * Callback function for raceproof_create_file(). This function is
 + * expected to do something that makes dirname(path) permanent despite
 + * the fact that other processes might be cleaning up empty
 + * directories at the same time. Usually it will create a file named
 + * path, but alternatively it could create another file in that
 + * directory, or even chdir() into that directory. The function should
 + * return 0 if the action was completed successfully. On error, it
 + * should return a nonzero result and set errno.
 + * raceproof_create_file() treats two errno values specially:
 + *
 + * - ENOENT -- dirname(path) does not exist. In this case,
 + *             raceproof_create_file() tries creating dirname(path)
 + *             (and any parent directories, if necessary) and calls
 + *             the function again.
 + *
 + * - EISDIR -- the file already exists and is a directory. In this
 + *             case, raceproof_create_file() removes the directory if
 + *             it is empty (and recursively any empty directories that
 + *             it contains) and calls the function again.
 + *
 + * Any other errno causes raceproof_create_file() to fail with the
 + * callback's return value and errno.
 + *
 + * Obviously, this function should be OK with being called again if it
 + * fails with ENOENT or EISDIR. In other scenarios it will not be
 + * called again.
 + */
 +typedef int create_file_fn(const char *path, void *cb);
 +
 +/*
 + * Create a file in dirname(path) by calling fn, creating leading
 + * directories if necessary. Retry a few times in case we are racing
 + * with another process that is trying to clean up the directory that
 + * contains path. See the documentation for create_file_fn for more
 + * details.
 + *
 + * Return the value and set the errno that resulted from the most
 + * recent call of fn. fn is always called at least once, and will be
 + * called more than once if it returns ENOENT or EISDIR.
 + */
 +int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 +
  int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  const char *enter_repo(const char *path, int strict);
@@@ -1150,7 -1109,7 +1150,7 @@@ char *strbuf_realpath(struct strbuf *re
                      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);
+ char *real_pathdup(const char *path, int die_on_error);
  const char *absolute_path(const char *path);
  char *absolute_pathdup(const char *path);
  const char *remove_leading_path(const char *in, const char *prefix);
@@@ -1860,11 -1819,8 +1860,11 @@@ extern int git_config_include(const cha
   *
   * (i.e., what gets handed to a config_fn_t). The caller provides the section;
   * we return -1 if it does not match, 0 otherwise. The subsection and key
 - * out-parameters are filled by the function (and subsection is NULL if it is
 + * out-parameters are filled by the function (and *subsection is NULL if it is
   * missing).
 + *
 + * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
 + * there is no subsection at all.
   */
  extern int parse_config_key(const char *var,
                            const char *section,
diff --combined setup.c
index f14cbcd3383528566e37cdf76b1fcf517cd31f1e,6b48cb91ff2bdde1d02b2696b5b38a64cc663161..8f64fbdfb28fc2e487cfdba76561e5b3a25766f0
+++ b/setup.c
@@@ -254,7 -254,7 +254,7 @@@ int get_common_dir_noenv(struct strbuf 
                if (!is_absolute_path(data.buf))
                        strbuf_addf(&path, "%s/", gitdir);
                strbuf_addbuf(&path, &data);
 -              strbuf_addstr(sb, real_path(path.buf));
 +              strbuf_add_real_path(sb, path.buf);
                ret = 1;
        } else {
                strbuf_addstr(sb, gitdir);
@@@ -698,7 -698,7 +698,7 @@@ static const char *setup_discovered_git
        /* --work-tree is set without --git-dir; use discovered one */
        if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
                if (offset != cwd->len && !is_absolute_path(gitdir))
-                       gitdir = real_pathdup(gitdir);
+                       gitdir = real_pathdup(gitdir, 1);
                if (chdir(cwd->buf))
                        die_errno("Could not come back to cwd");
                return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
@@@ -806,7 -806,7 +806,7 @@@ static int canonicalize_ceiling_entry(s
                /* Keep entry but do not canonicalize it */
                return 1;
        } else {
-               char *real_path = real_pathdup(ceil);
+               char *real_path = real_pathdup(ceil, 0);
                if (!real_path) {
                        return 0;
                }
diff --combined worktree.c
index d7b911aac730001b466379c26faa390fb21a5bfb,0486e31ad4a5f5ee02813d06bf9a48e48e3667cb..fa7bc67a50a6d52116b0ca994b473668998f1c3f
@@@ -175,7 -175,7 +175,7 @@@ struct worktree **get_worktrees(unsigne
        struct dirent *d;
        int counter = 0, alloc = 2;
  
 -      list = xmalloc(alloc * sizeof(struct worktree *));
 +      ALLOC_ARRAY(list, alloc);
  
        list[counter++] = get_main_worktree();
  
@@@ -255,7 -255,7 +255,7 @@@ struct worktree *find_worktree(struct w
                return wt;
  
        arg = prefix_filename(prefix, strlen(prefix), arg);
-       path = real_pathdup(arg);
+       path = real_pathdup(arg, 1);
        for (; *list; list++)
                if (!fspathcmp(path, real_path((*list)->path)))
                        break;