Merge branch 'jk/tree-walk-overflow'
authorJunio C Hamano <gitster@pobox.com>
Thu, 22 Aug 2019 19:34:10 +0000 (12:34 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 22 Aug 2019 19:34:10 +0000 (12:34 -0700)
Codepaths to walk tree objects have been audited for integer
overflows and hardened.

* jk/tree-walk-overflow:
tree-walk: harden make_traverse_path() length computations
tree-walk: add a strbuf wrapper for make_traverse_path()
tree-walk: accept a raw length for traverse_path_len()
tree-walk: use size_t consistently
tree-walk: drop oid from traverse_info
setup_traverse_info(): stop copying oid

1  2 
builtin/merge-tree.c
cache-tree.c
tree-walk.c
tree-walk.h
unpack-trees.c
diff --combined builtin/merge-tree.c
index 97b54caeb90085e0fb4a88898ac87c7a5cb45eed,87d949cf882d7285cd9fec13bc22fbe8b9138c1f..e72714a5a87d02540f07fbbc65a86f66ed122cd8
@@@ -180,8 -180,9 +180,9 @@@ static struct merge_list *create_entry(
  
  static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
  {
-       char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz);
-       return make_traverse_path(path, info, n);
+       struct strbuf buf = STRBUF_INIT;
+       strbuf_make_traverse_path(&buf, info, n->path, n->pathlen);
+       return strbuf_detach(&buf, NULL);
  }
  
  static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result)
  static void unresolved_directory(const struct traverse_info *info,
                                 struct name_entry n[3])
  {
 +      struct repository *r = the_repository;
        char *newbase;
        struct name_entry *p;
        struct tree_desc t[3];
        newbase = traverse_path(info, p);
  
  #define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)
 -      buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
 -      buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
 -      buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
 +      buf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));
 +      buf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));
 +      buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));
  #undef ENTRY_OID
  
        merge_trees(t, newbase);
