refs/reftable: add fsck check for checking the table name

Add glue code in 'refs/reftable-backend.c' which calls the reftable library to perform the fsck checks. Here we also map the reftable errors to Git' fsck errors. Introduce a check to validate table names for a given reftable stack. Also add 'badReftableTableName' as a corresponding error within Git. The reftable specification mentions: It suggested to use ${min_update_index}-${max_update_index}-${random}.ref as a naming convention. So treat non-conformant file names as warnings. While adding the fsck header to 'refs/reftable-backend.c', modify the list to maintain lexicographical ordering. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed Oct 7, 2025 at 14:11 UTC 466a3a1afdd82bb2b0e24e5cbed1ff3b35c19abd
5 files changed +115 -5
Documentation/fsck-msgids.adoc
+3
@@ -38,6 +38,9 @@
38 `badReferentName`::
39 (ERROR) The referent name of a symref is invalid.
40
41 +`badReftableTableName`::
42 + (WARN) A reftable table has an invalid name.
43 +
44 `badTagName`::
45 (INFO) A tag has an invalid format.
46
fsck.h
+1
@@ -73,6 +73,7 @@ enum fsck_msg_type {
73 FUNC(UNKNOWN_TYPE, ERROR) \
74 FUNC(ZERO_PADDED_DATE, ERROR) \
75 /* warnings */ \
76 + FUNC(BAD_REFTABLE_TABLE_NAME, WARN) \
77 FUNC(EMPTY_NAME, WARN) \
78 FUNC(FULL_PATHNAME, WARN) \
79 FUNC(HAS_DOT, WARN) \
refs/reftable-backend.c
+52 -5
@@ -6,6 +6,7 @@
6 #include "../config.h"
7 #include "../dir.h"
8 #include "../environment.h"
9 +#include "../fsck.h"
10 #include "../gettext.h"
11 #include "../hash.h"
12 #include "../hex.h"
@@ -15,10 +16,11 @@
16 #include "../path.h"
17 #include "../refs.h"
18 #include "../reftable/reftable-basics.h"
18 -#include "../reftable/reftable-stack.h"
19 -#include "../reftable/reftable-record.h"
19 #include "../reftable/reftable-error.h"
20 +#include "../reftable/reftable-fsck.h"
21 #include "../reftable/reftable-iterator.h"
22 +#include "../reftable/reftable-record.h"
23 +#include "../reftable/reftable-stack.h"
24 #include "../repo-settings.h"
25 #include "../setup.h"
26 #include "../strmap.h"
@@ -2674,11 +2676,56 @@ done:
2676 return ret;
2677 }
2678
2677 -static int reftable_be_fsck(struct ref_store *ref_store UNUSED,
2678 - struct fsck_options *o UNUSED,
2679 +static void reftable_fsck_verbose_handler(const char *msg, void *cb_data)
2680 +{
2681 + struct fsck_options *o = cb_data;
2682 +
2683 + if (o->verbose)
2684 + fprintf_ln(stderr, "%s", msg);
2685 +}
2686 +
2687 +static const enum fsck_msg_id fsck_msg_id_map[] = {
2688 + [REFTABLE_FSCK_ERROR_TABLE_NAME] = FSCK_MSG_BAD_REFTABLE_TABLE_NAME,
2689 +};
2690 +
2691 +static int reftable_fsck_error_handler(struct reftable_fsck_info *info,
2692 + void *cb_data)
2693 +{
2694 + struct fsck_ref_report report = { .path = info->path };
2695 + struct fsck_options *o = cb_data;
2696 + enum fsck_msg_id msg_id;
2697 +
2698 + if (info->error < 0 || info->error >= REFTABLE_FSCK_MAX_VALUE)
2699 + BUG("unknown fsck error: %d", (int)info->error);
2700 +
2701 + msg_id = fsck_msg_id_map[info->error];
2702 +
2703 + if (!msg_id)
2704 + BUG("fsck_msg_id value missing for reftable error: %d", (int)info->error);
2705 +
2706 + return fsck_report_ref(o, &report, msg_id, "%s", info->msg);
2707 +}
2708 +
2709 +static int reftable_be_fsck(struct ref_store *ref_store, struct fsck_options *o,
2710 struct worktree *wt UNUSED)
2711 {
2681 - return 0;
2712 + struct reftable_ref_store *refs;
2713 + struct strmap_entry *entry;
2714 + struct hashmap_iter iter;
2715 + int ret = 0;
2716 +
2717 + refs = reftable_be_downcast(ref_store, REF_STORE_READ, "fsck");
2718 +
2719 + ret |= reftable_fsck_check(refs->main_backend.stack, reftable_fsck_error_handler,
2720 + reftable_fsck_verbose_handler, o);
2721 +
2722 + strmap_for_each_entry(&refs->worktree_backends, &iter, entry) {
2723 + struct reftable_backend *b = (struct reftable_backend *)entry->value;
2724 + ret |= reftable_fsck_check(b->stack, reftable_fsck_error_handler,
2725 + reftable_fsck_verbose_handler, o);
2726 + }
2727 +
2728 + return ret;
2729 }
2730
2731 struct ref_storage_be refs_be_reftable = {
t/meson.build
+1
@@ -145,6 +145,7 @@ integration_tests = [
145 't0611-reftable-httpd.sh',
146 't0612-reftable-jgit-compatibility.sh',
147 't0613-reftable-write-options.sh',
148 + 't0614-reftable-fsck.sh',
149 't1000-read-tree-m-3way.sh',
150 't1001-read-tree-m-2way.sh',
151 't1002-read-tree-m-u-2way.sh',
t/t0614-reftable-fsck.sh new
+58
@@ -0,0 +1,58 @@
1 +#!/bin/sh
2 +
3 +test_description='Test reftable backend consistency check'
4 +
5 +GIT_TEST_DEFAULT_REF_FORMAT=reftable
6 +export GIT_TEST_DEFAULT_REF_FORMAT
7 +
8 +. ./test-lib.sh
9 +
10 +test_expect_success "no errors reported on a well formed repository" '
11 + test_when_finished "rm -rf repo" &&
12 + git init repo &&
13 + (
14 + cd repo &&
15 + git commit --allow-empty -m initial &&
16 +
17 + for i in $(test_seq 20)
18 + do
19 + git update-ref refs/heads/branch-$i HEAD || return 1
20 + done &&
21 +
22 + # The repository should end up with multiple tables.
23 + test_line_count ">" 1 .git/reftable/tables.list &&
24 +
25 + git refs verify 2>err &&
26 + test_must_be_empty err
27 + )
28 +'
29 +
30 +for TABLE_NAME in "foo-bar-e4d12d59.ref" \
31 + "0x00000000zzzz-0x00000000zzzz-e4d12d59.ref" \
32 + "0x000000000001-0x000000000002-e4d12d59.abc" \
33 + "0x000000000001-0x000000000002-e4d12d59.refabc"; do
34 + test_expect_success "table name $TABLE_NAME should be checked" '
35 + test_when_finished "rm -rf repo" &&
36 + git init repo &&
37 + (
38 + cd repo &&
39 + git commit --allow-empty -m initial &&
40 +
41 + git refs verify 2>err &&
42 + test_must_be_empty err &&
43 +
44 + EXISTING_TABLE=$(head -n1 .git/reftable/tables.list) &&
45 + mv ".git/reftable/$EXISTING_TABLE" ".git/reftable/$TABLE_NAME" &&
46 + sed "s/${EXISTING_TABLE}/${TABLE_NAME}/g" .git/reftable/tables.list > tables.list &&
47 + mv tables.list .git/reftable/tables.list &&
48 +
49 + git refs verify 2>err &&
50 + cat >expect <<-EOF &&
51 + warning: ${TABLE_NAME}: badReftableTableName: invalid reftable table name
52 + EOF
53 + test_cmp expect err
54 + )
55 + '
56 +done
57 +
58 +test_done