Merge branch 'bw/realpath-wo-chdir'
authorJunio C Hamano <gitster@pobox.com>
Wed, 18 Jan 2017 23:12:16 +0000 (15:12 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 18 Jan 2017 23:12:16 +0000 (15:12 -0800)
The implementation of "real_path()" was to go there with chdir(2)
and call getcwd(3), but this obviously wouldn't be usable in a
threaded environment. Rewrite it to manually resolve relative
paths including symbolic links in path components.

* bw/realpath-wo-chdir:
real_path: set errno when max number of symlinks is exceeded
real_path: prevent redefinition of MAXSYMLINKS

1  2 
dir.c
diff --combined dir.c
index 4ac63bc940fa043fe82002dd6c282b0b5abcbad8,bfa8c8a9a51297c3a969c8706467504cb11ce51b..65c3e681b8e04aa47b6cf41faca0822ead59fb6a
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
  #include "varint.h"
  #include "ewah/ewok.h"
  
 -struct path_simplify {
 -      int len;
 -      const char *path;
 -};
 -
  /*
   * Tells read_directory_recursive how a file or directory should be treated.
   * Values are ordered by significance, e.g. if a directory contains both
@@@ -45,7 -50,7 +45,7 @@@ struct cached_dir 
  
  static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        const char *path, int len, struct untracked_cache_dir *untracked,
 -      int check_only, const struct path_simplify *simplify);
 +      int check_only, const struct pathspec *pathspec);
  static int get_dtype(struct dirent *de, const char *path, int len);
  
  int fspathcmp(const char *a, const char *b)
@@@ -174,21 -179,17 +174,21 @@@ char *common_prefix(const struct pathsp
  
  int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec)
  {
 -      size_t len;
 +      char *prefix;
 +      size_t prefix_len;
  
        /*
         * Calculate common prefix for the pathspec, and
         * use that to optimize the directory walk
         */
 -      len = common_prefix_len(pathspec);
 +      prefix = common_prefix(pathspec);
 +      prefix_len = prefix ? strlen(prefix) : 0;
  
        /* Read the directory and prune it */
 -      read_directory(dir, pathspec->nr ? pathspec->_raw[0] : "", len, pathspec);
 -      return len;
 +      read_directory(dir, prefix, prefix_len, pathspec);
 +
 +      free(prefix);
 +      return prefix_len;
  }
  
  int within_depth(const char *name, int namelen,
@@@ -1311,7 -1312,7 +1311,7 @@@ static enum exist_status directory_exis
  static enum path_treatment treat_directory(struct dir_struct *dir,
        struct untracked_cache_dir *untracked,
        const char *dirname, int len, int baselen, int exclude,
 -      const struct path_simplify *simplify)
 +      const struct pathspec *pathspec)
  {
        /* The "len-1" is to strip the final '/' */
        switch (directory_exists_in_index(dirname, len-1)) {
        untracked = lookup_untracked(dir->untracked, untracked,
                                     dirname + baselen, len - baselen);
        return read_directory_recursive(dir, dirname, len,
 -                                      untracked, 1, simplify);
 +                                      untracked, 1, pathspec);
  }
  
  /*
   * reading - if the path cannot possibly be in the pathspec,
   * return true, and we'll skip it early.
   */
 -static int simplify_away(const char *path, int pathlen, const struct path_simplify *simplify)
 +static int simplify_away(const char *path, int pathlen,
 +                       const struct pathspec *pathspec)
  {
 -      if (simplify) {
 -              for (;;) {
 -                      const char *match = simplify->path;
 -                      int len = simplify->len;
 +      int i;
  
 -                      if (!match)
 -                              break;
 -                      if (len > pathlen)
 -                              len = pathlen;
 -                      if (!memcmp(path, match, len))
 -                              return 0;
 -                      simplify++;
 -              }
 -              return 1;
 +      if (!pathspec || !pathspec->nr)
 +              return 0;
 +
 +      GUARD_PATHSPEC(pathspec,
 +                     PATHSPEC_FROMTOP |
 +                     PATHSPEC_MAXDEPTH |
 +                     PATHSPEC_LITERAL |
 +                     PATHSPEC_GLOB |
 +                     PATHSPEC_ICASE |
 +                     PATHSPEC_EXCLUDE);
 +
 +      for (i = 0; i < pathspec->nr; i++) {
 +              const struct pathspec_item *item = &pathspec->items[i];
 +              int len = item->nowildcard_len;
 +
 +              if (len > pathlen)
 +                      len = pathlen;
 +              if (!ps_strncmp(item, item->match, path, len))
 +                      return 0;
        }
 -      return 0;
 +
 +      return 1;
  }
  
  /*
   *   2. the path is a directory prefix of some element in the
   *      pathspec
   */
 -static int exclude_matches_pathspec(const char *path, int len,
 -              const struct path_simplify *simplify)
 -{
 -      if (simplify) {
 -              for (; simplify->path; simplify++) {
 -                      if (len == simplify->len
 -                          && !memcmp(path, simplify->path, len))
 -                              return 1;
 -                      if (len < simplify->len
 -                          && simplify->path[len] == '/'
 -                          && !memcmp(path, simplify->path, len))
 -                              return 1;
 -              }
 +static int exclude_matches_pathspec(const char *path, int pathlen,
 +                                  const struct pathspec *pathspec)
 +{
 +      int i;
 +
 +      if (!pathspec || !pathspec->nr)
 +              return 0;
 +
 +      GUARD_PATHSPEC(pathspec,
 +                     PATHSPEC_FROMTOP |
 +                     PATHSPEC_MAXDEPTH |
 +                     PATHSPEC_LITERAL |
 +                     PATHSPEC_GLOB |
 +                     PATHSPEC_ICASE |
 +                     PATHSPEC_EXCLUDE);
 +
 +      for (i = 0; i < pathspec->nr; i++) {
 +              const struct pathspec_item *item = &pathspec->items[i];
 +              int len = item->nowildcard_len;
 +
 +              if (len == pathlen &&
 +                  !ps_strncmp(item, item->match, path, pathlen))
 +                      return 1;
 +              if (len > pathlen &&
 +                  item->match[pathlen] == '/' &&
 +                  !ps_strncmp(item, item->match, path, pathlen))
 +                      return 1;
        }
        return 0;
  }
@@@ -1482,7 -1460,7 +1482,7 @@@ static enum path_treatment treat_one_pa
                                          struct untracked_cache_dir *untracked,
                                          struct strbuf *path,
                                          int baselen,
 -                                        const struct path_simplify *simplify,
 +                                        const struct pathspec *pathspec,
                                          int dtype, struct dirent *de)
  {
        int exclude;
        case DT_DIR:
                strbuf_addch(path, '/');
                return treat_directory(dir, untracked, path->buf, path->len,
 -                                     baselen, exclude, simplify);
 +                                     baselen, exclude, pathspec);
        case DT_REG:
        case DT_LNK:
                return exclude ? path_excluded : path_untracked;
@@@ -1546,7 -1524,7 +1546,7 @@@ static enum path_treatment treat_path_f
                                           struct cached_dir *cdir,
                                           struct strbuf *path,
                                           int baselen,
 -                                         const struct path_simplify *simplify)
 +                                         const struct pathspec *pathspec)
  {
        strbuf_setlen(path, baselen);
        if (!cdir->ucd) {
                 * with check_only set.
                 */
                return read_directory_recursive(dir, path->buf, path->len,
 -                                              cdir->ucd, 1, simplify);
 +                                              cdir->ucd, 1, pathspec);
        /*
         * We get path_recurse in the first run when
         * directory_exists_in_index() returns index_nonexistent. We
@@@ -1578,23 -1556,23 +1578,23 @@@ static enum path_treatment treat_path(s
                                      struct cached_dir *cdir,
                                      struct strbuf *path,
                                      int baselen,
 -                                    const struct path_simplify *simplify)
 +                                    const struct pathspec *pathspec)
  {
        int dtype;
        struct dirent *de = cdir->de;
  
        if (!de)
                return treat_path_fast(dir, untracked, cdir, path,
 -                                     baselen, simplify);
 +                                     baselen, pathspec);
        if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
 -      if (simplify_away(path->buf, path->len, simplify))
 +      if (simplify_away(path->buf, path->len, pathspec))
                return path_none;
  
        dtype = DTYPE(de);
 -      return treat_one_path(dir, untracked, path, baselen, simplify, dtype, de);
 +      return treat_one_path(dir, untracked, path, baselen, pathspec, dtype, de);
  }
  
  static void add_untracked(struct untracked_cache_dir *dir, const char *name)
@@@ -1725,7 -1703,7 +1725,7 @@@ static void close_cached_dir(struct cac
  static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                                    const char *base, int baselen,
                                    struct untracked_cache_dir *untracked, int check_only,
 -                                  const struct path_simplify *simplify)
 +                                  const struct pathspec *pathspec)
  {
        struct cached_dir cdir;
        enum path_treatment state, subdir_state, dir_state = path_none;
  
        while (!read_cached_dir(&cdir)) {
                /* check how the file or directory should be treated */
 -              state = treat_path(dir, untracked, &cdir, &path, baselen, simplify);
 +              state = treat_path(dir, untracked, &cdir, &path,
 +                                 baselen, pathspec);
  
                if (state > dir_state)
                        dir_state = state;
                                              path.buf + baselen,
                                              path.len - baselen);
                        subdir_state =
 -                              read_directory_recursive(dir, path.buf, path.len,
 -                                                       ud, check_only, simplify);
 +                              read_directory_recursive(dir, path.buf,
 +                                                       path.len, ud,
 +                                                       check_only, pathspec);
                        if (subdir_state > dir_state)
                                dir_state = subdir_state;
                }
                        else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
                                ((dir->flags & DIR_COLLECT_IGNORED) &&
                                exclude_matches_pathspec(path.buf, path.len,
 -                                      simplify)))
 +                                                       pathspec)))
                                dir_add_ignored(dir, path.buf, path.len);
                        break;
  
