Merge branch 'jk/ewah-bounds-check'
authorJunio C Hamano <gitster@pobox.com>
Mon, 18 Jun 2018 18:23:22 +0000 (11:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 18 Jun 2018 18:23:22 +0000 (11:23 -0700)
The code to read compressed bitmap was not careful to avoid reading
past the end of the file, which has been corrected.

* jk/ewah-bounds-check:
ewah: adjust callers of ewah_read_mmap()
ewah_read_mmap: bounds-check mmap reads

1  2 
dir.c
pack-bitmap.c
t/t5310-pack-bitmaps.sh
diff --combined dir.c
index ccf8b4975e8645182da783b2367e31401a4cdadb,c9714cfb402457a3a84bb9b159ca3d4d9bc3cad8..fe9bf58e4c72790dca5492de4bd98df06798d27a
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -19,7 -19,6 +19,7 @@@
  #include "varint.h"
  #include "ewah/ewok.h"
  #include "fsmonitor.h"
 +#include "submodule-config.h"
  
  /*
   * Tells read_directory_recursive how a file or directory should be treated.
@@@ -232,10 -231,12 +232,10 @@@ int within_depth(const char *name, int 
   *     1 along with { data, size } of the (possibly augmented) buffer
   *       when successful.
   *
 - * Optionally updates the given sha1_stat with the given OID (when valid).
 + * Optionally updates the given oid_stat with the given OID (when valid).
   */
 -static int do_read_blob(const struct object_id *oid,
 -                      struct sha1_stat *sha1_stat,
 -                      size_t *size_out,
 -                      char **data_out)
 +static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
 +                      size_t *size_out, char **data_out)
  {
        enum object_type type;
        unsigned long sz;
        *size_out = 0;
        *data_out = NULL;
  
 -      data = read_sha1_file(oid->hash, &type, &sz);
 +      data = read_object_file(oid, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return -1;
        }
  
 -      if (sha1_stat) {
 -              memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat));
 -              hashcpy(sha1_stat->sha1, oid->hash);
 +      if (oid_stat) {
 +              memset(&oid_stat->stat, 0, sizeof(oid_stat->stat));
 +              oidcpy(&oid_stat->oid, oid);
        }
  
        if (sz == 0) {
@@@ -653,8 -654,9 +653,8 @@@ void add_exclude(const char *string, co
  
  static int read_skip_worktree_file_from_index(const struct index_state *istate,
                                              const char *path,
 -                                            size_t *size_out,
 -                                            char **data_out,
 -                                            struct sha1_stat *sha1_stat)
 +                                            size_t *size_out, char **data_out,
 +                                            struct oid_stat *oid_stat)
  {
        int pos, len;
  
        if (!ce_skip_worktree(istate->cache[pos]))
                return -1;
  
 -      return do_read_blob(&istate->cache[pos]->oid, sha1_stat, size_out, data_out);
 +      return do_read_blob(&istate->cache[pos]->oid, oid_stat, size_out, data_out);
  }
  
  /*
@@@ -745,8 -747,8 +745,8 @@@ static struct untracked_cache_dir *look
        FLEX_ALLOC_MEM(d, name, name, len);
  
        ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc);
 -      memmove(dir->dirs + first + 1, dir->dirs + first,
 -              (dir->dirs_nr - first) * sizeof(*dir->dirs));
 +      MOVE_ARRAY(dir->dirs + first + 1, dir->dirs + first,
 +                 dir->dirs_nr - first);
        dir->dirs_nr++;
        dir->dirs[first] = d;
        return d;
@@@ -772,16 -774,7 +772,16 @@@ static void invalidate_directory(struc
                                 struct untracked_cache_dir *dir)
  {
        int i;
 -      uc->dir_invalidated++;
 +
 +      /*
 +       * Invalidation increment here is just roughly correct. If
 +       * untracked_nr or any of dirs[].recurse is non-zero, we
 +       * should increment dir_invalidated too. But that's more
 +       * expensive to do.
 +       */
 +      if (dir->valid)
 +              uc->dir_invalidated++;
 +
        dir->valid = 0;
        dir->untracked_nr = 0;
        for (i = 0; i < dir->dirs_nr; i++)
@@@ -802,8 -795,9 +802,8 @@@ static int add_excludes_from_buffer(cha
   * ss_valid is non-zero, "ss" must contain good value as input.
   */
  static int add_excludes(const char *fname, const char *base, int baselen,
 -                      struct exclude_list *el,
 -                      struct index_state *istate,
 -                      struct sha1_stat *sha1_stat)
 +                      struct exclude_list *el, struct index_state *istate,
 +                      struct oid_stat *oid_stat)
  {
        struct stat st;
        int r;
                        return -1;
                r = read_skip_worktree_file_from_index(istate, fname,
                                                       &size, &buf,
 -                                                     sha1_stat);
 +                                                     oid_stat);
                if (r != 1)
                        return r;
        } else {
                size = xsize_t(st.st_size);
                if (size == 0) {
 -                      if (sha1_stat) {
 -                              fill_stat_data(&sha1_stat->stat, &st);
 -                              hashcpy(sha1_stat->sha1, EMPTY_BLOB_SHA1_BIN);
 -                              sha1_stat->valid = 1;
 +                      if (oid_stat) {
 +                              fill_stat_data(&oid_stat->stat, &st);
 +                              oidcpy(&oid_stat->oid, the_hash_algo->empty_blob);
 +                              oid_stat->valid = 1;
                        }
                        close(fd);
                        return 0;
                }
                buf[size++] = '\n';
                close(fd);
 -              if (sha1_stat) {
 +              if (oid_stat) {
                        int pos;
 -                      if (sha1_stat->valid &&
 -                          !match_stat_data_racy(istate, &sha1_stat->stat, &st))
 +                      if (oid_stat->valid &&
 +                          !match_stat_data_racy(istate, &oid_stat->stat, &st))
                                ; /* no content change, ss->sha1 still good */
                        else if (istate &&
                                 (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 &&
                                 !ce_stage(istate->cache[pos]) &&
                                 ce_uptodate(istate->cache[pos]) &&
                                 !would_convert_to_git(istate, fname))
 -                              hashcpy(sha1_stat->sha1,
 -                                      istate->cache[pos]->oid.hash);
 +                              oidcpy(&oid_stat->oid,
 +                                     &istate->cache[pos]->oid);
                        else
 -                              hash_sha1_file(buf, size, "blob", sha1_stat->sha1);
 -                      fill_stat_data(&sha1_stat->stat, &st);
 -                      sha1_stat->valid = 1;
 +                              hash_object_file(buf, size, "blob",
 +                                               &oid_stat->oid);
 +                      fill_stat_data(&oid_stat->stat, &st);
 +                      oid_stat->valid = 1;
                }
        }
  
@@@ -937,7 -930,7 +937,7 @@@ struct exclude_list *add_exclude_list(s
   * Used to set up core.excludesfile and .git/info/exclude lists.
   */
  static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
 -                                   struct sha1_stat *sha1_stat)
 +                                   struct oid_stat *oid_stat)
  {
        struct exclude_list *el;
        /*
        if (!dir->untracked)
                dir->unmanaged_exclude_files++;
        el = add_exclude_list(dir, EXC_FILE, fname);
 -      if (add_excludes(fname, "", 0, el, NULL, sha1_stat) < 0)
 +      if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0)
                die("cannot use %s as an exclude file", fname);
  }
  
@@@ -1187,7 -1180,7 +1187,7 @@@ static void prep_exclude(struct dir_str
  
        while (current < baselen) {
                const char *cp;
 -              struct sha1_stat sha1_stat;
 +              struct oid_stat oid_stat;
  
                stk = xcalloc(1, sizeof(*stk));
                if (current < 0) {
                }
  
                /* Try to read per-directory file */
 -              hashclr(sha1_stat.sha1);
 -              sha1_stat.valid = 0;
 +              oidclr(&oid_stat.oid);
 +              oid_stat.valid = 0;
                if (dir->exclude_per_dir &&
                    /*
                     * If we know that no files have been added in
                    (!untracked || !untracked->valid ||
                     /*
                      * .. and .gitignore does not exist before
 -                    * (i.e. null exclude_sha1). Then we can skip
 +                    * (i.e. null exclude_oid). Then we can skip
                      * loading .gitignore, which would result in
                      * ENOENT anyway.
                      */
 -                   !is_null_sha1(untracked->exclude_sha1))) {
 +                   !is_null_oid(&untracked->exclude_oid))) {
                        /*
                         * dir->basebuf gets reused by the traversal, but we
                         * need fname to remain unchanged to ensure the src
                        strbuf_addstr(&sb, dir->exclude_per_dir);
                        el->src = strbuf_detach(&sb, NULL);
                        add_excludes(el->src, el->src, stk->baselen, el, istate,
 -                                   untracked ? &sha1_stat : NULL);
 +                                   untracked ? &oid_stat : NULL);
                }
                /*
                 * NEEDSWORK: when untracked cache is enabled, prep_exclude()
                 * order, though, if you do that.
                 */
                if (untracked &&
 -                  hashcmp(sha1_stat.sha1, untracked->exclude_sha1)) {
 +                  oidcmp(&oid_stat.oid, &untracked->exclude_oid)) {
                        invalidate_gitignore(dir->untracked, untracked);
 -                      hashcpy(untracked->exclude_sha1, sha1_stat.sha1);
 +                      oidcpy(&untracked->exclude_oid, &oid_stat.oid);
                }
                dir->exclude_stack = stk;
                current = stk->baselen;
@@@ -1780,7 -1773,7 +1780,7 @@@ static enum path_treatment treat_path(s
        if (!de)
                return treat_path_fast(dir, untracked, cdir, istate, path,
                                       baselen, pathspec);
 -      if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
 +      if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
@@@ -1816,19 -1809,24 +1816,19 @@@ static int valid_cached_dir(struct dir_
         */
        refresh_fsmonitor(istate);
        if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
 -              if (stat(path->len ? path->buf : ".", &st)) {
 -                      invalidate_directory(dir->untracked, untracked);
 +              if (lstat(path->len ? path->buf : ".", &st)) {
                        memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
                        return 0;
                }
                if (!untracked->valid ||
                        match_stat_data_racy(istate, &untracked->stat_data, &st)) {
 -                      if (untracked->valid)
 -                              invalidate_directory(dir->untracked, untracked);
                        fill_stat_data(&untracked->stat_data, &st);
                        return 0;
                }
        }
  
 -      if (untracked->check_only != !!check_only) {
 -              invalidate_directory(dir->untracked, untracked);
 +      if (untracked->check_only != !!check_only)
                return 0;
 -      }
  
        /*
         * prep_exclude will be called eventually on this directory,
@@@ -1855,20 -1853,13 +1855,20 @@@ static int open_cached_dir(struct cache
                           struct strbuf *path,
                           int check_only)
  {
 +      const char *c_path;
 +
        memset(cdir, 0, sizeof(*cdir));
        cdir->untracked = untracked;
        if (valid_cached_dir(dir, untracked, istate, path, check_only))
                return 0;
 -      cdir->fdir = opendir(path->len ? path->buf : ".");
 -      if (dir->untracked)
 +      c_path = path->len ? path->buf : ".";
 +      cdir->fdir = opendir(c_path);
 +      if (!cdir->fdir)
 +              warning_errno(_("could not open directory '%s'"), c_path);
 +      if (dir->untracked) {
 +              invalidate_directory(dir->untracked, untracked);
                dir->untracked->dir_opened++;
 +      }
        if (!cdir->fdir)
                return -1;
        return 0;
@@@ -2173,13 -2164,8 +2173,13 @@@ static struct untracked_cache_dir *vali
                                                      const struct pathspec *pathspec)
  {
        struct untracked_cache_dir *root;
 +      static int untracked_cache_disabled = -1;
  
 -      if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE"))
 +      if (!dir->untracked)
 +              return NULL;
 +      if (untracked_cache_disabled < 0)
 +              untracked_cache_disabled = git_env_bool("GIT_DISABLE_UNTRACKED_CACHE", 0);
 +      if (untracked_cache_disabled)
                return NULL;
  
        /*
  
        /* Validate $GIT_DIR/info/exclude and core.excludesfile */
        root = dir->untracked->root;
 -      if (hashcmp(dir->ss_info_exclude.sha1,
 -                  dir->untracked->ss_info_exclude.sha1)) {
 +      if (oidcmp(&dir->ss_info_exclude.oid,
 +                 &dir->untracked->ss_info_exclude.oid)) {
                invalidate_gitignore(dir->untracked, root);
                dir->untracked->ss_info_exclude = dir->ss_info_exclude;
        }
 -      if (hashcmp(dir->ss_excludes_file.sha1,
 -                  dir->untracked->ss_excludes_file.sha1)) {
 +      if (oidcmp(&dir->ss_excludes_file.oid,
 +                 &dir->untracked->ss_excludes_file.oid)) {
                invalidate_gitignore(dir->untracked, root);
                dir->untracked->ss_excludes_file = dir->ss_excludes_file;
        }
@@@ -2262,7 -2248,6 +2262,7 @@@ int read_directory(struct dir_struct *d
                   const char *path, int len, const struct pathspec *pathspec)
  {
        struct untracked_cache_dir *untracked;
 +      uint64_t start = getnanotime();
  
        if (has_symlink_leading_path(path, len))
                return dir->nr;
                dir->nr = i;
        }
  
 +      trace_performance_since(start, "read directory %.*s", len, path);
        if (dir->untracked) {
 +              static int force_untracked_cache = -1;
                static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
 +
 +              if (force_untracked_cache < 0)
 +                      force_untracked_cache =
 +                              git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0);
                trace_printf_key(&trace_untracked_stats,
                                 "node creation: %u\n"
                                 "gitignore invalidation: %u\n"
                                 dir->untracked->gitignore_invalidated,
                                 dir->untracked->dir_invalidated,
                                 dir->untracked->dir_opened);
 -              if (dir->untracked == istate->untracked &&
 +              if (force_untracked_cache &&
 +                      dir->untracked == istate->untracked &&
                    (dir->untracked->dir_opened ||
                     dir->untracked->gitignore_invalidated ||
                     dir->untracked->dir_invalidated))
@@@ -2623,10 -2601,9 +2623,10 @@@ static void write_one_dir(struct untrac
                stat_data_to_disk(&stat_data, &untracked->stat_data);
                strbuf_add(&wd->sb_stat, &stat_data, sizeof(stat_data));
        }
 -      if (!is_null_sha1(untracked->exclude_sha1)) {
 +      if (!is_null_oid(&untracked->exclude_oid)) {
                ewah_set(wd->sha1_valid, i);
 -              strbuf_add(&wd->sb_sha1, untracked->exclude_sha1, 20);
 +              strbuf_add(&wd->sb_sha1, untracked->exclude_oid.hash,
 +                         the_hash_algo->rawsz);
        }
  
        intlen = encode_varint(untracked->untracked_nr, intbuf);
@@@ -2661,8 -2638,8 +2661,8 @@@ void write_untracked_extension(struct s
        FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
        stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
        stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
 -      hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
 -      hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
 +      hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.oid.hash);
 +      hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.oid.hash);
        ouc->dir_flags = htonl(untracked->dir_flags);
  
        varint_len = encode_varint(untracked->ident.len, varbuf);
@@@ -2827,24 -2804,25 +2827,24 @@@ static void read_stat(size_t pos, void 
        ud->valid = 1;
  }
  
 -static void read_sha1(size_t pos, void *cb)
 +static void read_oid(size_t pos, void *cb)
  {
        struct read_data *rd = cb;
        struct untracked_cache_dir *ud = rd->ucd[pos];
 -      if (rd->data + 20 > rd->end) {
 +      if (rd->data + the_hash_algo->rawsz > rd->end) {
                rd->data = rd->end + 1;
                return;
        }
 -      hashcpy(ud->exclude_sha1, rd->data);
 -      rd->data += 20;
 +      hashcpy(ud->exclude_oid.hash, rd->data);
 +      rd->data += the_hash_algo->rawsz;
  }
  
 -static void load_sha1_stat(struct sha1_stat *sha1_stat,
 -                         const unsigned char *data,
 -                         const unsigned char *sha1)
 +static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
 +                        const unsigned char *sha1)
  {
 -      stat_data_from_disk(&sha1_stat->stat, data);
 -      hashcpy(sha1_stat->sha1, sha1);
 -      sha1_stat->valid = 1;
 +      stat_data_from_disk(&oid_stat->stat, data);
 +      hashcpy(oid_stat->oid.hash, sha1);
 +      oid_stat->valid = 1;
  }
  
  struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
        struct read_data rd;
        const unsigned char *next = data, *end = (const unsigned char *)data + sz;
        const char *ident;
-       int ident_len, len;
+       int ident_len;
+       ssize_t len;
        const char *exclude_per_dir;
  
        if (sz <= 1 || end[-1] != '\0')
        uc = xcalloc(1, sizeof(*uc));
        strbuf_init(&uc->ident, ident_len);
        strbuf_add(&uc->ident, ident, ident_len);
 -      load_sha1_stat(&uc->ss_info_exclude,
 -                     next + ouc_offset(info_exclude_stat),
 -                     next + ouc_offset(info_exclude_sha1));
 -      load_sha1_stat(&uc->ss_excludes_file,
 -                     next + ouc_offset(excludes_file_stat),
 -                     next + ouc_offset(excludes_file_sha1));
 +      load_oid_stat(&uc->ss_info_exclude,
 +                    next + ouc_offset(info_exclude_stat),
 +                    next + ouc_offset(info_exclude_sha1));
 +      load_oid_stat(&uc->ss_excludes_file,
 +                    next + ouc_offset(excludes_file_stat),
 +                    next + ouc_offset(excludes_file_sha1));
        uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
        exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
        uc->exclude_per_dir = xstrdup(exclude_per_dir);
        ewah_each_bit(rd.check_only, set_check_only, &rd);
        rd.data = next + len;
        ewah_each_bit(rd.valid, read_stat, &rd);
 -      ewah_each_bit(rd.sha1_valid, read_sha1, &rd);
 +      ewah_each_bit(rd.sha1_valid, read_oid, &rd);
        next = rd.data;
  
  done:
@@@ -2990,12 -2969,10 +2991,12 @@@ static int invalidate_one_component(str
  }
  
  void untracked_cache_invalidate_path(struct index_state *istate,
 -                                   const char *path)
 +                                   const char *path, int safe_path)
  {
        if (!istate->untracked || !istate->untracked->root)
                return;
 +      if (!safe_path && !verify_path(path, 0))
 +              return;
        invalidate_one_component(istate->untracked, istate->untracked->root,
                                 path, strlen(path));
  }
  void untracked_cache_remove_from_index(struct index_state *istate,
                                       const char *path)
  {
 -      untracked_cache_invalidate_path(istate, path);
 +      untracked_cache_invalidate_path(istate, path, 1);
  }
  
  void untracked_cache_add_to_index(struct index_state *istate,
                                  const char *path)
  {
 -      untracked_cache_invalidate_path(istate, path);
 +      untracked_cache_invalidate_path(istate, path, 1);
  }
  
 -/* 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_)
 +static void connect_wt_gitdir_in_nested(const char *sub_worktree,
 +                                      const char *sub_gitdir)
 +{
 +      int i;
 +      struct repository subrepo;
 +      struct strbuf sub_wt = STRBUF_INIT;
 +      struct strbuf sub_gd = STRBUF_INIT;
 +
 +      const struct submodule *sub;
 +
 +      /* If the submodule has no working tree, we can ignore it. */
 +      if (repo_init(&subrepo, sub_gitdir, sub_worktree))
 +              return;
 +
 +      if (repo_read_index(&subrepo) < 0)
 +              die("index file corrupt in repo %s", subrepo.gitdir);
 +
 +      for (i = 0; i < subrepo.index->cache_nr; i++) {
 +              const struct cache_entry *ce = subrepo.index->cache[i];
 +
 +              if (!S_ISGITLINK(ce->ce_mode))
 +                      continue;
 +
 +              while (i + 1 < subrepo.index->cache_nr &&
 +                     !strcmp(ce->name, subrepo.index->cache[i + 1]->name))
 +                      /*
 +                       * Skip entries with the same name in different stages
 +                       * to make sure an entry is returned only once.
 +                       */
 +                      i++;
 +
 +              sub = submodule_from_path(&subrepo, &null_oid, ce->name);
 +              if (!sub || !is_submodule_active(&subrepo, ce->name))
 +                      /* .gitmodules broken or inactive sub */
 +                      continue;
 +
 +              strbuf_reset(&sub_wt);
 +              strbuf_reset(&sub_gd);
 +              strbuf_addf(&sub_wt, "%s/%s", sub_worktree, sub->path);
 +              strbuf_addf(&sub_gd, "%s/modules/%s", sub_gitdir, sub->name);
 +
 +              connect_work_tree_and_git_dir(sub_wt.buf, sub_gd.buf, 1);
 +      }
 +      strbuf_release(&sub_wt);
 +      strbuf_release(&sub_gd);
 +      repo_clear(&subrepo);
 +}
 +
 +void connect_work_tree_and_git_dir(const char *work_tree_,
 +                                 const char *git_dir_,
 +                                 int recurse_into_nested)
  {
        struct strbuf gitfile_sb = STRBUF_INIT;
        struct strbuf cfg_sb = STRBUF_INIT;
        strbuf_release(&gitfile_sb);
        strbuf_release(&cfg_sb);
        strbuf_release(&rel_path);
 +
 +      if (recurse_into_nested)
 +              connect_wt_gitdir_in_nested(work_tree, git_dir);
 +
        free(work_tree);
        free(git_dir);
  }
@@@ -3109,5 -3033,5 +3110,5 @@@ void relocate_gitdir(const char *path, 
                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);
 +      connect_work_tree_and_git_dir(path, new_git_dir, 0);
  }
diff --combined pack-bitmap.c
index 06771113fb311e4a75fa7a1e325221e4b60c850c,7e92d8319561a9a2ae61be9e9264c6d7ae2d0cdf..18f8b22aeb422bb81999e1e564006daf8b64f637
@@@ -10,8 -10,6 +10,8 @@@
  #include "pack-revindex.h"
  #include "pack-objects.h"
  #include "packfile.h"
 +#include "repository.h"
 +#include "object-store.h"
  
  /*
   * An entry on the bitmap index, representing the bitmap for a given
@@@ -120,7 -118,7 +120,7 @@@ static struct ewah_bitmap *read_bitmap_
  {
        struct ewah_bitmap *b = ewah_pool_new();
  
-       int bitmap_size = ewah_read_mmap(b,
+       ssize_t bitmap_size = ewah_read_mmap(b,
                index->map + index->map_pos,
                index->map_size - index->map_pos);
  
@@@ -255,7 -253,7 +255,7 @@@ static char *pack_bitmap_filename(struc
        size_t len;
  
        if (!strip_suffix(p->pack_name, ".pack", &len))
 -              die("BUG: pack_name does not end in .pack");
 +              BUG("pack_name does not end in .pack");
        return xstrfmt("%.*s.bitmap", (int)len, p->pack_name);
  }
  
@@@ -336,7 -334,8 +336,7 @@@ static int open_pack_bitmap(void
  
        assert(!bitmap_git.map && !bitmap_git.loaded);
  
 -      prepare_packed_git();
 -      for (p = packed_git; p; p = p->next) {
 +      for (p = get_packed_git(the_repository); p; p = p->next) {
                if (open_pack_bitmap_1(p) == 0)
                        ret = 0;
        }
@@@ -723,13 -722,13 +723,13 @@@ int prepare_bitmap_walk(struct rev_inf
                revs->ignore_missing_links = 0;
  
                if (haves_bitmap == NULL)
 -                      die("BUG: failed to perform bitmap walk");
 +                      BUG("failed to perform bitmap walk");
        }
  
        wants_bitmap = find_objects(revs, wants, haves_bitmap);
  
        if (!wants_bitmap)
 -              die("BUG: failed to perform bitmap walk");
 +              BUG("failed to perform bitmap walk");
  
        if (haves_bitmap)
                bitmap_and_not(wants_bitmap, haves_bitmap);
@@@ -1033,7 -1032,7 +1033,7 @@@ int rebuild_existing_bitmaps(struct pac
                oe = packlist_find(mapping, sha1, NULL);
  
                if (oe)
 -                      reposition[i] = oe->in_pack_pos + 1;
 +                      reposition[i] = oe_in_pack_pos(mapping, oe) + 1;
        }
  
        rebuild = bitmap_new();
diff --combined t/t5310-pack-bitmaps.sh
index 423c0a475f7e87b4c32abf60ef85ccbb898eca11,b11bc392a840e91378b67c0e272c7c05711c9993..2d22a17c4a7f9dcc402e89e73ce01c97ebd4ba48
@@@ -264,9 -264,9 +264,9 @@@ test_expect_success 'pack with missing 
  '
  
  test_expect_success JGIT 'we can read jgit bitmaps' '
 -      git clone . compat-jgit &&
 +      git clone --bare . compat-jgit.git &&
        (
 -              cd compat-jgit &&
 +              cd compat-jgit.git &&
                rm -f .git/objects/pack/*.bitmap &&
                jgit gc &&
                git rev-list --test-bitmap HEAD
  '
  
  test_expect_success JGIT 'jgit can read our bitmaps' '
 -      git clone . compat-us &&
 +      git clone --bare . compat-us.git &&
        (
 -              cd compat-us &&
 +              cd compat-us.git &&
                git repack -adb &&
                # jgit gc will barf if it does not like our bitmaps
                jgit gc
  '
  
  test_expect_success 'splitting packs does not generate bogus bitmaps' '
 -      test-genrandom foo $((1024 * 1024)) >rand &&
 +      test-tool genrandom foo $((1024 * 1024)) >rand &&
        git add rand &&
        git commit -m "commit with big file" &&
        git -c pack.packSizeLimit=500k repack -adb &&
@@@ -331,4 -331,17 +331,17 @@@ test_expect_success 'pack reuse respect
        git show-index <empty.idx >actual &&
        test_cmp expect actual
  '
+ test_expect_success 'truncated bitmap fails gracefully' '
+       git repack -ad &&
+       git rev-list --use-bitmap-index --count --all >expect &&
+       bitmap=$(ls .git/objects/pack/*.bitmap) &&
+       test_when_finished "rm -f $bitmap" &&
+       head -c 512 <$bitmap >$bitmap.tmp &&
+       mv -f $bitmap.tmp $bitmap &&
+       git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
+       test_cmp expect actual &&
+       test_i18ngrep corrupt stderr
+ '
  test_done