Coverage Report

Created: 2026-10-17 07:15

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