@@@ -1811,9 -1787,36 +1811,9 @@@ static int cmp_name(const void *p1, con
        return name_compare(e1->name, e1->len, e2->name, e2->len);
  }
  
 -static struct path_simplify *create_simplify(const char **pathspec)
 -{
 -      int nr, alloc = 0;
 -      struct path_simplify *simplify = NULL;
 -
 -      if (!pathspec)
 -              return NULL;
 -
 -      for (nr = 0 ; ; nr++) {
 -              const char *match;
 -              ALLOC_GROW(simplify, nr + 1, alloc);
 -              match = *pathspec++;
 -              if (!match)
 -                      break;
 -              simplify[nr].path = match;
 -              simplify[nr].len = simple_length(match);
 -      }
 -      simplify[nr].path = NULL;
 -      simplify[nr].len = 0;
 -      return simplify;
 -}
 -
 -static void free_simplify(struct path_simplify *simplify)
 -{
 -      free(simplify);
 -}
 -
  static int treat_leading_path(struct dir_struct *dir,
                              const char *path, int len,
 -                            const struct path_simplify *simplify)
 +                            const struct pathspec *pathspec)
  {
        struct strbuf sb = STRBUF_INIT;
        int baselen, rc = 0;
                strbuf_add(&sb, path, baselen);
                if (!is_directory(sb.buf))
                        break;
 -              if (simplify_away(sb.buf, sb.len, simplify))
 +              if (simplify_away(sb.buf, sb.len, pathspec))
                        break;
 -              if (treat_one_path(dir, NULL, &sb, baselen, simplify,
 +              if (treat_one_path(dir, NULL, &sb, baselen, pathspec,
                                   DT_DIR, NULL) == path_none)
                        break; /* do not recurse into it */
                if (len <= baselen) {
@@@ -2007,14 -2010,33 +2007,14 @@@ static struct untracked_cache_dir *vali
        return root;
  }
  
 -int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
 +int read_directory(struct dir_struct *dir, const char *path,
 +                 int len, const struct pathspec *pathspec)
  {
 -      struct path_simplify *simplify;
        struct untracked_cache_dir *untracked;
  
 -      /*
 -       * Check out create_simplify()
 -       */
 -      if (pathspec)
 -              GUARD_PATHSPEC(pathspec,
 -                             PATHSPEC_FROMTOP |
 -                             PATHSPEC_MAXDEPTH |
 -                             PATHSPEC_LITERAL |
 -                             PATHSPEC_GLOB |
 -                             PATHSPEC_ICASE |
 -                             PATHSPEC_EXCLUDE);
 -
        if (has_symlink_leading_path(path, len))
                return dir->nr;
  
 -      /*
 -       * exclude patterns are treated like positive ones in
 -       * create_simplify. Usually exclude patterns should be a
 -       * subset of positive ones, which has no impacts on
 -       * create_simplify().
 -       */
 -      simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
        untracked = validate_untracked_cache(dir, len, pathspec);
        if (!untracked)
                /*
                 * e.g. prep_exclude()
                 */
                dir->untracked = NULL;
 -      if (!len || treat_leading_path(dir, path, len, simplify))
 -              read_directory_recursive(dir, path, len, untracked, 0, simplify);
 -      free_simplify(simplify);
 +      if (!len || treat_leading_path(dir, path, len, pathspec))
 +              read_directory_recursive(dir, path, len, untracked, 0, pathspec);
        QSORT(dir->entries, dir->nr, cmp_name);
        QSORT(dir->ignored, dir->ignored_nr, cmp_name);
        if (dir->untracked) {
@@@ -2725,40 -2748,3 +2725,40 @@@ void untracked_cache_add_to_index(struc
  {
        untracked_cache_invalidate_path(istate, path);
  }
-       char *git_dir = xstrdup(real_path(git_dir_));
-       char *work_tree = xstrdup(real_path(work_tree_));
 +
 +/* Update gitfile and core.worktree setting to connect work tree and git dir */
 +void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
 +{
 +      struct strbuf file_name = STRBUF_INIT;
 +      struct strbuf rel_path = STRBUF_INIT;
++      char *git_dir = real_pathdup(git_dir_);
++      char *work_tree = real_pathdup(work_tree_);
 +
 +      /* Update gitfile */
 +      strbuf_addf(&file_name, "%s/.git", work_tree);
 +      write_file(file_name.buf, "gitdir: %s",
 +                 relative_path(git_dir, work_tree, &rel_path));
 +
 +      /* Update core.worktree setting */
 +      strbuf_reset(&file_name);
 +      strbuf_addf(&file_name, "%s/config", git_dir);
 +      git_config_set_in_file(file_name.buf, "core.worktree",
 +                             relative_path(work_tree, git_dir, &rel_path));
 +
 +      strbuf_release(&file_name);
 +      strbuf_release(&rel_path);
 +      free(work_tree);
 +      free(git_dir);
 +}
 +
 +/*
 + * Migrate the git directory of the given path from old_git_dir to new_git_dir.
 + */
 +void relocate_gitdir(const char *path, const char *old_git_dir, const char *new_git_dir)
 +{
 +      if (rename(old_git_dir, new_git_dir) < 0)
 +              die_errno(_("could not migrate git directory from '%s' to '%s'"),
 +                      old_git_dir, new_git_dir);
 +
 +      connect_work_tree_and_git_dir(path, new_git_dir);
 +}