odb: provide infrastructure for pluggable fsck checks

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 <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
Patrick Steinhardt 2026-09-11 15:27:29 +02:00 committed by Junio C Hamano
parent 485f5aeb94
commit a51b77aa1d
8 changed files with 102 additions and 5 deletions

View File

@ -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;

8
odb.c
View File

@ -1177,3 +1177,11 @@ 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)
ret |= odb_source_fsck(source, options);
return ret;
}

23
odb.h
View File

@ -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.

View File

@ -893,6 +893,21 @@ 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;

if (!(opts->flags & ODB_FSCK_FULL) && !source->local)
return 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 +923,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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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