packfile.con commit pack: move open_pack_index(), parse_pack_index() (0317f45)
   1#include "cache.h"
   2#include "mru.h"
   3#include "pack.h"
   4
   5char *odb_pack_name(struct strbuf *buf,
   6                    const unsigned char *sha1,
   7                    const char *ext)
   8{
   9        strbuf_reset(buf);
  10        strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
  11                    sha1_to_hex(sha1), ext);
  12        return buf->buf;
  13}
  14
  15char *sha1_pack_name(const unsigned char *sha1)
  16{
  17        static struct strbuf buf = STRBUF_INIT;
  18        return odb_pack_name(&buf, sha1, "pack");
  19}
  20
  21char *sha1_pack_index_name(const unsigned char *sha1)
  22{
  23        static struct strbuf buf = STRBUF_INIT;
  24        return odb_pack_name(&buf, sha1, "idx");
  25}
  26
  27unsigned int pack_used_ctr;
  28unsigned int pack_mmap_calls;
  29unsigned int peak_pack_open_windows;
  30unsigned int pack_open_windows;
  31unsigned int pack_open_fds;
  32unsigned int pack_max_fds;
  33size_t peak_pack_mapped;
  34size_t pack_mapped;
  35struct packed_git *packed_git;
  36
  37static struct mru packed_git_mru_storage;
  38struct mru *packed_git_mru = &packed_git_mru_storage;
  39
  40#define SZ_FMT PRIuMAX
  41static inline uintmax_t sz_fmt(size_t s) { return s; }
  42
  43void pack_report(void)
  44{
  45        fprintf(stderr,
  46                "pack_report: getpagesize()            = %10" SZ_FMT "\n"
  47                "pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n"
  48                "pack_report: core.packedGitLimit      = %10" SZ_FMT "\n",
  49                sz_fmt(getpagesize()),
  50                sz_fmt(packed_git_window_size),
  51                sz_fmt(packed_git_limit));
  52        fprintf(stderr,
  53                "pack_report: pack_used_ctr            = %10u\n"
  54                "pack_report: pack_mmap_calls          = %10u\n"
  55                "pack_report: pack_open_windows        = %10u / %10u\n"
  56                "pack_report: pack_mapped              = "
  57                        "%10" SZ_FMT " / %10" SZ_FMT "\n",
  58                pack_used_ctr,
  59                pack_mmap_calls,
  60                pack_open_windows, peak_pack_open_windows,
  61                sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped));
  62}
  63
  64/*
  65 * Open and mmap the index file at path, perform a couple of
  66 * consistency checks, then record its information to p.  Return 0 on
  67 * success.
  68 */
  69static int check_packed_git_idx(const char *path, struct packed_git *p)
  70{
  71        void *idx_map;
  72        struct pack_idx_header *hdr;
  73        size_t idx_size;
  74        uint32_t version, nr, i, *index;
  75        int fd = git_open(path);
  76        struct stat st;
  77
  78        if (fd < 0)
  79                return -1;
  80        if (fstat(fd, &st)) {
  81                close(fd);
  82                return -1;
  83        }
  84        idx_size = xsize_t(st.st_size);
  85        if (idx_size < 4 * 256 + 20 + 20) {
  86                close(fd);
  87                return error("index file %s is too small", path);
  88        }
  89        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
  90        close(fd);
  91
  92        hdr = idx_map;
  93        if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
  94                version = ntohl(hdr->idx_version);
  95                if (version < 2 || version > 2) {
  96                        munmap(idx_map, idx_size);
  97                        return error("index file %s is version %"PRIu32
  98                                     " and is not supported by this binary"
  99                                     " (try upgrading GIT to a newer version)",
 100                                     path, version);
 101                }
 102        } else
 103                version = 1;
 104
 105        nr = 0;
 106        index = idx_map;
 107        if (version > 1)
 108                index += 2;  /* skip index header */
 109        for (i = 0; i < 256; i++) {
 110                uint32_t n = ntohl(index[i]);
 111                if (n < nr) {
 112                        munmap(idx_map, idx_size);
 113                        return error("non-monotonic index %s", path);
 114                }
 115                nr = n;
 116        }
 117
 118        if (version == 1) {
 119                /*
 120                 * Total size:
 121                 *  - 256 index entries 4 bytes each
 122                 *  - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
 123                 *  - 20-byte SHA1 of the packfile
 124                 *  - 20-byte SHA1 file checksum
 125                 */
 126                if (idx_size != 4*256 + nr * 24 + 20 + 20) {
 127                        munmap(idx_map, idx_size);
 128                        return error("wrong index v1 file size in %s", path);
 129                }
 130        } else if (version == 2) {
 131                /*
 132                 * Minimum size:
 133                 *  - 8 bytes of header
 134                 *  - 256 index entries 4 bytes each
 135                 *  - 20-byte sha1 entry * nr
 136                 *  - 4-byte crc entry * nr
 137                 *  - 4-byte offset entry * nr
 138                 *  - 20-byte SHA1 of the packfile
 139                 *  - 20-byte SHA1 file checksum
 140                 * And after the 4-byte offset table might be a
 141                 * variable sized table containing 8-byte entries
 142                 * for offsets larger than 2^31.
 143                 */
 144                unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
 145                unsigned long max_size = min_size;
 146                if (nr)
 147                        max_size += (nr - 1)*8;
 148                if (idx_size < min_size || idx_size > max_size) {
 149                        munmap(idx_map, idx_size);
 150                        return error("wrong index v2 file size in %s", path);
 151                }
 152                if (idx_size != min_size &&
 153                    /*
 154                     * make sure we can deal with large pack offsets.
 155                     * 31-bit signed offset won't be enough, neither
 156                     * 32-bit unsigned one will be.
 157                     */
 158                    (sizeof(off_t) <= 4)) {
 159                        munmap(idx_map, idx_size);
 160                        return error("pack too large for current definition of off_t in %s", path);
 161                }
 162        }
 163
 164        p->index_version = version;
 165        p->index_data = idx_map;
 166        p->index_size = idx_size;
 167        p->num_objects = nr;
 168        return 0;
 169}
 170
 171int open_pack_index(struct packed_git *p)
 172{
 173        char *idx_name;
 174        size_t len;
 175        int ret;
 176
 177        if (p->index_data)
 178                return 0;
 179
 180        if (!strip_suffix(p->pack_name, ".pack", &len))
 181                die("BUG: pack_name does not end in .pack");
 182        idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name);
 183        ret = check_packed_git_idx(idx_name, p);
 184        free(idx_name);
 185        return ret;
 186}
 187
 188static struct packed_git *alloc_packed_git(int extra)
 189{
 190        struct packed_git *p = xmalloc(st_add(sizeof(*p), extra));
 191        memset(p, 0, sizeof(*p));
 192        p->pack_fd = -1;
 193        return p;
 194}
 195
 196struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
 197{
 198        const char *path = sha1_pack_name(sha1);
 199        size_t alloc = st_add(strlen(path), 1);
 200        struct packed_git *p = alloc_packed_git(alloc);
 201
 202        memcpy(p->pack_name, path, alloc); /* includes NUL */
 203        hashcpy(p->sha1, sha1);
 204        if (check_packed_git_idx(idx_path, p)) {
 205                free(p);
 206                return NULL;
 207        }
 208
 209        return p;
 210}