@@@ -352,16 -352,14 +353,16 @@@ static void merge_trees(struct tree_des
        traverse_trees(&the_index, 3, t, &info);
  }
  
 -static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
 +static void *get_tree_descriptor(struct repository *r,
 +                               struct tree_desc *desc,
 +                               const char *rev)
  {
        struct object_id oid;
        void *buf;
  
 -      if (get_oid(rev, &oid))
 +      if (repo_get_oid(r, rev, &oid))
                die("unknown rev %s", rev);
 -      buf = fill_tree_descriptor(desc, &oid);
 +      buf = fill_tree_descriptor(r, desc, &oid);
        if (!buf)
                die("%s is not a tree", rev);
        return buf;
  
  int cmd_merge_tree(int argc, const char **argv, const char *prefix)
  {
 +      struct repository *r = the_repository;
        struct tree_desc t[3];
        void *buf1, *buf2, *buf3;
  
        if (argc != 4)
                usage(merge_tree_usage);
  
 -      buf1 = get_tree_descriptor(t+0, argv[1]);
 -      buf2 = get_tree_descriptor(t+1, argv[2]);
 -      buf3 = get_tree_descriptor(t+2, argv[3]);
 +      buf1 = get_tree_descriptor(r, t+0, argv[1]);
 +      buf2 = get_tree_descriptor(r, t+1, argv[2]);
 +      buf3 = get_tree_descriptor(r, t+2, argv[3]);
        merge_trees(t, "");
        free(buf1);
        free(buf2);
diff --combined cache-tree.c
index 706ffcf1883a01d1ac6f3e966f9d10ebc2e1d8dd,badf5669f1d69ca70b79d2b1607d12280f544cae..c22161f987152ea18715b00815e987847786c5ff
@@@ -6,8 -6,8 +6,8 @@@
  #include "object-store.h"
  #include "replace-object.h"
  
 -#ifndef DEBUG
 -#define DEBUG 0
 +#ifndef DEBUG_CACHE_TREE
 +#define DEBUG_CACHE_TREE 0
  #endif
  
  struct cache_tree *cache_tree(void)
@@@ -111,7 -111,7 +111,7 @@@ static int do_invalidate_path(struct ca
        int namelen;
        struct cache_tree_sub *down;
  
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
        fprintf(stderr, "cache-tree invalidate <%s>\n", path);
  #endif
  
@@@ -398,7 -398,7 +398,7 @@@ static int update_one(struct cache_tre
                strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
                strbuf_add(&buffer, oid->hash, the_hash_algo->rawsz);
  
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
                fprintf(stderr, "cache-tree update-one %o %.*s\n",
                        mode, entlen, path + baselen);
  #endif
  
        strbuf_release(&buffer);
        it->entry_count = to_invalidate ? -1 : i - *skip_count;
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
        fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
                it->entry_count, it->subtree_nr,
                oid_to_hex(&it->oid));
@@@ -462,7 -462,7 +462,7 @@@ static void write_one(struct strbuf *bu
        strbuf_add(buffer, path, pathlen);
        strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr);
  
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
        if (0 <= it->entry_count)
                fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n",
                        pathlen, path, it->entry_count, it->subtree_nr,
@@@ -536,7 -536,7 +536,7 @@@ static struct cache_tree *read_one(cons
                size -= rawsz;
        }
  
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
        if (0 <= it->entry_count)
                fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n",
                        *buffer, it->entry_count, subtree_nr,
@@@ -713,7 -713,7 +713,7 @@@ static struct cache_tree *find_cache_tr
        if (!info->prev)
                return root;
        our_parent = find_cache_tree_from_traversal(root, info->prev);
-       return cache_tree_find(our_parent, info->name.path);
+       return cache_tree_find(our_parent, info->name);
  }
  
  int cache_tree_matches_traversal(struct cache_tree *root,
diff --combined tree-walk.c
index c20b62f49e4709253d71e18eea0a2472809d8e9d,4f1e9d79ab9aa2a13a4dc63edbaf2b4c613105d3..bea819d826fb1c855a7b92dbe2605885c12f5c16
@@@ -81,15 -81,13 +81,15 @@@ int init_tree_desc_gently(struct tree_d
        return result;
  }
  
 -void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid)
 +void *fill_tree_descriptor(struct repository *r,
 +                         struct tree_desc *desc,
 +                         const struct object_id *oid)
  {
        unsigned long size = 0;
        void *buf = NULL;
  
        if (oid) {
 -              buf = read_object_with_reference(oid, tree_type, &size, NULL);
 +              buf = read_object_with_reference(r, oid, tree_type, &size, NULL);
                if (!buf)
                        die("unable to read tree %s", oid_to_hex(oid));
        }
@@@ -170,40 -168,61 +170,61 @@@ int tree_entry_gently(struct tree_desc 
  
  void setup_traverse_info(struct traverse_info *info, const char *base)
  {
-       int pathlen = strlen(base);
+       size_t pathlen = strlen(base);
        static struct traverse_info dummy;
  
        memset(info, 0, sizeof(*info));
        if (pathlen && base[pathlen-1] == '/')
                pathlen--;
        info->pathlen = pathlen ? pathlen + 1 : 0;
-       info->name.path = base;
-       info->name.pathlen = pathlen;
-       if (pathlen) {
-               hashcpy(info->name.oid.hash, (const unsigned char *)base + pathlen + 1);
+       info->name = base;
+       info->namelen = pathlen;
+       if (pathlen)
                info->prev = &dummy;
-       }
  }
  
- char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
+ char *make_traverse_path(char *path, size_t pathlen,
+                        const struct traverse_info *info,
+                        const char *name, size_t namelen)
  {
-       int len = tree_entry_len(n);
-       int pathlen = info->pathlen;
+       /* Always points to the end of the name we're about to add */
+       size_t pos = st_add(info->pathlen, namelen);
+       if (pos >= pathlen)
+               BUG("too small buffer passed to make_traverse_path");
  
-       path[pathlen + len] = 0;
+       path[pos] = 0;
        for (;;) {
-               memcpy(path + pathlen, n->path, len);
-               if (!pathlen)
+               if (pos < namelen)
+                       BUG("traverse_info pathlen does not match strings");
+               pos -= namelen;
+               memcpy(path + pos, name, namelen);
+               if (!pos)
                        break;
-               path[--pathlen] = '/';
-               n = &info->name;
-               len = tree_entry_len(n);
+               path[--pos] = '/';
+               if (!info)
+                       BUG("traverse_info ran out of list items");
+               name = info->name;
+               namelen = info->namelen;
                info = info->prev;
-               pathlen -= len;
        }
        return path;
  }
  
+ void strbuf_make_traverse_path(struct strbuf *out,
+                              const struct traverse_info *info,
+                              const char *name, size_t namelen)
+ {
+       size_t len = traverse_path_len(info, namelen);
+       strbuf_grow(out, len);
+       make_traverse_path(out->buf + out->len, out->alloc - out->len,
+                          info, name, namelen);
+       strbuf_setlen(out, out->len + len);
+ }
  struct tree_desc_skip {
        struct tree_desc_skip *prev;
        const void *ptr;
@@@ -400,13 -419,12 +421,12 @@@ int traverse_trees(struct index_state *
                tx[i].d = t[i];
  
        if (info->prev) {
-               strbuf_grow(&base, info->pathlen);
-               make_traverse_path(base.buf, info->prev, &info->name);
-               base.buf[info->pathlen-1] = '/';
-               strbuf_setlen(&base, info->pathlen);
-               traverse_path = xstrndup(base.buf, info->pathlen);
+               strbuf_make_traverse_path(&base, info->prev,
+                                         info->name, info->namelen);
+               strbuf_addch(&base, '/');
+               traverse_path = xstrndup(base.buf, base.len);
        } else {
-               traverse_path = xstrndup(info->name.path, info->pathlen);
+               traverse_path = xstrndup(info->name, info->pathlen);
        }
        info->traverse_path = traverse_path;
        for (;;) {
@@@ -502,9 -520,7 +522,9 @@@ struct dir_state 
        struct object_id oid;
  };
  
 -static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned short *mode)
 +static int find_tree_entry(struct repository *r, struct tree_desc *t,
 +                         const char *name, struct object_id *result,
 +                         unsigned short *mode)
  {
        int namelen = strlen(name);
        while (t->size) {
                        oidcpy(result, &oid);
                        return 0;
                }
 -              return get_tree_entry(&oid, name + entrylen, result, mode);
 +              return get_tree_entry(r, &oid, name + entrylen, result, mode);
        }
        return -1;
  }
  
 -int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned short *mode)
 +int get_tree_entry(struct repository *r,
 +                 const struct object_id *tree_oid,
 +                 const char *name,
 +                 struct object_id *oid,
 +                 unsigned short *mode)
  {
        int retval;
        void *tree;
        unsigned long size;
        struct object_id root;
  
 -      tree = read_object_with_reference(tree_oid, tree_type, &size, &root);
 +      tree = read_object_with_reference(r, tree_oid, tree_type, &size, &root);
        if (!tree)
                return -1;
  
        } else {
                struct tree_desc t;
                init_tree_desc(&t, tree, size);
 -              retval = find_tree_entry(&t, name, oid, mode);
 +              retval = find_tree_entry(r, &t, name, oid, mode);
        }
        free(tree);
        return retval;
   * See the code for enum get_oid_result for a description of
   * the return values.
   */
 -enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode)
 +enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
 +              struct object_id *tree_oid, const char *name,
 +              struct object_id *result, struct strbuf *result_path,
 +              unsigned short *mode)
  {
        int retval = MISSING_OBJECT;
        struct dir_state *parents = NULL;
                        void *tree;
                        struct object_id root;
                        unsigned long size;
 -                      tree = read_object_with_reference(&current_tree_oid,
 +                      tree = read_object_with_reference(r,
 +                                                        &current_tree_oid,
                                                          tree_type, &size,
                                                          &root);
                        if (!tree)
                }
  
                /* Look up the first (or only) path component in the tree. */
 -              find_result = find_tree_entry(&t, namebuf.buf,
 +              find_result = find_tree_entry(r, &t, namebuf.buf,
                                              &current_tree_oid, mode);
                if (find_result) {
                        goto done;
                         */
                        retval = DANGLING_SYMLINK;
  
 -                      contents = read_object_file(&current_tree_oid, &type,
 +                      contents = repo_read_object_file(r,
 +                                                  &current_tree_oid, &type,
                                                    &link_len);
  
                        if (!contents)
diff --combined tree-walk.h
index 2a5db29e8f196f535c75fbf84dcae8f45ca5c4a3,a3ad54e6ce7def4711268c9f972556efd53a6ff1..abe2caf4e0faae271967787b02137505a22ac5ff
@@@ -45,21 -45,22 +45,24 @@@ int init_tree_desc_gently(struct tree_d
  int tree_entry(struct tree_desc *, struct name_entry *);
  int tree_entry_gently(struct tree_desc *, struct name_entry *);
  
 -void *fill_tree_descriptor(struct tree_desc *desc, const struct object_id *oid);
 +void *fill_tree_descriptor(struct repository *r,
 +                         struct tree_desc *desc,
 +                         const struct object_id *oid);
  
  struct traverse_info;
  typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
  int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);
  
 -enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode);
 +enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r, struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode);
  
  struct traverse_info {
        const char *traverse_path;
        struct traverse_info *prev;
-       struct name_entry name;
-       int pathlen;
+       const char *name;
+       size_t namelen;
+       unsigned mode;
+       size_t pathlen;
        struct pathspec *pathspec;
  
        unsigned long df_conflicts;
        int show_all_errors;
  };
  
 -int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned short *);
 +int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *);
- char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
+ char *make_traverse_path(char *path, size_t pathlen, const struct traverse_info *info,
+                        const char *name, size_t namelen);
+ void strbuf_make_traverse_path(struct strbuf *out,
+                              const struct traverse_info *info,
+                              const char *name, size_t namelen);
  void setup_traverse_info(struct traverse_info *info, const char *base);
  
- static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
+ static inline size_t traverse_path_len(const struct traverse_info *info,
+                                      size_t namelen)
  {
-       return info->pathlen + tree_entry_len(n);
+       return st_add(info->pathlen, namelen);
  }
  
  /* in general, positive means "kind of interesting" */
diff --combined unpack-trees.c
index 62276d4fef5f67e7147377f16e24e30abca0906a,65c4677578ffafc56259a91bd13797e063d87a49..50f257bd5c768ae87f8e526c587fa11afad98ad6
@@@ -315,7 -315,7 +315,7 @@@ static struct progress *get_progress(st
                        total++;
        }
  
 -      return start_delayed_progress(_("Checking out files"), total);
 +      return start_delayed_progress(_("Updating files"), total);
  }
  
  static void setup_collided_checkout_detection(struct checkout *state,
@@@ -632,7 -632,7 +632,7 @@@ static int unpack_index_entry(struct ca
        return ret;
  }
  
- static int find_cache_pos(struct traverse_info *, const struct name_entry *);
+ static int find_cache_pos(struct traverse_info *, const char *p, size_t len);
  
  static void restore_cache_bottom(struct traverse_info *info, int bottom)
  {
@@@ -651,7 -651,7 +651,7 @@@ static int switch_cache_bottom(struct t
        if (o->diff_index_cached)
                return 0;
        ret = o->cache_bottom;
-       pos = find_cache_pos(info->prev, &info->name);
+       pos = find_cache_pos(info->prev, info->name, info->namelen);
  
        if (pos < -1)
                o->cache_bottom = -2 - pos;
@@@ -686,21 -686,19 +686,19 @@@ static int index_pos_by_traverse_info(s
                                      struct traverse_info *info)
  {
        struct unpack_trees_options *o = info->data;
-       int len = traverse_path_len(info, names);
-       char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */);
+       struct strbuf name = STRBUF_INIT;
        int pos;
  
-       make_traverse_path(name, info, names);
-       name[len++] = '/';
-       name[len] = '\0';
-       pos = index_name_pos(o->src_index, name, len);
+       strbuf_make_traverse_path(&name, info, names->path, names->pathlen);
+       strbuf_addch(&name, '/');
+       pos = index_name_pos(o->src_index, name.buf, name.len);
        if (pos >= 0)
                BUG("This is a directory and should not exist in index");
        pos = -pos - 1;
-       if (!starts_with(o->src_index->cache[pos]->name, name) ||
-           (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name)))
+       if (!starts_with(o->src_index->cache[pos]->name, name.buf) ||
+           (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name.buf)))
                BUG("pos must point at the first entry in this directory");
-       free(name);
+       strbuf_release(&name);
        return pos;
  }
  
