Skip to content

Commit a02675a

Browse files
committed
Merge branch 'ds/fsck-pack-revindex'
"git fsck" learned to validate the on-disk pack reverse index files. * ds/fsck-pack-revindex: fsck: validate .rev file header fsck: check rev-index position values fsck: check rev-index checksums fsck: create scaffolding for rev-index checks
2 parents 849c8b3 + 5a6072f commit a02675a

File tree

5 files changed

+169
-4
lines changed

5 files changed

+169
-4
lines changed

builtin/fsck.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "resolve-undo.h"
2727
#include "run-command.h"
2828
#include "worktree.h"
29+
#include "pack-revindex.h"
2930

3031
#define REACHABLE 0x0001
3132
#define SEEN 0x0002
@@ -55,6 +56,7 @@ static int name_objects;
5556
#define ERROR_REFS 010
5657
#define ERROR_COMMIT_GRAPH 020
5758
#define ERROR_MULTI_PACK_INDEX 040
59+
#define ERROR_PACK_REV_INDEX 0100
5860

5961
static const char *describe_object(const struct object_id *oid)
6062
{
@@ -858,6 +860,38 @@ static int mark_packed_for_connectivity(const struct object_id *oid,
858860
return 0;
859861
}
860862

863+
static int check_pack_rev_indexes(struct repository *r, int show_progress)
864+
{
865+
struct progress *progress = NULL;
866+
uint32_t pack_count = 0;
867+
int res = 0;
868+
869+
if (show_progress) {
870+
for (struct packed_git *p = get_all_packs(the_repository); p; p = p->next)
871+
pack_count++;
872+
progress = start_delayed_progress("Verifying reverse pack-indexes", pack_count);
873+
pack_count = 0;
874+
}
875+
876+
for (struct packed_git *p = get_all_packs(the_repository); p; p = p->next) {
877+
int load_error = load_pack_revindex_from_disk(p);
878+
879+
if (load_error < 0) {
880+
error(_("unable to load rev-index for pack '%s'"), p->pack_name);
881+
res = ERROR_PACK_REV_INDEX;
882+
} else if (!load_error &&
883+
!load_pack_revindex(the_repository, p) &&
884+
verify_pack_revindex(p)) {
885+
error(_("invalid rev-index for pack '%s'"), p->pack_name);
886+
res = ERROR_PACK_REV_INDEX;
887+
}
888+
display_progress(progress, ++pack_count);
889+
}
890+
stop_progress(&progress);
891+
892+
return res;
893+
}
894+
861895
static char const * const fsck_usage[] = {
862896
N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
863897
" [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
@@ -1021,6 +1055,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
10211055
free_worktrees(worktrees);
10221056
}
10231057

1058+
errors_found |= check_pack_rev_indexes(the_repository, show_progress);
1059+
10241060
check_connectivity();
10251061

10261062
if (the_repository->settings.core_commit_graph) {

pack-bitmap.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
381381
goto cleanup;
382382
}
383383

384-
if (load_midx_revindex(bitmap_git->midx) < 0) {
384+
if (load_midx_revindex(bitmap_git->midx)) {
385385
warning(_("multi-pack bitmap is missing required reverse index"));
386386
goto cleanup;
387387
}
@@ -2142,7 +2142,7 @@ uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
21422142

21432143
if (!bitmap_is_midx(bitmap_git))
21442144
load_reverse_index(r, bitmap_git);
2145-
else if (load_midx_revindex(bitmap_git->midx) < 0)
2145+
else if (load_midx_revindex(bitmap_git->midx))
21462146
BUG("rebuild_existing_bitmaps: missing required rev-cache "
21472147
"extension");
21482148

pack-revindex.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "trace2.h"
88
#include "config.h"
99
#include "midx.h"
10+
#include "csum-file.h"
1011

1112
struct revindex_entry {
1213
off_t offset;
@@ -213,7 +214,8 @@ static int load_revindex_from_disk(char *revindex_name,
213214
fd = git_open(revindex_name);
214215

215216
if (fd < 0) {
216-
ret = -1;
217+
/* "No file" means return 1. */
218+
ret = 1;
217219
goto cleanup;
218220
}
219221
if (fstat(fd, &st)) {
@@ -265,7 +267,7 @@ static int load_revindex_from_disk(char *revindex_name,
265267
return ret;
266268
}
267269

268-
static int load_pack_revindex_from_disk(struct packed_git *p)
270+
int load_pack_revindex_from_disk(struct packed_git *p)
269271
{
270272
char *revindex_name;
271273
int ret;
@@ -303,6 +305,43 @@ int load_pack_revindex(struct repository *r, struct packed_git *p)
303305
return -1;
304306
}
305307

308+
/*
309+
* verify_pack_revindex verifies that the on-disk rev-index for the given
310+
* pack-file is the same that would be created if written from scratch.
311+
*
312+
* A negative number is returned on error.
313+
*/
314+
int verify_pack_revindex(struct packed_git *p)
315+
{
316+
int res = 0;
317+
318+
/* Do not bother checking if not initialized. */
319+
if (!p->revindex_map || !p->revindex_data)
320+
return res;
321+
322+
if (!hashfile_checksum_valid((const unsigned char *)p->revindex_map, p->revindex_size)) {
323+
error(_("invalid checksum"));
324+
res = -1;
325+
}
326+
327+
/* This may fail due to a broken .idx. */
328+
if (create_pack_revindex_in_memory(p))
329+
return res;
330+
331+
for (size_t i = 0; i < p->num_objects; i++) {
332+
uint32_t nr = p->revindex[i].nr;
333+
uint32_t rev_val = get_be32(p->revindex_data + i);
334+
335+
if (nr != rev_val) {
336+
error(_("invalid rev-index position at %"PRIu64": %"PRIu32" != %"PRIu32""),
337+
(uint64_t)i, nr, rev_val);
338+
res = -1;
339+
}
340+
}
341+
342+
return res;
343+
}
344+
306345
int load_midx_revindex(struct multi_pack_index *m)
307346
{
308347
struct strbuf revindex_name = STRBUF_INIT;

pack-revindex.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ struct repository;
5151
*/
5252
int load_pack_revindex(struct repository *r, struct packed_git *p);
5353

54+
/*
55+
* Specifically load a pack revindex from disk.
56+
*
57+
* Returns 0 on success, 1 on "no .rev file", and -1 when there is an
58+
* error parsing the .rev file.
59+
*/
60+
int load_pack_revindex_from_disk(struct packed_git *p);
61+
62+
/*
63+
* verify_pack_revindex verifies that the on-disk rev-index for the given
64+
* pack-file is the same that would be created if written from scratch.
65+
*
66+
* A negative number is returned on error.
67+
*/
68+
int verify_pack_revindex(struct packed_git *p);
69+
5470
/*
5571
* load_midx_revindex loads the '.rev' file corresponding to the given
5672
* multi-pack index by mmap-ing it and assigning pointers in the

t/t5325-reverse-index.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,78 @@ test_expect_success 'revindex in-memory vs on-disk' '
131131
test_cmp on-disk in-core
132132
)
133133
'
134+
135+
test_expect_success 'fsck succeeds on good rev-index' '
136+
test_when_finished rm -fr repo &&
137+
git init repo &&
138+
(
139+
cd repo &&
140+
141+
test_commit commit &&
142+
git -c pack.writeReverseIndex=true repack -ad &&
143+
git fsck 2>err &&
144+
test_must_be_empty err
145+
)
146+
'
147+
148+
test_expect_success 'set up rev-index corruption tests' '
149+
git init corrupt &&
150+
(
151+
cd corrupt &&
152+
153+
test_commit commit &&
154+
git -c pack.writeReverseIndex=true repack -ad &&
155+
156+
revfile=$(ls .git/objects/pack/pack-*.rev) &&
157+
chmod a+w $revfile &&
158+
cp $revfile $revfile.bak
159+
)
160+
'
161+
162+
corrupt_rev_and_verify () {
163+
(
164+
pos="$1" &&
165+
value="$2" &&
166+
error="$3" &&
167+
168+
cd corrupt &&
169+
revfile=$(ls .git/objects/pack/pack-*.rev) &&
170+
171+
# Reset to original rev-file.
172+
cp $revfile.bak $revfile &&
173+
174+
printf "$value" | dd of=$revfile bs=1 seek="$pos" conv=notrunc &&
175+
test_must_fail git fsck 2>err &&
176+
grep "$error" err
177+
)
178+
}
179+
180+
test_expect_success 'fsck catches invalid checksum' '
181+
revfile=$(ls corrupt/.git/objects/pack/pack-*.rev) &&
182+
orig_size=$(wc -c <$revfile) &&
183+
hashpos=$((orig_size - 10)) &&
184+
corrupt_rev_and_verify $hashpos bogus \
185+
"invalid checksum"
186+
'
187+
188+
test_expect_success 'fsck catches invalid row position' '
189+
corrupt_rev_and_verify 14 "\07" \
190+
"invalid rev-index position"
191+
'
192+
193+
test_expect_success 'fsck catches invalid header: magic number' '
194+
corrupt_rev_and_verify 1 "\07" \
195+
"reverse-index file .* has unknown signature"
196+
'
197+
198+
test_expect_success 'fsck catches invalid header: version' '
199+
corrupt_rev_and_verify 7 "\02" \
200+
"reverse-index file .* has unsupported version"
201+
'
202+
203+
test_expect_success 'fsck catches invalid header: hash function' '
204+
corrupt_rev_and_verify 11 "\03" \
205+
"reverse-index file .* has unsupported hash id"
206+
'
207+
134208
test_done

0 commit comments

Comments
 (0)