Merge branch 'jk/cache-tree-protect-from-broken-libgit2'
authorJunio C Hamano <gitster@pobox.com>
Thu, 6 Nov 2014 18:51:35 +0000 (10:51 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 6 Nov 2014 18:51:35 +0000 (10:51 -0800)
The code to use cache-tree trusted the on-disk data too much
and fell into an infinite loop.

* jk/cache-tree-protect-from-broken-libgit2:
cache-tree: avoid infinite loop on zero-entry tree

1  2 
cache-tree.c
diff --combined cache-tree.c
index 215202c42d2243ebf704ac083937d65aa8497929,648e187491eec128e1cfb18ef64450144e2b323e..32772b95644d2f32841984b25b685379c6914ad4
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "lockfile.h"
  #include "tree.h"
  #include "tree-walk.h"
  #include "cache-tree.h"
@@@ -247,12 -246,9 +247,12 @@@ static int update_one(struct cache_tre
        struct strbuf buffer;
        int missing_ok = flags & WRITE_TREE_MISSING_OK;
        int dryrun = flags & WRITE_TREE_DRY_RUN;
 +      int repair = flags & WRITE_TREE_REPAIR;
        int to_invalidate = 0;
        int i;
  
 +      assert(!(dryrun && repair));
 +
        *skip_count = 0;
  
        if (0 <= it->entry_count && has_sha1_file(it->sha1))
                                    flags);
                if (subcnt < 0)
                        return subcnt;
+               if (!subcnt)
+                       die("index cache-tree records empty sub-tree");
                i += subcnt;
                sub->count = subcnt; /* to be used in the next loop */
                *skip_count += subskip;
                int pathlen, entlen;
                const unsigned char *sha1;
                unsigned mode;
 +              int expected_missing = 0;
  
                path = ce->name;
                pathlen = ce_namelen(ce);
                        i += sub->count;
                        sha1 = sub->cache_tree->sha1;
                        mode = S_IFDIR;
 -                      if (sub->cache_tree->entry_count < 0)
 +                      if (sub->cache_tree->entry_count < 0) {
                                to_invalidate = 1;
 +                              expected_missing = 1;
 +                      }
                }
                else {
                        sha1 = ce->sha1;
                }
                if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
                        strbuf_release(&buffer);
 +                      if (expected_missing)
 +                              return -1;
                        return error("invalid object %06o %s for '%.*s'",
                                mode, sha1_to_hex(sha1), entlen+baselen, path);
                }
  #endif
        }
  
 -      if (dryrun)
 +      if (repair) {
 +              unsigned char sha1[20];
 +              hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1);
 +              if (has_sha1_file(sha1))
 +                      hashcpy(it->sha1, sha1);
 +              else
 +                      to_invalidate = 1;
 +      } else if (dryrun)
                hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
        else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
                strbuf_release(&buffer);