@@@ -811,8 -809,10 +809,10 @@@ static int traverse_trees_recursive(in
        newinfo = *info;
        newinfo.prev = info;
        newinfo.pathspec = info->pathspec;
-       newinfo.name = *p;
-       newinfo.pathlen += tree_entry_len(p) + 1;
+       newinfo.name = p->path;
+       newinfo.namelen = p->pathlen;
+       newinfo.mode = p->mode;
+       newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);
        newinfo.df_conflicts |= df_conflicts;
  
        /*
                        const struct object_id *oid = NULL;
                        if (dirmask & 1)
                                oid = &names[i].oid;
 -                      buf[nr_buf++] = fill_tree_descriptor(t + i, oid);
 +                      buf[nr_buf++] = fill_tree_descriptor(the_repository, t + i, oid);
                }
        }
  
   * itself - the caller needs to do the final check for the cache
   * entry having more data at the end!
   */
- static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
+ static int do_compare_entry_piecewise(const struct cache_entry *ce,
+                                     const struct traverse_info *info,
+                                     const char *name, size_t namelen,
+                                     unsigned mode)
  {
-       int len, pathlen, ce_len;
+       int pathlen, ce_len;
        const char *ce_name;
  
        if (info->prev) {
                int cmp = do_compare_entry_piecewise(ce, info->prev,
-                                                    &info->name);
+                                                    info->name, info->namelen,
+                                                    info->mode);
                if (cmp)
                        return cmp;
        }
        ce_len -= pathlen;
        ce_name = ce->name + pathlen;
  
