| 1 | #define DISABLE_SIGN_COMPARE_WARNINGS |
| 2 | |
| 3 | #include "git-compat-util.h" |
| 4 | #include "config.h" |
| 5 | #include "dir.h" |
| 6 | #include "hex.h" |
| 7 | #include "packfile.h" |
| 8 | #include "hash-lookup.h" |
| 9 | #include "midx.h" |
| 10 | #include "progress.h" |
| 11 | #include "trace2.h" |
| 12 | #include "chunk-format.h" |
| 13 | #include "pack-bitmap.h" |
| 14 | #include "pack-revindex.h" |
| 15 | #include "strvec.h" |
| 16 | |
| 17 | #define MIDX_PACK_ERROR ((void *)(intptr_t)-1) |
| 18 | |
| 19 | int midx_checksum_valid(struct multi_pack_index *m); |
| 20 | void clear_midx_files_ext(struct odb_source *source, const char *ext, |
| 21 | const char *keep_hash); |
| 22 | void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext, |
| 23 | const struct strvec *keep_hashes); |
| 24 | int cmp_idx_or_pack_name(const char *idx_or_pack_name, |
| 25 | const char *idx_name); |
| 26 | |
| 27 | const char *midx_get_checksum_hex(const struct multi_pack_index *m) |
| 28 | { |
| 29 | return hash_to_hex_algop(midx_get_checksum_hash(m), |
| 30 | m->source->odb->repo->hash_algo); |
| 31 | } |
| 32 | |
| 33 | const unsigned char *midx_get_checksum_hash(const struct multi_pack_index *m) |
| 34 | { |
| 35 | return m->data + m->data_len - m->source->odb->repo->hash_algo->rawsz; |
| 36 | } |
| 37 | |
| 38 | void get_midx_filename(struct odb_source *source, struct strbuf *out) |
| 39 | { |
| 40 | get_midx_filename_ext(source, out, NULL, NULL); |
| 41 | } |
| 42 | |
| 43 | void get_midx_filename_ext(struct odb_source *source, struct strbuf *out, |
| 44 | const unsigned char *hash, const char *ext) |
| 45 | { |
| 46 | strbuf_addf(out, "%s/pack/multi-pack-index", source->path); |
| 47 | if (ext) |
| 48 | strbuf_addf(out, "-%s.%s", hash_to_hex_algop(hash, source->odb->repo->hash_algo), ext); |
| 49 | } |
| 50 | |
| 51 | static int midx_read_oid_fanout(const unsigned char *chunk_start, |
| 52 | size_t chunk_size, void *data) |
| 53 | { |
| 54 | int i; |
| 55 | struct multi_pack_index *m = data; |
| 56 | m->chunk_oid_fanout = (uint32_t *)chunk_start; |
| 57 | |
| 58 | if (chunk_size != 4 * 256) { |
| 59 | error(_("multi-pack-index OID fanout is of the wrong size")); |
| 60 | return 1; |
| 61 | } |
| 62 | for (i = 0; i < 255; i++) { |
| 63 | uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]); |
| 64 | uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i+1]); |
| 65 | |
| 66 | if (oid_fanout1 > oid_fanout2) { |
| 67 | error(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"), |
| 68 | i, oid_fanout1, oid_fanout2, i + 1); |
| 69 | return 1; |
| 70 | } |
| 71 | } |
| 72 | m->num_objects = ntohl(m->chunk_oid_fanout[255]); |
| 73 | return 0; |
| 74 | } |
| 75 | |
| 76 | static int midx_read_oid_lookup(const unsigned char *chunk_start, |
| 77 | size_t chunk_size, void *data) |
| 78 | { |
| 79 | struct multi_pack_index *m = data; |
| 80 | m->chunk_oid_lookup = chunk_start; |
| 81 | |
| 82 | if (chunk_size != st_mult(m->hash_len, m->num_objects)) { |
| 83 | error(_("multi-pack-index OID lookup chunk is the wrong size")); |
| 84 | return 1; |
| 85 | } |
| 86 | return 0; |
| 87 | } |
| 88 | |
| 89 | static int midx_read_object_offsets(const unsigned char *chunk_start, |
| 90 | size_t chunk_size, void *data) |
| 91 | { |
| 92 | struct multi_pack_index *m = data; |
| 93 | m->chunk_object_offsets = chunk_start; |
| 94 | |
| 95 | if (chunk_size != st_mult(m->num_objects, MIDX_CHUNK_OFFSET_WIDTH)) { |
| 96 | error(_("multi-pack-index object offset chunk is the wrong size")); |
| 97 | return 1; |
| 98 | } |
| 99 | return 0; |
| 100 | } |
| 101 | |
| 102 | struct multi_pack_index *get_multi_pack_index(struct odb_source *source) |
| 103 | { |
| 104 | struct odb_source_files *files = odb_source_files_downcast(source); |
| 105 | packfile_store_prepare(files->packed); |
| 106 | return files->packed->midx; |
| 107 | } |
| 108 | |
| 109 | static struct multi_pack_index *load_multi_pack_index_one(struct odb_source *source, |
| 110 | const char *midx_name) |
| 111 | { |
| 112 | struct repository *r = source->odb->repo; |
| 113 | struct multi_pack_index *m = NULL; |
| 114 | int fd; |
| 115 | struct stat st; |
| 116 | size_t midx_size; |
| 117 | void *midx_map = NULL; |
| 118 | uint32_t hash_version; |
| 119 | uint32_t i; |
| 120 | const char *cur_pack_name; |
| 121 | struct chunkfile *cf = NULL; |
| 122 | |
| 123 | fd = git_open(midx_name); |
| 124 | |
| 125 | if (fd < 0) |
| 126 | goto cleanup_fail; |
| 127 | if (fstat(fd, &st)) { |
| 128 | error_errno(_("failed to read %s"), midx_name); |
| 129 | goto cleanup_fail; |
| 130 | } |
| 131 | |
| 132 | midx_size = xsize_t(st.st_size); |
| 133 | |
| 134 | if (midx_size < (MIDX_HEADER_SIZE + r->hash_algo->rawsz)) { |
| 135 | error(_("multi-pack-index file %s is too small"), midx_name); |
| 136 | goto cleanup_fail; |
| 137 | } |
| 138 | |
| 139 | midx_map = xmmap(NULL, midx_size, PROT_READ, MAP_PRIVATE, fd, 0); |
| 140 | close(fd); |
| 141 | |
| 142 | CALLOC_ARRAY(m, 1); |
| 143 | m->data = midx_map; |
| 144 | m->data_len = midx_size; |
| 145 | m->source = source; |
| 146 | |
| 147 | m->signature = get_be32(m->data); |
| 148 | if (m->signature != MIDX_SIGNATURE) |
| 149 | die(_("multi-pack-index signature 0x%08x does not match signature 0x%08x"), |
| 150 | m->signature, MIDX_SIGNATURE); |
| 151 | |
| 152 | m->version = m->data[MIDX_BYTE_FILE_VERSION]; |
| 153 | if (m->version != MIDX_VERSION_V1 && m->version != MIDX_VERSION_V2) |
| 154 | die(_("multi-pack-index version %d not recognized"), |
| 155 | m->version); |
| 156 | |
| 157 | hash_version = m->data[MIDX_BYTE_HASH_VERSION]; |
| 158 | if (hash_version != oid_version(r->hash_algo)) { |
| 159 | error(_("multi-pack-index hash version %u does not match version %u"), |
| 160 | hash_version, oid_version(r->hash_algo)); |
| 161 | goto cleanup_fail; |
| 162 | } |
| 163 | m->hash_len = r->hash_algo->rawsz; |
| 164 | |
| 165 | m->num_chunks = m->data[MIDX_BYTE_NUM_CHUNKS]; |
| 166 | |
| 167 | m->num_packs = get_be32(m->data + MIDX_BYTE_NUM_PACKS); |
| 168 | |
| 169 | m->preferred_pack_idx = -1; |
| 170 | |
| 171 | cf = init_chunkfile(NULL); |
| 172 | |
| 173 | if (read_table_of_contents(cf, m->data, midx_size, |
| 174 | MIDX_HEADER_SIZE, m->num_chunks, |
| 175 | MIDX_CHUNK_ALIGNMENT)) |
| 176 | goto cleanup_fail; |
| 177 | |
| 178 | if (pair_chunk(cf, MIDX_CHUNKID_PACKNAMES, &m->chunk_pack_names, &m->chunk_pack_names_len)) |
| 179 | die(_("multi-pack-index required pack-name chunk missing or corrupted")); |
| 180 | if (read_chunk(cf, MIDX_CHUNKID_OIDFANOUT, midx_read_oid_fanout, m)) |
| 181 | die(_("multi-pack-index required OID fanout chunk missing or corrupted")); |
| 182 | if (read_chunk(cf, MIDX_CHUNKID_OIDLOOKUP, midx_read_oid_lookup, m)) |
| 183 | die(_("multi-pack-index required OID lookup chunk missing or corrupted")); |
| 184 | if (read_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS, midx_read_object_offsets, m)) |
| 185 | die(_("multi-pack-index required object offsets chunk missing or corrupted")); |
| 186 | |
| 187 | pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets, |
| 188 | &m->chunk_large_offsets_len); |
| 189 | if (git_env_bool("GIT_TEST_MIDX_READ_BTMP", 1)) |
| 190 | pair_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS, |
| 191 | (const unsigned char **)&m->chunk_bitmapped_packs, |
| 192 | &m->chunk_bitmapped_packs_len); |
| 193 | |
| 194 | if (git_env_bool("GIT_TEST_MIDX_READ_RIDX", 1)) |
| 195 | pair_chunk(cf, MIDX_CHUNKID_REVINDEX, &m->chunk_revindex, |
| 196 | &m->chunk_revindex_len); |
| 197 | |
| 198 | CALLOC_ARRAY(m->pack_names, m->num_packs); |
| 199 | CALLOC_ARRAY(m->packs, m->num_packs); |
| 200 | |
| 201 | cur_pack_name = (const char *)m->chunk_pack_names; |
| 202 | for (i = 0; i < m->num_packs; i++) { |
| 203 | const char *end; |
| 204 | size_t avail = m->chunk_pack_names_len - |
| 205 | (cur_pack_name - (const char *)m->chunk_pack_names); |
| 206 | |
| 207 | m->pack_names[i] = cur_pack_name; |
| 208 | |
| 209 | end = memchr(cur_pack_name, '\0', avail); |
| 210 | if (!end) |
| 211 | die(_("multi-pack-index pack-name chunk is too short")); |
| 212 | cur_pack_name = end + 1; |
| 213 | |
| 214 | if (m->version == MIDX_VERSION_V1 && |
| 215 | i && strcmp(m->pack_names[i], m->pack_names[i - 1]) <= 0) |
| 216 | die(_("multi-pack-index pack names out of order: '%s' before '%s'"), |
| 217 | m->pack_names[i - 1], |
| 218 | m->pack_names[i]); |
| 219 | } |
| 220 | |
| 221 | trace2_data_intmax("midx", r, "load/num_packs", m->num_packs); |
| 222 | trace2_data_intmax("midx", r, "load/num_objects", m->num_objects); |
| 223 | |
| 224 | free_chunkfile(cf); |
| 225 | return m; |
| 226 | |
| 227 | cleanup_fail: |
| 228 | free(m); |
| 229 | free_chunkfile(cf); |
| 230 | if (midx_map) |
| 231 | munmap(midx_map, midx_size); |
| 232 | if (0 <= fd) |
| 233 | close(fd); |
| 234 | return NULL; |
| 235 | } |
| 236 | |
| 237 | void get_midx_chain_dirname(struct odb_source *source, struct strbuf *buf) |
| 238 | { |
| 239 | strbuf_addf(buf, "%s/pack/multi-pack-index.d", source->path); |
| 240 | } |
| 241 | |
| 242 | void get_midx_chain_filename(struct odb_source *source, struct strbuf *buf) |
| 243 | { |
| 244 | get_midx_chain_dirname(source, buf); |
| 245 | strbuf_addstr(buf, "/multi-pack-index-chain"); |
| 246 | } |
| 247 | |
| 248 | void get_split_midx_filename_ext(struct odb_source *source, struct strbuf *buf, |
| 249 | const unsigned char *hash, const char *ext) |
| 250 | { |
| 251 | get_midx_chain_dirname(source, buf); |
| 252 | strbuf_addf(buf, "/multi-pack-index-%s.%s", |
| 253 | hash_to_hex_algop(hash, source->odb->repo->hash_algo), ext); |
| 254 | } |
| 255 | |
| 256 | static int open_multi_pack_index_chain(const struct git_hash_algo *hash_algo, |
| 257 | const char *chain_file, int *fd, |
| 258 | struct stat *st) |
| 259 | { |
| 260 | *fd = git_open(chain_file); |
| 261 | if (*fd < 0) |
| 262 | return 0; |
| 263 | if (fstat(*fd, st)) { |
| 264 | close(*fd); |
| 265 | return 0; |
| 266 | } |
| 267 | if (st->st_size < hash_algo->hexsz) { |
| 268 | close(*fd); |
| 269 | if (!st->st_size) { |
| 270 | /* treat empty files the same as missing */ |
| 271 | errno = ENOENT; |
| 272 | } else { |
| 273 | warning(_("multi-pack-index chain file too small")); |
| 274 | errno = EINVAL; |
| 275 | } |
| 276 | return 0; |
| 277 | } |
| 278 | return 1; |
| 279 | } |
| 280 | |
| 281 | static int add_midx_to_chain(struct multi_pack_index *midx, |
| 282 | struct multi_pack_index *midx_chain) |
| 283 | { |
| 284 | if (midx_chain) { |
| 285 | if (unsigned_add_overflows(midx_chain->num_packs, |
| 286 | midx_chain->num_packs_in_base)) { |
| 287 | warning(_("pack count in base MIDX too high: %"PRIuMAX), |
| 288 | (uintmax_t)midx_chain->num_packs_in_base); |
| 289 | return 0; |
| 290 | } |
| 291 | if (unsigned_add_overflows(midx_chain->num_objects, |
| 292 | midx_chain->num_objects_in_base)) { |
| 293 | warning(_("object count in base MIDX too high: %"PRIuMAX), |
| 294 | (uintmax_t)midx_chain->num_objects_in_base); |
| 295 | return 0; |
| 296 | } |
| 297 | midx->num_packs_in_base = midx_chain->num_packs + |
| 298 | midx_chain->num_packs_in_base; |
| 299 | midx->num_objects_in_base = midx_chain->num_objects + |
| 300 | midx_chain->num_objects_in_base; |
| 301 | } |
| 302 | |
| 303 | midx->base_midx = midx_chain; |
| 304 | midx->has_chain = 1; |
| 305 | |
| 306 | return 1; |
| 307 | } |
| 308 | |
| 309 | static struct multi_pack_index *load_midx_chain_fd_st(struct odb_source *source, |
| 310 | int fd, struct stat *st, |
| 311 | int *incomplete_chain) |
| 312 | { |
| 313 | const struct git_hash_algo *hash_algo = source->odb->repo->hash_algo; |
| 314 | struct multi_pack_index *midx_chain = NULL; |
| 315 | struct strbuf buf = STRBUF_INIT; |
| 316 | int valid = 1; |
| 317 | uint32_t i, count; |
| 318 | FILE *fp = xfdopen(fd, "r"); |
| 319 | |
| 320 | count = st->st_size / (hash_algo->hexsz + 1); |
| 321 | |
| 322 | for (i = 0; i < count; i++) { |
| 323 | struct multi_pack_index *m; |
| 324 | struct object_id layer; |
| 325 | |
| 326 | if (strbuf_getline_lf(&buf, fp) == EOF) |
| 327 | break; |
| 328 | |
| 329 | if (get_oid_hex_algop(buf.buf, &layer, hash_algo)) { |
| 330 | warning(_("invalid multi-pack-index chain: line '%s' " |
| 331 | "not a hash"), |
| 332 | buf.buf); |
| 333 | valid = 0; |
| 334 | break; |
| 335 | } |
| 336 | |
| 337 | valid = 0; |
| 338 | |
| 339 | strbuf_reset(&buf); |
| 340 | get_split_midx_filename_ext(source, &buf, |
| 341 | layer.hash, MIDX_EXT_MIDX); |
| 342 | m = load_multi_pack_index_one(source, buf.buf); |
| 343 | |
| 344 | if (m) { |
| 345 | if (add_midx_to_chain(m, midx_chain)) { |
| 346 | midx_chain = m; |
| 347 | valid = 1; |
| 348 | } else { |
| 349 | close_midx(m); |
| 350 | } |
| 351 | } |
| 352 | if (!valid) { |
| 353 | warning(_("unable to find all multi-pack index files")); |
| 354 | break; |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | fclose(fp); |
| 359 | strbuf_release(&buf); |
| 360 | |
| 361 | *incomplete_chain = !valid; |
| 362 | return midx_chain; |
| 363 | } |
| 364 | |
| 365 | static struct multi_pack_index *load_multi_pack_index_chain(struct odb_source *source) |
| 366 | { |
| 367 | struct strbuf chain_file = STRBUF_INIT; |
| 368 | struct stat st; |
| 369 | int fd; |
| 370 | struct multi_pack_index *m = NULL; |
| 371 | |
| 372 | get_midx_chain_filename(source, &chain_file); |
| 373 | if (open_multi_pack_index_chain(source->odb->repo->hash_algo, chain_file.buf, &fd, &st)) { |
| 374 | int incomplete; |
| 375 | /* ownership of fd is taken over by load function */ |
| 376 | m = load_midx_chain_fd_st(source, fd, &st, &incomplete); |
| 377 | } |
| 378 | |
| 379 | strbuf_release(&chain_file); |
| 380 | return m; |
| 381 | } |
| 382 | |
| 383 | struct multi_pack_index *load_multi_pack_index(struct odb_source *source) |
| 384 | { |
| 385 | struct strbuf midx_name = STRBUF_INIT; |
| 386 | struct multi_pack_index *m; |
| 387 | |
| 388 | get_midx_filename(source, &midx_name); |
| 389 | |
| 390 | m = load_multi_pack_index_one(source, midx_name.buf); |
| 391 | if (!m) |
| 392 | m = load_multi_pack_index_chain(source); |
| 393 | |
| 394 | strbuf_release(&midx_name); |
| 395 | |
| 396 | return m; |
| 397 | } |
| 398 | |
| 399 | void close_midx(struct multi_pack_index *m) |
| 400 | { |
| 401 | uint32_t i; |
| 402 | |
| 403 | if (!m) |
| 404 | return; |
| 405 | |
| 406 | close_midx(m->base_midx); |
| 407 | |
| 408 | munmap((unsigned char *)m->data, m->data_len); |
| 409 | |
| 410 | for (i = 0; i < m->num_packs; i++) { |
| 411 | if (m->packs[i] && m->packs[i] != MIDX_PACK_ERROR) |
| 412 | m->packs[i]->multi_pack_index = 0; |
| 413 | } |
| 414 | FREE_AND_NULL(m->packs); |
| 415 | FREE_AND_NULL(m->pack_names); |
| 416 | FREE_AND_NULL(m->pack_names_sorted); |
| 417 | free(m); |
| 418 | } |
| 419 | |
| 420 | static uint32_t midx_for_object(struct multi_pack_index **_m, uint32_t pos) |
| 421 | { |
| 422 | struct multi_pack_index *m = *_m; |
| 423 | while (m && pos < m->num_objects_in_base) |
| 424 | m = m->base_midx; |
| 425 | |
| 426 | if (!m) |
| 427 | BUG("NULL multi-pack-index for object position: %"PRIu32, pos); |
| 428 | |
| 429 | if (pos >= m->num_objects + m->num_objects_in_base) |
| 430 | die(_("invalid MIDX object position, MIDX is likely corrupt")); |
| 431 | |
| 432 | *_m = m; |
| 433 | |
| 434 | return pos - m->num_objects_in_base; |
| 435 | } |
| 436 | |
| 437 | static uint32_t midx_for_pack(struct multi_pack_index **_m, |
| 438 | uint32_t pack_int_id) |
| 439 | { |
| 440 | struct multi_pack_index *m = *_m; |
| 441 | while (m && pack_int_id < m->num_packs_in_base) |
| 442 | m = m->base_midx; |
| 443 | |
| 444 | if (!m) |
| 445 | BUG("NULL multi-pack-index for pack ID: %"PRIu32, pack_int_id); |
| 446 | |
| 447 | if (pack_int_id >= m->num_packs + m->num_packs_in_base) |
| 448 | die(_("bad pack-int-id: %u (%u total packs)"), |
| 449 | pack_int_id, m->num_packs + m->num_packs_in_base); |
| 450 | |
| 451 | *_m = m; |
| 452 | |
| 453 | return pack_int_id - m->num_packs_in_base; |
| 454 | } |
| 455 | |
| 456 | int prepare_midx_pack(struct multi_pack_index *m, |
| 457 | uint32_t pack_int_id) |
| 458 | { |
| 459 | struct odb_source_files *files = odb_source_files_downcast(m->source); |
| 460 | struct strbuf pack_name = STRBUF_INIT; |
| 461 | struct packed_git *p; |
| 462 | |
| 463 | pack_int_id = midx_for_pack(&m, pack_int_id); |
| 464 | |
| 465 | if (m->packs[pack_int_id] == MIDX_PACK_ERROR) |
| 466 | return 1; |
| 467 | if (m->packs[pack_int_id]) |
| 468 | return 0; |
| 469 | |
| 470 | strbuf_addf(&pack_name, "%s/pack/%s", files->base.path, |
| 471 | m->pack_names[pack_int_id]); |
| 472 | p = packfile_store_load_pack(files->packed, |
| 473 | pack_name.buf, files->base.local); |
| 474 | strbuf_release(&pack_name); |
| 475 | |
| 476 | if (!p) { |
| 477 | m->packs[pack_int_id] = MIDX_PACK_ERROR; |
| 478 | return 1; |
| 479 | } |
| 480 | |
| 481 | p->multi_pack_index = 1; |
| 482 | m->packs[pack_int_id] = p; |
| 483 | |
| 484 | return 0; |
| 485 | } |
| 486 | |
| 487 | struct packed_git *nth_midxed_pack(struct multi_pack_index *m, |
| 488 | uint32_t pack_int_id) |
| 489 | { |
| 490 | uint32_t local_pack_int_id = midx_for_pack(&m, pack_int_id); |
| 491 | if (m->packs[local_pack_int_id] == MIDX_PACK_ERROR) |
| 492 | return NULL; |
| 493 | return m->packs[local_pack_int_id]; |
| 494 | } |
| 495 | |
| 496 | #define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t)) |
| 497 | |
| 498 | int nth_bitmapped_pack(struct multi_pack_index *m, |
| 499 | struct bitmapped_pack *bp, uint32_t pack_int_id) |
| 500 | { |
| 501 | uint32_t local_pack_int_id = midx_for_pack(&m, pack_int_id); |
| 502 | |
| 503 | if (!m->chunk_bitmapped_packs) |
| 504 | return error(_("MIDX does not contain the BTMP chunk")); |
| 505 | |
| 506 | if (prepare_midx_pack(m, pack_int_id)) |
| 507 | return error(_("could not load bitmapped pack %"PRIu32), pack_int_id); |
| 508 | |
| 509 | bp->p = m->packs[local_pack_int_id]; |
| 510 | bp->bitmap_pos = get_be32((char *)m->chunk_bitmapped_packs + |
| 511 | MIDX_CHUNK_BITMAPPED_PACKS_WIDTH * local_pack_int_id); |
| 512 | bp->bitmap_nr = get_be32((char *)m->chunk_bitmapped_packs + |
| 513 | MIDX_CHUNK_BITMAPPED_PACKS_WIDTH * local_pack_int_id + |
| 514 | sizeof(uint32_t)); |
| 515 | bp->pack_int_id = pack_int_id; |
| 516 | bp->from_midx = m; |
| 517 | |
| 518 | return 0; |
| 519 | } |
| 520 | |
| 521 | int bsearch_one_midx(const struct object_id *oid, struct multi_pack_index *m, |
| 522 | uint32_t *result) |
| 523 | { |
| 524 | int ret = bsearch_hash(oid->hash, m->chunk_oid_fanout, |
| 525 | m->chunk_oid_lookup, |
| 526 | m->source->odb->repo->hash_algo->rawsz, |
| 527 | result); |
| 528 | if (result) |
| 529 | *result += m->num_objects_in_base; |
| 530 | return ret; |
| 531 | } |
| 532 | |
| 533 | int bsearch_midx(const struct object_id *oid, struct multi_pack_index *m, |
| 534 | uint32_t *result) |
| 535 | { |
| 536 | for (; m; m = m->base_midx) |
| 537 | if (bsearch_one_midx(oid, m, result)) |
| 538 | return 1; |
| 539 | return 0; |
| 540 | } |
| 541 | |
| 542 | int midx_has_oid(struct multi_pack_index *m, const struct object_id *oid) |
| 543 | { |
| 544 | return bsearch_midx(oid, m, NULL); |
| 545 | } |
| 546 | |
| 547 | struct object_id *nth_midxed_object_oid(struct object_id *oid, |
| 548 | struct multi_pack_index *m, |
| 549 | uint32_t n) |
| 550 | { |
| 551 | if (n >= m->num_objects + m->num_objects_in_base) |
| 552 | return NULL; |
| 553 | |
| 554 | n = midx_for_object(&m, n); |
| 555 | |
| 556 | oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n), |
| 557 | m->source->odb->repo->hash_algo); |
| 558 | return oid; |
| 559 | } |
| 560 | |
| 561 | off_t nth_midxed_offset(struct multi_pack_index *m, uint32_t pos) |
| 562 | { |
| 563 | const unsigned char *offset_data; |
| 564 | uint32_t offset32; |
| 565 | |
| 566 | pos = midx_for_object(&m, pos); |
| 567 | |
| 568 | offset_data = m->chunk_object_offsets + (off_t)pos * MIDX_CHUNK_OFFSET_WIDTH; |
| 569 | offset32 = get_be32(offset_data + sizeof(uint32_t)); |
| 570 | |
| 571 | if (m->chunk_large_offsets && offset32 & MIDX_LARGE_OFFSET_NEEDED) { |
| 572 | if (sizeof(off_t) < sizeof(uint64_t)) |
| 573 | die(_("multi-pack-index stores a 64-bit offset, but off_t is too small")); |
| 574 | |
| 575 | offset32 ^= MIDX_LARGE_OFFSET_NEEDED; |
| 576 | if (offset32 >= m->chunk_large_offsets_len / sizeof(uint64_t)) |
| 577 | die(_("multi-pack-index large offset out of bounds")); |
| 578 | return get_be64(m->chunk_large_offsets + sizeof(uint64_t) * offset32); |
| 579 | } |
| 580 | |
| 581 | return offset32; |
| 582 | } |
| 583 | |
| 584 | uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos) |
| 585 | { |
| 586 | pos = midx_for_object(&m, pos); |
| 587 | |
| 588 | return m->num_packs_in_base + get_be32(m->chunk_object_offsets + |
| 589 | (off_t)pos * MIDX_CHUNK_OFFSET_WIDTH); |
| 590 | } |
| 591 | |
| 592 | int fill_midx_entry(struct multi_pack_index *m, |
| 593 | const struct object_id *oid, |
| 594 | struct pack_entry *e) |
| 595 | { |
| 596 | uint32_t pos; |
| 597 | uint32_t pack_int_id; |
| 598 | struct packed_git *p; |
| 599 | |
| 600 | if (!bsearch_midx(oid, m, &pos)) |
| 601 | return 0; |
| 602 | |
| 603 | midx_for_object(&m, pos); |
| 604 | pack_int_id = nth_midxed_pack_int_id(m, pos); |
| 605 | |
| 606 | if (prepare_midx_pack(m, pack_int_id)) |
| 607 | return 0; |
| 608 | p = m->packs[pack_int_id - m->num_packs_in_base]; |
| 609 | |
| 610 | /* |
| 611 | * We are about to tell the caller where they can locate the |
| 612 | * requested object. We better make sure the packfile is |
| 613 | * still here and can be accessed before supplying that |
| 614 | * answer, as it may have been deleted since the MIDX was |
| 615 | * loaded! |
| 616 | */ |
| 617 | if (!is_pack_valid(p)) |
| 618 | return 0; |
| 619 | |
| 620 | if (oidset_size(&p->bad_objects) && |
| 621 | oidset_contains(&p->bad_objects, oid)) |
| 622 | return 0; |
| 623 | |
| 624 | e->offset = nth_midxed_offset(m, pos); |
| 625 | e->p = p; |
| 626 | |
| 627 | return 1; |
| 628 | } |
| 629 | |
| 630 | /* Match "foo.idx" against either "foo.pack" _or_ "foo.idx". */ |
| 631 | int cmp_idx_or_pack_name(const char *idx_or_pack_name, |
| 632 | const char *idx_name) |
| 633 | { |
| 634 | /* Skip past any initial matching prefix. */ |
| 635 | while (*idx_name && *idx_name == *idx_or_pack_name) { |
| 636 | idx_name++; |
| 637 | idx_or_pack_name++; |
| 638 | } |
| 639 | |
| 640 | /* |
| 641 | * If we didn't match completely, we may have matched "pack-1234." and |
| 642 | * be left with "idx" and "pack" respectively, which is also OK. We do |
| 643 | * not have to check for "idx" and "idx", because that would have been |
| 644 | * a complete match (and in that case these strcmps will be false, but |
| 645 | * we'll correctly return 0 from the final strcmp() below. |
| 646 | * |
| 647 | * Technically this matches "fooidx" and "foopack", but we'd never have |
| 648 | * such names in the first place. |
| 649 | */ |
| 650 | if (!strcmp(idx_name, "idx") && !strcmp(idx_or_pack_name, "pack")) |
| 651 | return 0; |
| 652 | |
| 653 | /* |
| 654 | * This not only checks for a complete match, but also orders based on |
| 655 | * the first non-identical character, which means our ordering will |
| 656 | * match a raw strcmp(). That makes it OK to use this to binary search |
| 657 | * a naively-sorted list. |
| 658 | */ |
| 659 | return strcmp(idx_or_pack_name, idx_name); |
| 660 | } |
| 661 | |
| 662 | |
| 663 | static int midx_pack_names_cmp(const void *a, const void *b, void *m_) |
| 664 | { |
| 665 | struct multi_pack_index *m = m_; |
| 666 | return strcmp(m->pack_names[*(const size_t *)a], |
| 667 | m->pack_names[*(const size_t *)b]); |
| 668 | } |
| 669 | |
| 670 | int midx_layer_contains_pack(struct multi_pack_index *m, |
| 671 | const char *idx_or_pack_name) |
| 672 | { |
| 673 | uint32_t first = 0, last = m->num_packs; |
| 674 | |
| 675 | if (m->version == MIDX_VERSION_V2 && !m->pack_names_sorted) { |
| 676 | uint32_t i; |
| 677 | |
| 678 | ALLOC_ARRAY(m->pack_names_sorted, m->num_packs); |
| 679 | |
| 680 | for (i = 0; i < m->num_packs; i++) |
| 681 | m->pack_names_sorted[i] = i; |
| 682 | |
| 683 | QSORT_S(m->pack_names_sorted, m->num_packs, midx_pack_names_cmp, |
| 684 | m); |
| 685 | } |
| 686 | |
| 687 | while (first < last) { |
| 688 | uint32_t mid = first + (last - first) / 2; |
| 689 | const char *current; |
| 690 | int cmp; |
| 691 | |
| 692 | if (m->pack_names_sorted) |
| 693 | current = m->pack_names[m->pack_names_sorted[mid]]; |
| 694 | else |
| 695 | current = m->pack_names[mid]; |
| 696 | cmp = cmp_idx_or_pack_name(idx_or_pack_name, current); |
| 697 | if (!cmp) |
| 698 | return 1; |
| 699 | if (cmp > 0) { |
| 700 | first = mid + 1; |
| 701 | continue; |
| 702 | } |
| 703 | last = mid; |
| 704 | } |
| 705 | |
| 706 | return 0; |
| 707 | } |
| 708 | |
| 709 | int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name) |
| 710 | { |
| 711 | for (; m; m = m->base_midx) |
| 712 | if (midx_layer_contains_pack(m, idx_or_pack_name)) |
| 713 | return 1; |
| 714 | return 0; |
| 715 | } |
| 716 | |
| 717 | int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id) |
| 718 | { |
| 719 | if (m->preferred_pack_idx == -1) { |
| 720 | uint32_t midx_pos; |
| 721 | if (load_midx_revindex(m)) { |
| 722 | m->preferred_pack_idx = -2; |
| 723 | return -1; |
| 724 | } |
| 725 | |
| 726 | midx_pos = pack_pos_to_midx(m, m->num_objects_in_base); |
| 727 | |
| 728 | m->preferred_pack_idx = nth_midxed_pack_int_id(m, midx_pos); |
| 729 | |
| 730 | } else if (m->preferred_pack_idx == -2) |
| 731 | return -1; /* no revindex */ |
| 732 | |
| 733 | *pack_int_id = m->preferred_pack_idx; |
| 734 | return 0; |
| 735 | } |
| 736 | |
| 737 | int prepare_multi_pack_index_one(struct odb_source *source) |
| 738 | { |
| 739 | struct odb_source_files *files = odb_source_files_downcast(source); |
| 740 | struct repository *r = source->odb->repo; |
| 741 | |
| 742 | prepare_repo_settings(r); |
| 743 | if (!r->settings.core_multi_pack_index) |
| 744 | return 0; |
| 745 | |
| 746 | if (files->packed->midx) |
| 747 | return 1; |
| 748 | |
| 749 | files->packed->midx = load_multi_pack_index(source); |
| 750 | |
| 751 | return !!files->packed->midx; |
| 752 | } |
| 753 | |
| 754 | int midx_checksum_valid(struct multi_pack_index *m) |
| 755 | { |
| 756 | return hashfile_checksum_valid(m->source->odb->repo->hash_algo, |
| 757 | m->data, m->data_len); |
| 758 | } |
| 759 | |
| 760 | struct clear_midx_data { |
| 761 | struct strset keep; |
| 762 | const char *ext; |
| 763 | }; |
| 764 | |
| 765 | static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUSED, |
| 766 | const char *file_name, void *_data) |
| 767 | { |
| 768 | struct clear_midx_data *data = _data; |
| 769 | |
| 770 | if (!(starts_with(file_name, "multi-pack-index-") && |
| 771 | ends_with(file_name, data->ext))) |
| 772 | return; |
| 773 | if (strset_contains(&data->keep, file_name)) |
| 774 | return; |
| 775 | if (unlink(full_path)) |
| 776 | die_errno(_("failed to remove %s"), full_path); |
| 777 | } |
| 778 | |
| 779 | void clear_midx_files_ext(struct odb_source *source, const char *ext, |
| 780 | const char *keep_hash) |
| 781 | { |
| 782 | struct clear_midx_data data = { |
| 783 | .keep = STRSET_INIT, |
| 784 | .ext = ext, |
| 785 | }; |
| 786 | |
| 787 | if (keep_hash) { |
| 788 | struct strbuf buf = STRBUF_INIT; |
| 789 | strbuf_addf(&buf, "multi-pack-index-%s.%s", keep_hash, ext); |
| 790 | |
| 791 | strset_add(&data.keep, buf.buf); |
| 792 | |
| 793 | strbuf_release(&buf); |
| 794 | } |
| 795 | |
| 796 | for_each_file_in_pack_dir(source->path, clear_midx_file_ext, &data); |
| 797 | |
| 798 | strset_clear(&data.keep); |
| 799 | } |
| 800 | |
| 801 | void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext, |
| 802 | const struct strvec *keep_hashes) |
| 803 | { |
| 804 | struct clear_midx_data data = { |
| 805 | .keep = STRSET_INIT, |
| 806 | .ext = ext, |
| 807 | }; |
| 808 | struct strbuf buf = STRBUF_INIT; |
| 809 | |
| 810 | if (keep_hashes) { |
| 811 | for (size_t i = 0; i < keep_hashes->nr; i++) { |
| 812 | strbuf_reset(&buf); |
| 813 | strbuf_addf(&buf, "multi-pack-index-%s.%s", |
| 814 | keep_hashes->v[i], ext); |
| 815 | |
| 816 | strset_add(&data.keep, buf.buf); |
| 817 | } |
| 818 | } |
| 819 | |
| 820 | for_each_file_in_pack_subdir(source->path, "multi-pack-index.d", |
| 821 | clear_midx_file_ext, &data); |
| 822 | |
| 823 | strbuf_release(&buf); |
| 824 | strset_clear(&data.keep); |
| 825 | } |
| 826 | |
| 827 | void clear_midx_file(struct repository *r) |
| 828 | { |
| 829 | struct strbuf midx = STRBUF_INIT; |
| 830 | |
| 831 | get_midx_filename(r->objects->sources, &midx); |
| 832 | |
| 833 | if (r->objects) { |
| 834 | struct odb_source *source; |
| 835 | |
| 836 | for (source = r->objects->sources; source; source = source->next) { |
| 837 | struct odb_source_files *files = odb_source_files_downcast(source); |
| 838 | if (files->packed->midx) |
| 839 | close_midx(files->packed->midx); |
| 840 | files->packed->midx = NULL; |
| 841 | } |
| 842 | } |
| 843 | |
| 844 | if (remove_path(midx.buf)) |
| 845 | die(_("failed to clear multi-pack-index at %s"), midx.buf); |
| 846 | |
| 847 | clear_midx_files_ext(r->objects->sources, MIDX_EXT_BITMAP, NULL); |
| 848 | clear_midx_files_ext(r->objects->sources, MIDX_EXT_REV, NULL); |
| 849 | |
| 850 | strbuf_release(&midx); |
| 851 | } |
| 852 | |
| 853 | void clear_incremental_midx_files(struct repository *r, |
| 854 | const struct strvec *keep_hashes) |
| 855 | { |
| 856 | struct odb_source *source = r->objects->sources; |
| 857 | struct strbuf chain = STRBUF_INIT; |
| 858 | |
| 859 | get_midx_chain_filename(source, &chain); |
| 860 | |
| 861 | for (; source; source = source->next) { |
| 862 | struct odb_source_files *files = odb_source_files_downcast(source); |
| 863 | if (files->packed->midx) |
| 864 | close_midx(files->packed->midx); |
| 865 | files->packed->midx = NULL; |
| 866 | } |
| 867 | |
| 868 | if (!keep_hashes && remove_path(chain.buf)) |
| 869 | die(_("failed to clear multi-pack-index chain at %s"), |
| 870 | chain.buf); |
| 871 | |
| 872 | clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_BITMAP, |
| 873 | keep_hashes); |
| 874 | clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_REV, |
| 875 | keep_hashes); |
| 876 | clear_incremental_midx_files_ext(r->objects->sources, MIDX_EXT_MIDX, |
| 877 | keep_hashes); |
| 878 | |
| 879 | strbuf_release(&chain); |
| 880 | } |
| 881 | |
| 882 | static int verify_midx_error; |
| 883 | |
| 884 | __attribute__((format (printf, 1, 2))) |
| 885 | static void midx_report(const char *fmt, ...) |
| 886 | { |
| 887 | va_list ap; |
| 888 | verify_midx_error = 1; |
| 889 | va_start(ap, fmt); |
| 890 | vfprintf(stderr, fmt, ap); |
| 891 | fprintf(stderr, "\n"); |
| 892 | va_end(ap); |
| 893 | } |
| 894 | |
| 895 | struct pair_pos_vs_id |
| 896 | { |
| 897 | uint32_t pos; |
| 898 | uint32_t pack_int_id; |
| 899 | }; |
| 900 | |
| 901 | static int compare_pair_pos_vs_id(const void *_a, const void *_b) |
| 902 | { |
| 903 | struct pair_pos_vs_id *a = (struct pair_pos_vs_id *)_a; |
| 904 | struct pair_pos_vs_id *b = (struct pair_pos_vs_id *)_b; |
| 905 | |
| 906 | return b->pack_int_id - a->pack_int_id; |
| 907 | } |
| 908 | |
| 909 | /* |
| 910 | * Limit calls to display_progress() for performance reasons. |
| 911 | * The interval here was arbitrarily chosen. |
| 912 | */ |
| 913 | #define SPARSE_PROGRESS_INTERVAL (1 << 12) |
| 914 | #define midx_display_sparse_progress(progress, n) \ |
| 915 | do { \ |
| 916 | uint64_t _n = (n); \ |
| 917 | if ((_n & (SPARSE_PROGRESS_INTERVAL - 1)) == 0) \ |
| 918 | display_progress(progress, _n); \ |
| 919 | } while (0) |
| 920 | |
| 921 | int verify_midx_file(struct odb_source *source, unsigned flags) |
| 922 | { |
| 923 | struct repository *r = source->odb->repo; |
| 924 | struct pair_pos_vs_id *pairs = NULL; |
| 925 | uint32_t i; |
| 926 | struct progress *progress = NULL; |
| 927 | struct multi_pack_index *m = load_multi_pack_index(source); |
| 928 | struct multi_pack_index *curr; |
| 929 | verify_midx_error = 0; |
| 930 | |
| 931 | if (!m) { |
| 932 | int result = 0; |
| 933 | struct stat sb; |
| 934 | struct strbuf filename = STRBUF_INIT; |
| 935 | |
| 936 | get_midx_filename(source, &filename); |
| 937 | |
| 938 | if (!stat(filename.buf, &sb)) { |
| 939 | error(_("multi-pack-index file exists, but failed to parse")); |
| 940 | result = 1; |
| 941 | } |
| 942 | strbuf_release(&filename); |
| 943 | return result; |
| 944 | } |
| 945 | |
| 946 | if (!midx_checksum_valid(m)) |
| 947 | midx_report(_("incorrect checksum")); |
| 948 | |
| 949 | if (flags & MIDX_PROGRESS) |
| 950 | progress = start_delayed_progress(r, |
| 951 | _("Looking for referenced packfiles"), |
| 952 | m->num_packs + m->num_packs_in_base); |
| 953 | for (i = 0; i < m->num_packs + m->num_packs_in_base; i++) { |
| 954 | if (prepare_midx_pack(m, i)) |
| 955 | midx_report("failed to load pack in position %d", i); |
| 956 | |
| 957 | display_progress(progress, i + 1); |
| 958 | } |
| 959 | stop_progress(&progress); |
| 960 | |
| 961 | if (m->num_objects == 0) { |
| 962 | midx_report(_("the midx contains no oid")); |
| 963 | /* |
| 964 | * Remaining tests assume that we have objects, so we can |
| 965 | * return here. |
| 966 | */ |
| 967 | goto cleanup; |
| 968 | } |
| 969 | |
| 970 | if (flags & MIDX_PROGRESS) |
| 971 | progress = start_sparse_progress(r, |
| 972 | _("Verifying OID order in multi-pack-index"), |
| 973 | m->num_objects - 1); |
| 974 | |
| 975 | for (curr = m; curr; curr = curr->base_midx) { |
| 976 | for (i = 0; i < m->num_objects - 1; i++) { |
| 977 | struct object_id oid1, oid2; |
| 978 | |
| 979 | nth_midxed_object_oid(&oid1, m, m->num_objects_in_base + i); |
| 980 | nth_midxed_object_oid(&oid2, m, m->num_objects_in_base + i + 1); |
| 981 | |
| 982 | if (oidcmp(&oid1, &oid2) >= 0) |
| 983 | midx_report(_("oid lookup out of order: oid[%d] = %s >= %s = oid[%d]"), |
| 984 | i, oid_to_hex(&oid1), oid_to_hex(&oid2), i + 1); |
| 985 | |
| 986 | midx_display_sparse_progress(progress, i + 1); |
| 987 | } |
| 988 | } |
| 989 | stop_progress(&progress); |
| 990 | |
| 991 | /* |
| 992 | * Create an array mapping each object to its packfile id. Sort it |
| 993 | * to group the objects by packfile. Use this permutation to visit |
| 994 | * each of the objects and only require 1 packfile to be open at a |
| 995 | * time. |
| 996 | */ |
| 997 | ALLOC_ARRAY(pairs, m->num_objects + m->num_objects_in_base); |
| 998 | for (i = 0; i < m->num_objects + m->num_objects_in_base; i++) { |
| 999 | pairs[i].pos = i; |
| 1000 | pairs[i].pack_int_id = nth_midxed_pack_int_id(m, i); |
| 1001 | } |
| 1002 | |
| 1003 | if (flags & MIDX_PROGRESS) |
| 1004 | progress = start_sparse_progress(r, |
| 1005 | _("Sorting objects by packfile"), |
| 1006 | m->num_objects); |
| 1007 | display_progress(progress, 0); /* TODO: Measure QSORT() progress */ |
| 1008 | QSORT(pairs, m->num_objects, compare_pair_pos_vs_id); |
| 1009 | stop_progress(&progress); |
| 1010 | |
| 1011 | if (flags & MIDX_PROGRESS) |
| 1012 | progress = start_sparse_progress(r, |
| 1013 | _("Verifying object offsets"), |
| 1014 | m->num_objects); |
| 1015 | for (i = 0; i < m->num_objects + m->num_objects_in_base; i++) { |
| 1016 | struct object_id oid; |
| 1017 | struct pack_entry e; |
| 1018 | off_t m_offset, p_offset; |
| 1019 | |
| 1020 | if (i > 0 && pairs[i-1].pack_int_id != pairs[i].pack_int_id && |
| 1021 | nth_midxed_pack(m, pairs[i-1].pack_int_id)) { |
| 1022 | uint32_t pack_int_id = pairs[i-1].pack_int_id; |
| 1023 | struct packed_git *p = nth_midxed_pack(m, pack_int_id); |
| 1024 | |
| 1025 | close_pack_fd(p); |
| 1026 | close_pack_index(p); |
| 1027 | } |
| 1028 | |
| 1029 | nth_midxed_object_oid(&oid, m, pairs[i].pos); |
| 1030 | |
| 1031 | if (!fill_midx_entry(m, &oid, &e)) { |
| 1032 | midx_report(_("failed to load pack entry for oid[%d] = %s"), |
| 1033 | pairs[i].pos, oid_to_hex(&oid)); |
| 1034 | continue; |
| 1035 | } |
| 1036 | |
| 1037 | if (open_pack_index(e.p)) { |
| 1038 | midx_report(_("failed to load pack-index for packfile %s"), |
| 1039 | e.p->pack_name); |
| 1040 | break; |
| 1041 | } |
| 1042 | |
| 1043 | m_offset = e.offset; |
| 1044 | p_offset = find_pack_entry_one(&oid, e.p); |
| 1045 | |
| 1046 | if (m_offset != p_offset) |
| 1047 | midx_report(_("incorrect object offset for oid[%d] = %s: %"PRIx64" != %"PRIx64), |
| 1048 | pairs[i].pos, oid_to_hex(&oid), m_offset, p_offset); |
| 1049 | |
| 1050 | midx_display_sparse_progress(progress, i + 1); |
| 1051 | } |
| 1052 | stop_progress(&progress); |
| 1053 | |
| 1054 | cleanup: |
| 1055 | free(pairs); |
| 1056 | close_midx(m); |
| 1057 | |
| 1058 | return verify_midx_error; |
| 1059 | } |