diff --git a/builtin/fsck.c b/builtin/fsck.c index 6d368f06f3..9af4cc085b 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -7,13 +7,11 @@ #include "blob.h" #include "tag.h" #include "refs.h" -#include "pack.h" #include "cache-tree.h" #include "fsck.h" #include "parse-options.h" #include "progress.h" #include "packfile.h" -#include "object-file.h" #include "object-name.h" #include "odb.h" #include "odb/streaming.h" @@ -24,8 +22,6 @@ #include "run-command.h" #include "sparse-index.h" #include "worktree.h" -#include "pack-revindex.h" -#include "pack-bitmap.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -37,10 +33,8 @@ static int show_root; static int show_tags; static int show_unreachable; static int include_reflogs = 1; -static int check_full = 1; static int connectivity_only; static int check_strict; -static int keep_cache_objects; static struct fsck_options fsck_walk_options; static struct fsck_options fsck_obj_options; static int errors_found; @@ -48,17 +42,11 @@ static int write_lost_and_found; static int verbose; static int show_progress = -1; static int show_dangling = 1; -static int name_objects; -static int check_references = 1; static timestamp_t now; #define ERROR_OBJECT 01 #define ERROR_REACHABLE 02 -#define ERROR_PACK 04 #define ERROR_REFS 010 #define ERROR_COMMIT_GRAPH 020 -#define ERROR_MULTI_PACK_INDEX 040 -#define ERROR_PACK_REV_INDEX 0100 -#define ERROR_BITMAP 0200 static const char *describe_object(const struct object_id *oid) { @@ -401,14 +389,27 @@ static void check_connectivity(struct repository *repo) } } -static int fsck_obj(struct repository *repo, - struct object *obj, void *buffer, unsigned long size) +static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, + unsigned long size, void *buffer, int *eaten, void *cb_data) { + struct repository *repo = cb_data; + struct object *obj; int err; - if (obj->flags & SEEN) - return 0; - obj->flags |= SEEN; + /* + * Note, buffer may be NULL if type is OBJ_BLOB. See + * verify_packfile(), data_valid variable for details. + */ + obj = parse_object_buffer(repo, oid, type, size, buffer, eaten); + if (!obj) { + errors_found |= ERROR_OBJECT; + err = error(_("%s: object corrupt or missing"), + oid_to_hex(oid)); + goto out; + } + + obj->flags &= ~REACHABLE; + obj->flags |= HAS_OBJ | SEEN; if (verbose) fprintf_ln(stderr, _("Checking %s %s"), @@ -417,6 +418,7 @@ static int fsck_obj(struct repository *repo, if (fsck_walk(obj, NULL, &fsck_obj_options)) objerror(repo, obj, _("broken links")); + err = fsck_object(obj, buffer, size, &fsck_obj_options); if (err) goto out; @@ -442,32 +444,11 @@ static int fsck_obj(struct repository *repo, } out: - if (obj->type == OBJ_TREE) + if (obj && obj->type == OBJ_TREE) free_tree_buffer((struct tree *)obj); return err; } -static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, - unsigned long size, void *buffer, int *eaten, void *cb_data) -{ - struct repository *repo = cb_data; - struct object *obj; - - /* - * Note, buffer may be NULL if type is OBJ_BLOB. See - * verify_packfile(), data_valid variable for details. - */ - obj = parse_object_buffer(repo, oid, type, size, buffer, eaten); - if (!obj) { - errors_found |= ERROR_OBJECT; - return error(_("%s: object corrupt or missing"), - oid_to_hex(oid)); - } - obj->flags &= ~(REACHABLE | SEEN); - obj->flags |= HAS_OBJ; - return fsck_obj(repo, obj, buffer, size); -} - static int default_refs; static void fsck_handle_reflog_oid(struct repository *repo, @@ -713,103 +694,6 @@ static void process_refs(struct repository *repo, struct snapshot *snap) } } -struct for_each_loose_cb { - struct repository *repo; - struct progress *progress; -}; - -static int fsck_loose(const struct object_id *oid, const char *path, - void *cb_data) -{ - struct for_each_loose_cb *data = cb_data; - struct object *obj; - enum object_type type = OBJ_NONE; - size_t size; - void *contents = NULL; - int eaten; - struct object_info oi = OBJECT_INFO_INIT; - struct object_id real_oid = *null_oid(data->repo->hash_algo); - int err = 0; - - oi.sizep = &size; - oi.typep = &type; - - if (read_loose_object(data->repo, path, oid, &real_oid, &contents, &oi) < 0) { - if (contents && !oideq(&real_oid, oid)) - err = error(_("%s: hash-path mismatch, found at: %s"), - oid_to_hex(&real_oid), path); - else - err = error(_("%s: object corrupt or missing: %s"), - oid_to_hex(oid), path); - } - if (err < 0) { - errors_found |= ERROR_OBJECT; - free(contents); - return 0; /* keep checking other objects */ - } - - if (!contents && type != OBJ_BLOB) - BUG("read_loose_object streamed a non-blob"); - - obj = parse_object_buffer(data->repo, oid, type, size, - contents, &eaten); - - if (!obj) { - errors_found |= ERROR_OBJECT; - error(_("%s: object could not be parsed: %s"), - oid_to_hex(oid), path); - if (!eaten) - free(contents); - return 0; /* keep checking other objects */ - } - - obj->flags &= ~(REACHABLE | SEEN); - obj->flags |= HAS_OBJ; - if (fsck_obj(data->repo, obj, contents, size)) - errors_found |= ERROR_OBJECT; - - if (!eaten) - free(contents); - return 0; /* keep checking other objects, even if we saw an error */ -} - -static int fsck_cruft(const char *basename, const char *path, - void *data UNUSED) -{ - if (!starts_with(basename, "tmp_obj_")) - fprintf_ln(stderr, _("bad sha1 file: %s"), path); - return 0; -} - -static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data) -{ - struct for_each_loose_cb *cb_data = data; - struct progress *progress = cb_data->progress; - display_progress(progress, nr + 1); - return 0; -} - -static void fsck_source(struct repository *repo, struct odb_source *source) -{ - struct progress *progress = NULL; - struct for_each_loose_cb cb_data = { - .repo = source->odb->repo, - .progress = progress, - }; - - if (verbose) - fprintf_ln(stderr, _("Checking object directory")); - - if (show_progress) - progress = start_progress(repo, - _("Checking object directories"), 256); - - for_each_loose_file_in_source(source, fsck_loose, - fsck_cruft, fsck_subdir, &cb_data); - display_progress(progress, 256); - stop_progress(&progress); -} - static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path) { int i; @@ -918,40 +802,6 @@ static int mark_object_for_connectivity(const struct object_id *oid, return 0; } -static int check_pack_rev_indexes(struct repository *r, int show_progress) -{ - struct progress *progress = NULL; - struct packed_git *p; - uint32_t pack_count = 0; - int res = 0; - - if (show_progress) { - repo_for_each_pack(r, p) - pack_count++; - progress = start_delayed_progress(r, - "Verifying reverse pack-indexes", pack_count); - pack_count = 0; - } - - repo_for_each_pack(r, p) { - int load_error = load_pack_revindex_from_disk(p); - - if (load_error < 0) { - error(_("unable to load rev-index for pack '%s'"), p->pack_name); - res = ERROR_PACK_REV_INDEX; - } else if (!load_error && - !load_pack_revindex(r, p) && - verify_pack_revindex(p)) { - error(_("invalid rev-index for pack '%s'"), p->pack_name); - res = ERROR_PACK_REV_INDEX; - } - display_progress(progress, ++pack_count); - } - stop_progress(&progress); - - return res; -} - static void fsck_refs(struct repository *r) { struct child_process refs_verify = CHILD_PROCESS_INIT; @@ -986,30 +836,38 @@ static char const * const fsck_usage[] = { NULL }; -static struct option fsck_opts[] = { - OPT__VERBOSE(&verbose, N_("be verbose")), - OPT_BOOL(0, "unreachable", &show_unreachable, N_("show unreachable objects")), - OPT_BOOL(0, "dangling", &show_dangling, N_("show dangling objects")), - OPT_BOOL(0, "tags", &show_tags, N_("report tags")), - 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_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, - N_("write dangling objects in .git/lost-found")), - OPT_BOOL(0, "progress", &show_progress, N_("show progress")), - OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")), - OPT_BOOL(0, "references", &check_references, N_("check reference database consistency")), - OPT_END(), -}; - int cmd_fsck(int argc, const char **argv, const char *prefix, struct repository *repo) { + struct odb_fsck_options odb_fsck_opts = { + .flags = ODB_FSCK_FULL, + .object_cb = fsck_obj_buffer, + .object_payload = repo, + }; + int keep_cache_objects = 0; + int name_objects = 0; + int check_references = 1; + struct option fsck_opts[] = { + OPT__VERBOSE(&verbose, N_("be verbose")), + OPT_BOOL(0, "unreachable", &show_unreachable, N_("show unreachable objects")), + OPT_BOOL(0, "dangling", &show_dangling, N_("show dangling objects")), + OPT_BOOL(0, "tags", &show_tags, N_("report tags")), + 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_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, + N_("write dangling objects in .git/lost-found")), + OPT_BOOL(0, "progress", &show_progress, N_("show progress")), + OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")), + OPT_BOOL(0, "references", &check_references, N_("check reference database consistency")), + OPT_END(), + }; struct odb_source *source; struct snapshot snap = { .nr = 0, @@ -1037,11 +895,15 @@ int cmd_fsck(int argc, if (show_progress == -1) show_progress = isatty(2); - if (verbose) + if (verbose) { show_progress = 0; + odb_fsck_opts.flags |= ODB_FSCK_VERBOSE; + } + if (show_progress) + odb_fsck_opts.flags |= ODB_FSCK_PROGRESS; if (write_lost_and_found) { - check_full = 1; + odb_fsck_opts.flags |= ODB_FSCK_FULL; include_reflogs = 0; } @@ -1069,35 +931,8 @@ int cmd_fsck(int argc, odb_for_each_object(repo->objects, NULL, mark_object_for_connectivity, repo, 0); } else { - for (source = repo->objects->sources; source; source = source->next) - fsck_source(repo, source); - - if (check_full) { - struct packed_git *p; - uint32_t total = 0, count = 0; - struct progress *progress = NULL; - - if (show_progress) { - repo_for_each_pack(repo, p) { - if (open_pack_index(p)) - continue; - total += p->num_objects; - } - - progress = start_progress(repo, - _("Checking objects"), total); - } - - repo_for_each_pack(repo, p) { - /* verify gives error messages itself */ - if (verify_pack(repo, - p, fsck_obj_buffer, repo, - progress, count)) - errors_found |= ERROR_PACK; - count += p->num_objects; - } - stop_progress(&progress); - } + if (odb_fsck(repo->objects, &odb_fsck_opts) < 0) + errors_found |= ERROR_OBJECT; if (fsck_finish(&fsck_obj_options)) errors_found |= ERROR_OBJECT; @@ -1145,10 +980,6 @@ int cmd_fsck(int argc, free_worktrees(worktrees); } - errors_found |= check_pack_rev_indexes(repo, show_progress); - if (verify_bitmap_files(repo)) - errors_found |= ERROR_BITMAP; - check_connectivity(repo); if (repo->settings.core_commit_graph) { @@ -1168,23 +999,6 @@ int cmd_fsck(int argc, } } - if (repo->settings.core_multi_pack_index) { - struct child_process midx_verify = CHILD_PROCESS_INIT; - - for (source = repo->objects->sources; source; source = source->next) { - child_process_init(&midx_verify); - midx_verify.git_cmd = 1; - strvec_pushl(&midx_verify.args, "multi-pack-index", - "verify", "--object-dir", source->path, NULL); - if (show_progress) - strvec_push(&midx_verify.args, "--progress"); - else - strvec_push(&midx_verify.args, "--no-progress"); - if (run_command(&midx_verify)) - errors_found |= ERROR_MULTI_PACK_INDEX; - } - } - free_snapshot_refs(&snap); return errors_found; } diff --git a/odb.c b/odb.c index 40b0e13539..5d6db482e2 100644 --- a/odb.c +++ b/odb.c @@ -1190,3 +1190,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 24b45238a0..9ccc0e281d 100644 --- a/odb.h +++ b/odb.h @@ -198,6 +198,39 @@ 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), + + /* Display a progress meter, if sensible. */ + ODB_FSCK_PROGRESS = (1 << 1), + + /* Be extra verbose when checking the database. */ + ODB_FSCK_VERBOSE = (1 << 2), +}; + +/* Options that shall be passed to `odb_fsck()`. */ +struct odb_fsck_options { + enum odb_fsck_flags flags; + + int (*object_cb)(const struct object_id *oid, enum object_type type, + unsigned long size, void *buffer, int *eaten, void *cb_data); + void *object_payload; +}; + +/* + * 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 28747b580d..6efdbab901 100644 --- a/odb/source-files.c +++ b/odb/source-files.c @@ -928,6 +928,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) @@ -943,6 +955,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 8aff27c2f9..f67c74a724 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" @@ -363,6 +364,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; @@ -373,6 +380,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 57ad423523..f86841292b 100644 --- a/odb/source-loose.c +++ b/odb/source-loose.c @@ -12,6 +12,7 @@ #include "odb/streaming.h" #include "oidtree.h" #include "path.h" +#include "progress.h" #include "repository.h" #include "strbuf.h" #include "tempfile.h" @@ -1032,6 +1033,96 @@ static void odb_source_loose_free(struct odb_source *source) free(loose); } +struct fsck_loose_data { + struct odb_source_loose *source; + struct odb_fsck_options *opts; + struct progress *progress; + bool error_found; +}; + +static int fsck_loose(const struct object_id *oid, const char *path, + void *cb_data) +{ + struct fsck_loose_data *data = cb_data; + enum object_type type = OBJ_NONE; + size_t size; + void *contents = NULL; + int eaten = 0; + struct object_info oi = OBJECT_INFO_INIT; + struct object_id real_oid = *null_oid(data->source->base.odb->repo->hash_algo); + int err = 0; + + oi.sizep = &size; + oi.typep = &type; + + if (read_loose_object(data->source->base.odb->repo, + path, oid, &real_oid, &contents, &oi) < 0) { + if (contents && !oideq(&real_oid, oid)) + err = error(_("%s: hash-path mismatch, found at: %s"), + oid_to_hex(&real_oid), path); + else + err = error(_("%s: object corrupt or missing: %s"), + oid_to_hex(oid), path); + } + if (err < 0) + goto out; + + if (!contents && type != OBJ_BLOB) + BUG("read_loose_object streamed a non-blob"); + + if (data->opts->object_cb(oid, type, size, contents, &eaten, + data->opts->object_payload)) { + err = -1; + goto out; + } + +out: + if (err) + data->error_found = true; + if (!eaten) + free(contents); + return 0; /* keep checking other objects, even if we saw an error */ +} + +static int fsck_cruft(const char *basename, const char *path, + void *data UNUSED) +{ + if (!starts_with(basename, "tmp_obj_")) + fprintf_ln(stderr, _("bad sha1 file: %s"), path); + return 0; +} + +static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *cb_data) +{ + struct fsck_loose_data *data = cb_data; + display_progress(data->progress, nr + 1); + return 0; +} + +static int odb_source_loose_fsck(struct odb_source *source, + struct odb_fsck_options *opts) +{ + struct odb_source_loose *loose = odb_source_loose_downcast(source); + struct fsck_loose_data data = { + .source = loose, + .opts = opts, + }; + + if (opts->flags & ODB_FSCK_VERBOSE) + fprintf_ln(stderr, _("Checking object directory")); + + if (opts->flags & ODB_FSCK_PROGRESS) + data.progress = start_progress(source->odb->repo, + _("Checking object directories"), 256); + + for_each_loose_file_in_source(source, fsck_loose, + fsck_cruft, fsck_subdir, &data); + display_progress(data.progress, 256); + stop_progress(&data.progress); + + return data.error_found ? -1 : 0; +} + struct odb_source_loose *odb_source_loose_new(struct object_database *odb, const char *path, bool local) @@ -1044,6 +1135,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 9bea049b20..b4d05914e6 100644 --- a/odb/source-packed.c +++ b/odb/source-packed.c @@ -3,6 +3,7 @@ #include "chdir-notify.h" #include "dir.h" #include "gettext.h" +#include "fsck.h" #include "git-zlib.h" #include "hex.h" #include "list-objects-filter-options.h" @@ -10,9 +11,13 @@ #include "midx.h" #include "odb/source-packed.h" #include "odb/streaming.h" +#include "pack.h" +#include "pack-revindex.h" #include "packfile.h" #include "pack-bitmap.h" #include "strbuf.h" +#include "progress.h" +#include "run-command.h" static int find_pack_entry(struct odb_source_packed *store, const struct object_id *oid, @@ -849,6 +854,117 @@ static void odb_source_packed_free(struct odb_source *source) free(packed); } +static int verify_packs(struct odb_source_packed *source, + struct odb_fsck_options *opts) +{ + struct progress *progress = NULL; + struct packfile_list_entry *e; + uint32_t total = 0, count = 0; + int ret = 0; + + if (opts->flags & ODB_FSCK_PROGRESS) { + for (e = packfile_store_get_packs(source); e; e = e->next) { + if (open_pack_index(e->pack)) + continue; + total += e->pack->num_objects; + } + + progress = start_progress(source->base.odb->repo, + _("Checking objects"), total); + } + + for (e = packfile_store_get_packs(source); e; e = e->next) { + /* verify gives error messages itself */ + if (verify_pack(source->base.odb->repo, e->pack, + opts->object_cb, opts->object_payload, + progress, count)) + ret = -1; + count += e->pack->num_objects; + } + stop_progress(&progress); + + return ret; +} + +static int verify_reverse_indices(struct odb_source_packed *source, + struct odb_fsck_options *opts) +{ + struct progress *progress = NULL; + struct packfile_list_entry *e; + uint32_t pack_count = 0; + int res = 0; + + if (opts->flags & ODB_FSCK_PROGRESS) { + for (e = packfile_store_get_packs(source); e; e = e->next) + pack_count++; + progress = start_delayed_progress(source->base.odb->repo, + "Verifying reverse pack-indexes", pack_count); + pack_count = 0; + } + + for (e = packfile_store_get_packs(source); e; e = e->next) { + int load_error = load_pack_revindex_from_disk(e->pack); + + if (load_error < 0) { + error(_("unable to load rev-index for pack '%s'"), e->pack->pack_name); + res = -1; + } else if (!load_error && + !load_pack_revindex(source->base.odb->repo, e->pack) && + verify_pack_revindex(e->pack)) { + error(_("invalid rev-index for pack '%s'"), e->pack->pack_name); + res = -1; + } + display_progress(progress, ++pack_count); + } + stop_progress(&progress); + + return res; +} + +static int verify_midx(struct odb_source_packed *source, + struct odb_fsck_options *opts) +{ + struct child_process midx_verify = CHILD_PROCESS_INIT; + int ret = 0; + + if (!source->base.odb->repo->settings.core_multi_pack_index) + return 0; + + child_process_init(&midx_verify); + midx_verify.git_cmd = 1; + strvec_pushl(&midx_verify.args, "multi-pack-index", + "verify", "--object-dir", source->base.path, NULL); + if (opts->flags & ODB_FSCK_PROGRESS) + strvec_push(&midx_verify.args, "--progress"); + else + strvec_push(&midx_verify.args, "--no-progress"); + if (run_command(&midx_verify)) + ret = -1; + + return ret; +} + +static int odb_source_packed_fsck(struct odb_source *source, + struct odb_fsck_options *opts) +{ + struct odb_source_packed *packed = odb_source_packed_downcast(source); + int ret = 0; + + if ((opts->flags & ODB_FSCK_FULL) && verify_packs(packed, opts) < 0) + ret = -1; + + if (verify_reverse_indices(packed, opts) < 0) + ret = -1; + + if (verify_bitmap_files(packed)) + ret = -1; + + if (verify_midx(packed, opts) < 0) + ret = -1; + + return ret; +} + struct odb_source_packed *odb_source_packed_new(struct object_database *odb, const char *path, bool local) @@ -862,6 +978,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 16f6662350..ea00873763 100644 --- a/odb/source.h +++ b/odb/source.h @@ -322,6 +322,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); }; /* @@ -583,4 +594,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 diff --git a/pack-bitmap.c b/pack-bitmap.c index e0fb57d332..3de8e9590c 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -3410,28 +3410,22 @@ static int verify_bitmap_file(const struct git_hash_algo *algop, return res; } -int verify_bitmap_files(struct repository *r) +int verify_bitmap_files(struct odb_source_packed *source) { - struct odb_source *source; - struct packed_git *p; + struct packfile_list_entry *e; + struct multi_pack_index *m; int res = 0; - for (source = r->objects->sources; source; source = source->next) { - struct odb_source_files *files = odb_source_files_downcast(source); - struct multi_pack_index *m = get_multi_pack_index(files->packed); - char *midx_bitmap_name; - - if (!m) - continue; - - midx_bitmap_name = midx_bitmap_filename(m); - res |= verify_bitmap_file(r->hash_algo, midx_bitmap_name); + m = get_multi_pack_index(source); + if (m) { + char *midx_bitmap_name = midx_bitmap_filename(m); + res |= verify_bitmap_file(source->base.odb->repo->hash_algo, midx_bitmap_name); free(midx_bitmap_name); } - repo_for_each_pack(r, p) { - char *pack_bitmap_name = pack_bitmap_filename(p); - res |= verify_bitmap_file(r->hash_algo, pack_bitmap_name); + for (e = packfile_store_get_packs(source); e; e = e->next) { + char *pack_bitmap_name = pack_bitmap_filename(e->pack); + res |= verify_bitmap_file(source->base.odb->repo->hash_algo, pack_bitmap_name); free(pack_bitmap_name); } diff --git a/pack-bitmap.h b/pack-bitmap.h index 1385027c1f..847ad4762d 100644 --- a/pack-bitmap.h +++ b/pack-bitmap.h @@ -205,7 +205,7 @@ int bitmap_is_midx(struct bitmap_index *bitmap_git); int bitmap_is_preferred_refname(struct repository *r, const char *refname); -int verify_bitmap_files(struct repository *r); +int verify_bitmap_files(struct odb_source_packed *source); struct ewah_bitmap *read_bitmap(const unsigned char *map, size_t map_size, size_t *map_pos); diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 77cd96de78..1b4074304c 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -844,6 +844,11 @@ test_expect_success 'alternate objects are correctly blamed' ' echo "../../alt.git/objects" >.git/objects/info/alternates && mkdir alt.git/objects/$(dirname $path) && >alt.git/objects/$(dirname $path)/$(basename $path) && + + # Without "--full", only the local object source is checked. + git fsck --no-full >out 2>&1 && + test_must_be_empty out && + test_must_fail git fsck >out 2>&1 && test_grep alt.git out ' diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index 68143cb5b7..20b010c33b 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -573,6 +573,19 @@ test_expect_success 'verify incorrect checksum' ' $objdir "incorrect checksum" ' +test_expect_success 'git fsck --no-full checks multi-pack-index, --connectivity-only does not' ' + pos=$(($(wc -c <$objdir/pack/multi-pack-index) - 10)) && + corrupt_midx_and_verify $pos \ + "\377\377\377\377\377\377\377\377\377\377" \ + $objdir "incorrect checksum" && + + test_must_fail git fsck --no-full 2>err && + test_grep "incorrect checksum" err && + + git fsck --connectivity-only 2>err && + test_grep ! "incorrect checksum" err +' + test_expect_success 'setup for v1-specific fsck tests' ' git -c midx.version=1 multi-pack-index write ' diff --git a/t/t5325-reverse-index.sh b/t/t5325-reverse-index.sh index 5493791938..6b81abf663 100755 --- a/t/t5325-reverse-index.sh +++ b/t/t5325-reverse-index.sh @@ -204,4 +204,12 @@ test_expect_success 'fsck catches invalid header: hash function' ' "reverse-index file .* has unsupported hash id" ' +test_expect_success 'fsck --no-full checks rev-index, --connectivity-only does not' ' + test_must_fail git -C corrupt fsck --no-full 2>err && + test_grep "has unsupported hash id" err && + + git -C corrupt fsck --connectivity-only 2>err && + test_grep ! "has unsupported hash id" err +' + test_done diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh index 86beab1dae..8047459b00 100755 --- a/t/t5326-multi-pack-bitmaps.sh +++ b/t/t5326-multi-pack-bitmaps.sh @@ -498,7 +498,15 @@ test_expect_success 'git fsck correctly identifies good and bad bitmaps' ' corrupt_file "$packbitmap" && test_must_fail git fsck 2>err && test_grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err && - test_grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err + test_grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err && + + # The bitmap checks are performed with "--no-full", but not with + # "--connectivity-only". + test_must_fail git fsck --no-full 2>err && + test_grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err && + test_grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err && + git fsck --connectivity-only 2>err && + test_grep ! "invalid checksum" err ' test_expect_success 'corrupt MIDX with bitmap causes fallback' '