-       len = tree_entry_len(n);
-       return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
+       return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
  }
  
  static int do_compare_entry(const struct cache_entry *ce,
                            const struct traverse_info *info,
-                           const struct name_entry *n)
+                           const char *name, size_t namelen,
+                           unsigned mode)
  {
-       int len, pathlen, ce_len;
+       int pathlen, ce_len;
        const char *ce_name;
        int cmp;
  
         * it is quicker to use the precomputed version.
         */
        if (!info->traverse_path)
-               return do_compare_entry_piecewise(ce, info, n);
+               return do_compare_entry_piecewise(ce, info, name, namelen, mode);
  
        cmp = strncmp(ce->name, info->traverse_path, info->pathlen);
        if (cmp)
        ce_len -= pathlen;
        ce_name = ce->name + pathlen;
  
-       len = tree_entry_len(n);
-       return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
+       return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
  }
  
  static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
  {
-       int cmp = do_compare_entry(ce, info, n);
+       int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);
        if (cmp)
                return cmp;
  
         * Even if the beginning compared identically, the ce should
         * compare as bigger than a directory leading up to it!
         */
-       return ce_namelen(ce) > traverse_path_len(info, n);
+       return ce_namelen(ce) > traverse_path_len(info, tree_entry_len(n));
  }
  
  static int ce_in_traverse_path(const struct cache_entry *ce,
  {
        if (!info->prev)
                return 1;
-       if (do_compare_entry(ce, info->prev, &info->name))
+       if (do_compare_entry(ce, info->prev,
+                            info->name, info->namelen, info->mode))
                return 0;
        /*
         * If ce (blob) is the same name as the path (which is a tree
@@@ -954,7 -958,7 +958,7 @@@ static struct cache_entry *create_ce_en
        struct index_state *istate,
        int is_transient)
  {
-       int len = traverse_path_len(info, n);
+       size_t len = traverse_path_len(info, tree_entry_len(n));
        struct cache_entry *ce =
                is_transient ?
                make_empty_transient_cache_entry(len) :
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
        oidcpy(&ce->oid, &n->oid);
-       make_traverse_path(ce->name, info, n);
+       /* len+1 because the cache_entry allocates space for NUL */
+       make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen);
  
        return ce;
  }
