Merge branch 'ps/odb-pluggable-fsck' into jch
The consistency checks for the object database (fsck) have been decoupled from the generic builtin implementation and moved into the backend-specific object source layers, making them pluggable for different object storage formats. * ps/odb-pluggable-fsck: builtin/fsck: move loose object verification into the loose source builtin/fsck: move multi-pack index verification into the packed source builtin/fsck: move bitmap verification into the packed source builtin/fsck: move reverse index verification into the packed source builtin/fsck: move packfile verification into the packed source odb: provide infrastructure for pluggable fsck checks builtin/fsck: don't check alternates with "--no-full" builtin/fsck: de-globalize option handling builtin/fsck: merge `fsck_obj_buffer()` and `fsck_obj()` builtin/fsck: use `fsck_obj_buffer()` when checking loose objectsjch
commit
9f70572c3d
296
builtin/fsck.c
296
builtin/fsck.c
|
|
@ -7,13 +7,11 @@
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "pack.h"
|
|
||||||
#include "cache-tree.h"
|
#include "cache-tree.h"
|
||||||
#include "fsck.h"
|
#include "fsck.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
#include "progress.h"
|
#include "progress.h"
|
||||||
#include "packfile.h"
|
#include "packfile.h"
|
||||||
#include "object-file.h"
|
|
||||||
#include "object-name.h"
|
#include "object-name.h"
|
||||||
#include "odb.h"
|
#include "odb.h"
|
||||||
#include "odb/streaming.h"
|
#include "odb/streaming.h"
|
||||||
|
|
@ -24,8 +22,6 @@
|
||||||
#include "run-command.h"
|
#include "run-command.h"
|
||||||
#include "sparse-index.h"
|
#include "sparse-index.h"
|
||||||
#include "worktree.h"
|
#include "worktree.h"
|
||||||
#include "pack-revindex.h"
|
|
||||||
#include "pack-bitmap.h"
|
|
||||||
|
|
||||||
#define REACHABLE 0x0001
|
#define REACHABLE 0x0001
|
||||||
#define SEEN 0x0002
|
#define SEEN 0x0002
|
||||||
|
|
@ -37,10 +33,8 @@ static int show_root;
|
||||||
static int show_tags;
|
static int show_tags;
|
||||||
static int show_unreachable;
|
static int show_unreachable;
|
||||||
static int include_reflogs = 1;
|
static int include_reflogs = 1;
|
||||||
static int check_full = 1;
|
|
||||||
static int connectivity_only;
|
static int connectivity_only;
|
||||||
static int check_strict;
|
static int check_strict;
|
||||||
static int keep_cache_objects;
|
|
||||||
static struct fsck_options fsck_walk_options;
|
static struct fsck_options fsck_walk_options;
|
||||||
static struct fsck_options fsck_obj_options;
|
static struct fsck_options fsck_obj_options;
|
||||||
static int errors_found;
|
static int errors_found;
|
||||||
|
|
@ -48,17 +42,11 @@ static int write_lost_and_found;
|
||||||
static int verbose;
|
static int verbose;
|
||||||
static int show_progress = -1;
|
static int show_progress = -1;
|
||||||
static int show_dangling = 1;
|
static int show_dangling = 1;
|
||||||
static int name_objects;
|
|
||||||
static int check_references = 1;
|
|
||||||
static timestamp_t now;
|
static timestamp_t now;
|
||||||
#define ERROR_OBJECT 01
|
#define ERROR_OBJECT 01
|
||||||
#define ERROR_REACHABLE 02
|
#define ERROR_REACHABLE 02
|
||||||
#define ERROR_PACK 04
|
|
||||||
#define ERROR_REFS 010
|
#define ERROR_REFS 010
|
||||||
#define ERROR_COMMIT_GRAPH 020
|
#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)
|
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,
|
static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
|
||||||
struct object *obj, void *buffer, unsigned long size)
|
unsigned long size, void *buffer, int *eaten, void *cb_data)
|
||||||
{
|
{
|
||||||
|
struct repository *repo = cb_data;
|
||||||
|
struct object *obj;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (obj->flags & SEEN)
|
/*
|
||||||
return 0;
|
* Note, buffer may be NULL if type is OBJ_BLOB. See
|
||||||
obj->flags |= SEEN;
|
* 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)
|
if (verbose)
|
||||||
fprintf_ln(stderr, _("Checking %s %s"),
|
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))
|
if (fsck_walk(obj, NULL, &fsck_obj_options))
|
||||||
objerror(repo, obj, _("broken links"));
|
objerror(repo, obj, _("broken links"));
|
||||||
|
|
||||||
err = fsck_object(obj, buffer, size, &fsck_obj_options);
|
err = fsck_object(obj, buffer, size, &fsck_obj_options);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -442,32 +444,11 @@ static int fsck_obj(struct repository *repo,
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (obj->type == OBJ_TREE)
|
if (obj && obj->type == OBJ_TREE)
|
||||||
free_tree_buffer((struct tree *)obj);
|
free_tree_buffer((struct tree *)obj);
|
||||||
return err;
|
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 int default_refs;
|
||||||
|
|
||||||
static void fsck_handle_reflog_oid(struct repository *repo,
|
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)
|
static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -918,40 +802,6 @@ static int mark_object_for_connectivity(const struct object_id *oid,
|
||||||
return 0;
|
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)
|
static void fsck_refs(struct repository *r)
|
||||||
{
|
{
|
||||||
struct child_process refs_verify = CHILD_PROCESS_INIT;
|
struct child_process refs_verify = CHILD_PROCESS_INIT;
|
||||||
|
|
@ -986,30 +836,38 @@ static char const * const fsck_usage[] = {
|
||||||
NULL
|
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,
|
int cmd_fsck(int argc,
|
||||||
const char **argv,
|
const char **argv,
|
||||||
const char *prefix,
|
const char *prefix,
|
||||||
struct repository *repo)
|
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 odb_source *source;
|
||||||
struct snapshot snap = {
|
struct snapshot snap = {
|
||||||
.nr = 0,
|
.nr = 0,
|
||||||
|
|
@ -1037,11 +895,15 @@ int cmd_fsck(int argc,
|
||||||
|
|
||||||
if (show_progress == -1)
|
if (show_progress == -1)
|
||||||
show_progress = isatty(2);
|
show_progress = isatty(2);
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
show_progress = 0;
|
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) {
|
if (write_lost_and_found) {
|
||||||
check_full = 1;
|
odb_fsck_opts.flags |= ODB_FSCK_FULL;
|
||||||
include_reflogs = 0;
|
include_reflogs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1069,35 +931,8 @@ int cmd_fsck(int argc,
|
||||||
odb_for_each_object(repo->objects, NULL,
|
odb_for_each_object(repo->objects, NULL,
|
||||||
mark_object_for_connectivity, repo, 0);
|
mark_object_for_connectivity, repo, 0);
|
||||||
} else {
|
} else {
|
||||||
for (source = repo->objects->sources; source; source = source->next)
|
if (odb_fsck(repo->objects, &odb_fsck_opts) < 0)
|
||||||
fsck_source(repo, source);
|
errors_found |= ERROR_OBJECT;
|
||||||
|
|
||||||
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 (fsck_finish(&fsck_obj_options))
|
if (fsck_finish(&fsck_obj_options))
|
||||||
errors_found |= ERROR_OBJECT;
|
errors_found |= ERROR_OBJECT;
|
||||||
|
|
@ -1145,10 +980,6 @@ int cmd_fsck(int argc,
|
||||||
free_worktrees(worktrees);
|
free_worktrees(worktrees);
|
||||||
}
|
}
|
||||||
|
|
||||||
errors_found |= check_pack_rev_indexes(repo, show_progress);
|
|
||||||
if (verify_bitmap_files(repo))
|
|
||||||
errors_found |= ERROR_BITMAP;
|
|
||||||
|
|
||||||
check_connectivity(repo);
|
check_connectivity(repo);
|
||||||
|
|
||||||
if (repo->settings.core_commit_graph) {
|
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);
|
free_snapshot_refs(&snap);
|
||||||
return errors_found;
|
return errors_found;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
odb.c
9
odb.c
|
|
@ -1190,3 +1190,12 @@ void odb_reprepare(struct object_database *o)
|
||||||
{
|
{
|
||||||
odb_prepare(o, ODB_PREPARE_FLUSH_CACHES);
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
33
odb.h
33
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)`. */
|
/* Equivalent to `odb_prepare(o, ODB_PREPARE_FLUSH_CACHES)`. */
|
||||||
void odb_reprepare(struct object_database *o);
|
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
|
* Find source by its object directory path. Returns a `NULL` pointer in case
|
||||||
* the source could not be found.
|
* the source could not be found.
|
||||||
|
|
|
||||||
|
|
@ -928,6 +928,18 @@ static int odb_source_files_generate_pack(struct odb_source *source UNUSED,
|
||||||
return 0;
|
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,
|
struct odb_source_files *odb_source_files_new(struct object_database *odb,
|
||||||
const char *path,
|
const char *path,
|
||||||
bool local)
|
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.close = odb_source_files_close;
|
||||||
files->base.create_on_disk = odb_source_files_create_on_disk;
|
files->base.create_on_disk = odb_source_files_create_on_disk;
|
||||||
files->base.prepare = odb_source_files_prepare;
|
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_info = odb_source_files_read_object_info;
|
||||||
files->base.read_object_stream = odb_source_files_read_object_stream;
|
files->base.read_object_stream = odb_source_files_read_object_stream;
|
||||||
files->base.for_each_object = odb_source_files_for_each_object;
|
files->base.for_each_object = odb_source_files_for_each_object;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "git-compat-util.h"
|
#include "git-compat-util.h"
|
||||||
#include "object-file.h"
|
#include "object-file.h"
|
||||||
#include "odb.h"
|
#include "odb.h"
|
||||||
|
#include "fsck.h"
|
||||||
#include "odb/source-inmemory.h"
|
#include "odb/source-inmemory.h"
|
||||||
#include "odb/streaming.h"
|
#include "odb/streaming.h"
|
||||||
#include "oidtree.h"
|
#include "oidtree.h"
|
||||||
|
|
@ -363,6 +364,12 @@ static void odb_source_inmemory_free(struct odb_source *source)
|
||||||
free(inmemory);
|
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 *odb_source_inmemory_new(struct object_database *odb)
|
||||||
{
|
{
|
||||||
struct odb_source_inmemory *source;
|
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.free = odb_source_inmemory_free;
|
||||||
source->base.close = odb_source_inmemory_close;
|
source->base.close = odb_source_inmemory_close;
|
||||||
source->base.prepare = odb_source_inmemory_prepare;
|
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_info = odb_source_inmemory_read_object_info;
|
||||||
source->base.read_object_stream = odb_source_inmemory_read_object_stream;
|
source->base.read_object_stream = odb_source_inmemory_read_object_stream;
|
||||||
source->base.for_each_object = odb_source_inmemory_for_each_object;
|
source->base.for_each_object = odb_source_inmemory_for_each_object;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "odb/streaming.h"
|
#include "odb/streaming.h"
|
||||||
#include "oidtree.h"
|
#include "oidtree.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
|
#include "progress.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
#include "tempfile.h"
|
#include "tempfile.h"
|
||||||
|
|
@ -1032,6 +1033,96 @@ static void odb_source_loose_free(struct odb_source *source)
|
||||||
free(loose);
|
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,
|
struct odb_source_loose *odb_source_loose_new(struct object_database *odb,
|
||||||
const char *path,
|
const char *path,
|
||||||
bool local)
|
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.free = odb_source_loose_free;
|
||||||
loose->base.close = odb_source_loose_close;
|
loose->base.close = odb_source_loose_close;
|
||||||
loose->base.prepare = odb_source_loose_prepare;
|
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_info = odb_source_loose_read_object_info;
|
||||||
loose->base.read_object_stream = odb_source_loose_read_object_stream;
|
loose->base.read_object_stream = odb_source_loose_read_object_stream;
|
||||||
loose->base.for_each_object = odb_source_loose_for_each_object;
|
loose->base.for_each_object = odb_source_loose_for_each_object;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "chdir-notify.h"
|
#include "chdir-notify.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
|
#include "fsck.h"
|
||||||
#include "git-zlib.h"
|
#include "git-zlib.h"
|
||||||
#include "hex.h"
|
#include "hex.h"
|
||||||
#include "list-objects-filter-options.h"
|
#include "list-objects-filter-options.h"
|
||||||
|
|
@ -10,9 +11,13 @@
|
||||||
#include "midx.h"
|
#include "midx.h"
|
||||||
#include "odb/source-packed.h"
|
#include "odb/source-packed.h"
|
||||||
#include "odb/streaming.h"
|
#include "odb/streaming.h"
|
||||||
|
#include "pack.h"
|
||||||
|
#include "pack-revindex.h"
|
||||||
#include "packfile.h"
|
#include "packfile.h"
|
||||||
#include "pack-bitmap.h"
|
#include "pack-bitmap.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
|
#include "progress.h"
|
||||||
|
#include "run-command.h"
|
||||||
|
|
||||||
static int find_pack_entry(struct odb_source_packed *store,
|
static int find_pack_entry(struct odb_source_packed *store,
|
||||||
const struct object_id *oid,
|
const struct object_id *oid,
|
||||||
|
|
@ -849,6 +854,117 @@ static void odb_source_packed_free(struct odb_source *source)
|
||||||
free(packed);
|
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,
|
struct odb_source_packed *odb_source_packed_new(struct object_database *odb,
|
||||||
const char *path,
|
const char *path,
|
||||||
bool local)
|
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.free = odb_source_packed_free;
|
||||||
packed->base.close = odb_source_packed_close;
|
packed->base.close = odb_source_packed_close;
|
||||||
packed->base.prepare = odb_source_packed_prepare;
|
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_info = odb_source_packed_read_object_info;
|
||||||
packed->base.read_object_stream = odb_source_packed_read_object_stream;
|
packed->base.read_object_stream = odb_source_packed_read_object_stream;
|
||||||
packed->base.for_each_object = odb_source_packed_for_each_object;
|
packed->base.for_each_object = odb_source_packed_for_each_object;
|
||||||
|
|
|
||||||
21
odb/source.h
21
odb/source.h
|
|
@ -322,6 +322,17 @@ struct odb_source {
|
||||||
int (*generate_pack)(struct odb_source *source,
|
int (*generate_pack)(struct odb_source *source,
|
||||||
struct odb_pack_generator **out,
|
struct odb_pack_generator **out,
|
||||||
const struct odb_generate_pack_options *opts);
|
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);
|
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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -3410,28 +3410,22 @@ static int verify_bitmap_file(const struct git_hash_algo *algop,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int verify_bitmap_files(struct repository *r)
|
int verify_bitmap_files(struct odb_source_packed *source)
|
||||||
{
|
{
|
||||||
struct odb_source *source;
|
struct packfile_list_entry *e;
|
||||||
struct packed_git *p;
|
struct multi_pack_index *m;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
for (source = r->objects->sources; source; source = source->next) {
|
m = get_multi_pack_index(source);
|
||||||
struct odb_source_files *files = odb_source_files_downcast(source);
|
if (m) {
|
||||||
struct multi_pack_index *m = get_multi_pack_index(files->packed);
|
char *midx_bitmap_name = midx_bitmap_filename(m);
|
||||||
char *midx_bitmap_name;
|
res |= verify_bitmap_file(source->base.odb->repo->hash_algo, midx_bitmap_name);
|
||||||
|
|
||||||
if (!m)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
midx_bitmap_name = midx_bitmap_filename(m);
|
|
||||||
res |= verify_bitmap_file(r->hash_algo, midx_bitmap_name);
|
|
||||||
free(midx_bitmap_name);
|
free(midx_bitmap_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
repo_for_each_pack(r, p) {
|
for (e = packfile_store_get_packs(source); e; e = e->next) {
|
||||||
char *pack_bitmap_name = pack_bitmap_filename(p);
|
char *pack_bitmap_name = pack_bitmap_filename(e->pack);
|
||||||
res |= verify_bitmap_file(r->hash_algo, pack_bitmap_name);
|
res |= verify_bitmap_file(source->base.odb->repo->hash_algo, pack_bitmap_name);
|
||||||
free(pack_bitmap_name);
|
free(pack_bitmap_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 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,
|
struct ewah_bitmap *read_bitmap(const unsigned char *map,
|
||||||
size_t map_size, size_t *map_pos);
|
size_t map_size, size_t *map_pos);
|
||||||
|
|
|
||||||
|
|
@ -844,6 +844,11 @@ test_expect_success 'alternate objects are correctly blamed' '
|
||||||
echo "../../alt.git/objects" >.git/objects/info/alternates &&
|
echo "../../alt.git/objects" >.git/objects/info/alternates &&
|
||||||
mkdir alt.git/objects/$(dirname $path) &&
|
mkdir alt.git/objects/$(dirname $path) &&
|
||||||
>alt.git/objects/$(dirname $path)/$(basename $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_must_fail git fsck >out 2>&1 &&
|
||||||
test_grep alt.git out
|
test_grep alt.git out
|
||||||
'
|
'
|
||||||
|
|
|
||||||
|
|
@ -573,6 +573,19 @@ test_expect_success 'verify incorrect checksum' '
|
||||||
$objdir "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' '
|
test_expect_success 'setup for v1-specific fsck tests' '
|
||||||
git -c midx.version=1 multi-pack-index write
|
git -c midx.version=1 multi-pack-index write
|
||||||
'
|
'
|
||||||
|
|
|
||||||
|
|
@ -204,4 +204,12 @@ test_expect_success 'fsck catches invalid header: hash function' '
|
||||||
"reverse-index file .* has unsupported hash id"
|
"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
|
test_done
|
||||||
|
|
|
||||||
|
|
@ -498,7 +498,15 @@ test_expect_success 'git fsck correctly identifies good and bad bitmaps' '
|
||||||
corrupt_file "$packbitmap" &&
|
corrupt_file "$packbitmap" &&
|
||||||
test_must_fail git fsck 2>err &&
|
test_must_fail git fsck 2>err &&
|
||||||
test_grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" 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' '
|
test_expect_success 'corrupt MIDX with bitmap causes fallback' '
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue