[PATCH v2 05/10] odb: provide infrastructure for pluggable fsck checks
From: Patrick Steinhardt <hidden>
Date: 2026-08-31 06:46:34
Subsystem:
the rest · Maintainer:
Linus Torvalds
The on-disk consistency checks in git-fsck(1) are conceptually backend-specific: while connectivity checks and object-level parsing checks are generic, verifying the physical integrity of packfiles and loose objects is meaningful only to backends that use these formats: Having these checks live in "builtin/fsck.c" violates that layering, because it forces the command to reach directly into format-specific internals. Provide new infrastructure to make these format-specific checks pluggable and implement stubs for the different source types we already have. In subsequent commits we'll move functionality over piece by piece. Signed-off-by: Patrick Steinhardt <redacted> --- builtin/fsck.c | 16 +++++++++++----- odb.c | 9 +++++++++ odb.h | 23 +++++++++++++++++++++++ odb/source-files.c | 13 +++++++++++++ odb/source-inmemory.c | 8 ++++++++ odb/source-loose.c | 7 +++++++ odb/source-packed.c | 8 ++++++++ odb/source.h | 21 +++++++++++++++++++++ 8 files changed, 100 insertions(+), 5 deletions(-)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 3f6056535f..adbe192e56 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c@@ -965,7 +965,9 @@ int cmd_fsck(int argc, const char *prefix, struct repository *repo) { - int check_full = 1; + struct odb_fsck_options odb_fsck_opts = { + .flags = ODB_FSCK_FULL, + }; int keep_cache_objects = 0; int name_objects = 0; int check_references = 1;
@@ -977,7 +979,8 @@ int cmd_fsck(int argc, OPT_BOOL(0, "root", &show_root, N_("report root nodes")), OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")), OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")), - OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")), + OPT_BIT(0, "full", &odb_fsck_opts.flags, + N_("also consider packs and alternate objects"), ODB_FSCK_FULL), OPT_BOOL(0, "connectivity-only", &connectivity_only, N_("check only connectivity")), OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")), OPT_BOOL(0, "lost-found", &write_lost_and_found,
@@ -1018,7 +1021,7 @@ int cmd_fsck(int argc, show_progress = 0; if (write_lost_and_found) { - check_full = 1; + odb_fsck_opts.flags |= ODB_FSCK_FULL; include_reflogs = 0; }
@@ -1047,10 +1050,13 @@ int cmd_fsck(int argc, mark_object_for_connectivity, repo, 0); } else { for (source = repo->objects->sources; source; source = source->next) - if (check_full || source->local) + if ((odb_fsck_opts.flags & ODB_FSCK_FULL) || source->local) fsck_source(repo, source); - if (check_full) { + if (odb_fsck(repo->objects, &odb_fsck_opts) < 0) + errors_found |= ERROR_OBJECT; + + if (odb_fsck_opts.flags & ODB_FSCK_FULL) { struct packed_git *p; uint32_t total = 0, count = 0; struct progress *progress = NULL;
diff --git a/odb.c b/odb.c
index 1fe20808eb..766043b685 100644
--- a/odb.c
+++ b/odb.c@@ -1177,3 +1177,12 @@ void odb_reprepare(struct object_database *o) { odb_prepare(o, ODB_PREPARE_FLUSH_CACHES); } + +int odb_fsck(struct object_database *odb, struct odb_fsck_options *options) +{ + int ret = 0; + for (struct odb_source *source = odb->sources; source; source = source->next) + if ((options->flags & ODB_FSCK_FULL) || source->local) + ret |= odb_source_fsck(source, options); + return ret; +}
diff --git a/odb.h b/odb.h
index e60174070f..76c15e48f5 100644
--- a/odb.h
+++ b/odb.h@@ -206,6 +206,29 @@ void odb_prepare(struct object_database *o, enum odb_prepare_flags flags); /* Equivalent to `odb_prepare(o, ODB_PREPARE_FLUSH_CACHES)`. */ void odb_reprepare(struct object_database *o); +enum odb_fsck_flags { + /* + * If set, perform a full consistency check for the full object + * database, including all of its sources and the contents of their + * optimized formats. Otherwise, only check the local source, and + * restrict checks of its optimized formats to cheap structural + * verification of their metadata. + */ + ODB_FSCK_FULL = (1 << 0), +}; + +/* Options that shall be passed to `odb_fsck()`. */ +struct odb_fsck_options { + enum odb_fsck_flags flags; +}; + +/* + * Run backend-specific integrity checks on all object sources. Each source + * performs the checks appropriate to its type. Returns 0 on success, a + * negative error code otherwise. + */ +int odb_fsck(struct object_database *odb, struct odb_fsck_options *opts); + /* * Find source by its object directory path. Returns a `NULL` pointer in case * the source could not be found.
diff --git a/odb/source-files.c b/odb/source-files.c
index bd4fdf3a6c..f6fb560d2e 100644
--- a/odb/source-files.c
+++ b/odb/source-files.c@@ -893,6 +893,18 @@ static int odb_source_files_generate_pack(struct odb_source *source UNUSED, return 0; } +static int odb_source_files_fsck(struct odb_source *source, + struct odb_fsck_options *opts) +{ + struct odb_source_files *files = odb_source_files_downcast(source); + int ret = 0; + + ret |= odb_source_fsck(&files->loose->base, opts); + ret |= odb_source_fsck(&files->packed->base, opts); + + return ret; +} + struct odb_source_files *odb_source_files_new(struct object_database *odb, const char *path, bool local)
@@ -908,6 +920,7 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb, files->base.close = odb_source_files_close; files->base.create_on_disk = odb_source_files_create_on_disk; files->base.prepare = odb_source_files_prepare; + files->base.fsck = odb_source_files_fsck; files->base.read_object_info = odb_source_files_read_object_info; files->base.read_object_stream = odb_source_files_read_object_stream; files->base.for_each_object = odb_source_files_for_each_object;
diff --git a/odb/source-inmemory.c b/odb/source-inmemory.c
index 795672adf2..ba0f86da26 100644
--- a/odb/source-inmemory.c
+++ b/odb/source-inmemory.c@@ -1,6 +1,7 @@ #include "git-compat-util.h" #include "object-file.h" #include "odb.h" +#include "fsck.h" #include "odb/source-inmemory.h" #include "odb/streaming.h" #include "oidtree.h"
@@ -368,6 +369,12 @@ static void odb_source_inmemory_free(struct odb_source *source) free(inmemory); } +static int odb_source_inmemory_fsck(struct odb_source *source UNUSED, + struct odb_fsck_options *opts UNUSED) +{ + return 0; +} + struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb) { struct odb_source_inmemory *source;
@@ -378,6 +385,7 @@ struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb) source->base.free = odb_source_inmemory_free; source->base.close = odb_source_inmemory_close; source->base.prepare = odb_source_inmemory_prepare; + source->base.fsck = odb_source_inmemory_fsck; source->base.read_object_info = odb_source_inmemory_read_object_info; source->base.read_object_stream = odb_source_inmemory_read_object_stream; source->base.for_each_object = odb_source_inmemory_for_each_object;
diff --git a/odb/source-loose.c b/odb/source-loose.c
index bb3455dfbd..f68d3c4d6c 100644
--- a/odb/source-loose.c
+++ b/odb/source-loose.c@@ -1031,6 +1031,12 @@ static void odb_source_loose_free(struct odb_source *source) free(loose); } +static int odb_source_loose_fsck(struct odb_source *source UNUSED, + struct odb_fsck_options *opts UNUSED) +{ + return 0; +} + struct odb_source_loose *odb_source_loose_new(struct object_database *odb, const char *path, bool local)
@@ -1043,6 +1049,7 @@ struct odb_source_loose *odb_source_loose_new(struct object_database *odb, loose->base.free = odb_source_loose_free; loose->base.close = odb_source_loose_close; loose->base.prepare = odb_source_loose_prepare; + loose->base.fsck = odb_source_loose_fsck; loose->base.read_object_info = odb_source_loose_read_object_info; loose->base.read_object_stream = odb_source_loose_read_object_stream; loose->base.for_each_object = odb_source_loose_for_each_object;
diff --git a/odb/source-packed.c b/odb/source-packed.c
index 630d955585..7aacf4bc45 100644
--- a/odb/source-packed.c
+++ b/odb/source-packed.c@@ -2,6 +2,7 @@ #include "abspath.h" #include "chdir-notify.h" #include "dir.h" +#include "fsck.h" #include "git-zlib.h" #include "list-objects-filter-options.h" #include "mergesort.h"
@@ -826,6 +827,12 @@ static void odb_source_packed_free(struct odb_source *source) free(packed); } +static int odb_source_packed_fsck(struct odb_source *source UNUSED, + struct odb_fsck_options *opts UNUSED) +{ + return 0; +} + struct odb_source_packed *odb_source_packed_new(struct object_database *odb, const char *path, bool local)
@@ -839,6 +846,7 @@ struct odb_source_packed *odb_source_packed_new(struct object_database *odb, packed->base.free = odb_source_packed_free; packed->base.close = odb_source_packed_close; packed->base.prepare = odb_source_packed_prepare; + packed->base.fsck = odb_source_packed_fsck; packed->base.read_object_info = odb_source_packed_read_object_info; packed->base.read_object_stream = odb_source_packed_read_object_stream; packed->base.for_each_object = odb_source_packed_for_each_object;
diff --git a/odb/source.h b/odb/source.h
index 559e2ea2e9..10a5dd5194 100644
--- a/odb/source.h
+++ b/odb/source.h@@ -320,6 +320,17 @@ struct odb_source { int (*generate_pack)(struct odb_source *source, struct odb_pack_generator **out, const struct odb_generate_pack_options *opts); + + /* + * This callback is expected to check the integrity of the object source + * and report any errors found via the fsck options. The checks performed + * are backend-specific. + * + * The callback is expected to return 0 on success, a negative error + * code otherwise. + */ + int (*fsck)(struct odb_source *source, + struct odb_fsck_options *options); }; /*
@@ -588,4 +599,14 @@ static inline int odb_source_generate_pack(struct odb_source *source, return source->generate_pack(source, out, opts); } +/* + * Check the integrity of the object database source. The checks performed + * are backend-specific. Returns 0 on success, a negative error code otherwise. + */ +static inline int odb_source_fsck(struct odb_source *source, + struct odb_fsck_options *opts) +{ + return source->fsck(source, opts); +} + #endif
--
2.55.0.979.g7e5102b832.dirty