@@@ -1057,13 -1062,12 +1062,12 @@@ static int unpack_failed(struct unpack_
   * the directory.
   */
  static int find_cache_pos(struct traverse_info *info,
-                         const struct name_entry *p)
+                         const char *p, size_t p_len)
  {
        int pos;
        struct unpack_trees_options *o = info->data;
        struct index_state *index = o->src_index;
        int pfxlen = info->pathlen;
-       int p_len = tree_entry_len(p);
  
        for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
                const struct cache_entry *ce = index->cache[pos];
                        ce_len = ce_slash - ce_name;
                else
                        ce_len = ce_namelen(ce) - pfxlen;
-               cmp = name_compare(p->path, p_len, ce_name, ce_len);
+               cmp = name_compare(p, p_len, ce_name, ce_len);
                /*
                 * Exact match; if we have a directory we need to
                 * delay returning it.
                 * E.g.  ce_name == "t-i", and p->path == "t"; we may
                 * have "t/a" in the index.
                 */
-               if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) &&
+               if (p_len < ce_len && !memcmp(ce_name, p, p_len) &&
                    ce_name[p_len] < '/')
                        continue; /* keep looking */
                break;
  static struct cache_entry *find_cache_entry(struct traverse_info *info,
                                            const struct name_entry *p)
  {
-       int pos = find_cache_pos(info, p);
+       int pos = find_cache_pos(info, p->path, p->pathlen);
        struct unpack_trees_options *o = info->data;
  
        if (0 <= pos)
@@@ -1138,10 -1142,10 +1142,10 @@@ static void debug_path(struct traverse_
  {
        if (info->prev) {
                debug_path(info->prev);
-               if (*info->prev->name.path)
+               if (*info->prev->name)
                        putchar('/');
        }
-       printf("%s", info->name.path);
+       printf("%s", info->name);
  }
  
  static void debug_name_entry(int i, struct name_entry *n)