Merge branch 'sj/ref-fsck'

"git fsck" infrastructure has been taught to also check the sanity
of the ref database, in addition to the object database.

* sj/ref-fsck:
  fsck: add ref name check for files backend
  files-backend: add unified interface for refs scanning
  builtin/refs: add verify subcommand
  refs: set up ref consistency check infrastructure
  fsck: add refs report function
  fsck: add a unified interface for reporting fsck messages
  fsck: make "fsck_error" callback generic
  fsck: rename objects-related fsck error functions
  fsck: rename "skiplist" to "skip_oids"
This commit is contained in:
Junio C Hamano
2024-08-16 12:51:51 -07:00
16 changed files with 477 additions and 59 deletions

View File

@ -419,6 +419,15 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
return res;
}
static int debug_fsck(struct ref_store *ref_store,
struct fsck_options *o)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
int res = drefs->refs->be->fsck(drefs->refs, o);
trace_printf_key(&trace_refs, "fsck: %d\n", res);
return res;
}
struct ref_storage_be refs_be_debug = {
.name = "debug",
.init = NULL,
@ -451,4 +460,6 @@ struct ref_storage_be refs_be_debug = {
.create_reflog = debug_create_reflog,
.delete_reflog = debug_delete_reflog,
.reflog_expire = debug_reflog_expire,
.fsck = debug_fsck,
};

View File

@ -4,6 +4,7 @@
#include "../gettext.h"
#include "../hash.h"
#include "../hex.h"
#include "../fsck.h"
#include "../refs.h"
#include "refs-internal.h"
#include "ref-cache.h"
@ -3419,6 +3420,116 @@ static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
return ret;
}
/*
* For refs and reflogs, they share a unified interface when scanning
* the whole directory. This function is used as the callback for each
* regular file or symlink in the directory.
*/
typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
struct fsck_options *o,
const char *refs_check_dir,
struct dir_iterator *iter);
static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
struct fsck_options *o,
const char *refs_check_dir,
struct dir_iterator *iter)
{
struct strbuf sb = STRBUF_INIT;
int ret = 0;
/*
* Ignore the files ending with ".lock" as they may be lock files
* However, do not allow bare ".lock" files.
*/
if (iter->basename[0] != '.' && ends_with(iter->basename, ".lock"))
goto cleanup;
if (check_refname_format(iter->basename, REFNAME_ALLOW_ONELEVEL)) {
struct fsck_ref_report report = { .path = NULL };
strbuf_addf(&sb, "%s/%s", refs_check_dir, iter->relative_path);
report.path = sb.buf;
ret = fsck_report_ref(o, &report,
FSCK_MSG_BAD_REF_NAME,
"invalid refname format");
}
cleanup:
strbuf_release(&sb);
return ret;
}
static int files_fsck_refs_dir(struct ref_store *ref_store,
struct fsck_options *o,
const char *refs_check_dir,
files_fsck_refs_fn *fsck_refs_fn)
{
struct strbuf sb = STRBUF_INIT;
struct dir_iterator *iter;
int iter_status;
int ret = 0;
strbuf_addf(&sb, "%s/%s", ref_store->gitdir, refs_check_dir);
iter = dir_iterator_begin(sb.buf, 0);
if (!iter) {
ret = error_errno(_("cannot open directory %s"), sb.buf);
goto out;
}
while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
if (S_ISDIR(iter->st.st_mode)) {
continue;
} else if (S_ISREG(iter->st.st_mode) ||
S_ISLNK(iter->st.st_mode)) {
if (o->verbose)
fprintf_ln(stderr, "Checking %s/%s",
refs_check_dir, iter->relative_path);
for (size_t i = 0; fsck_refs_fn[i]; i++) {
if (fsck_refs_fn[i](ref_store, o, refs_check_dir, iter))
ret = -1;
}
} else {
struct fsck_ref_report report = { .path = iter->basename };
if (fsck_report_ref(o, &report,
FSCK_MSG_BAD_REF_FILETYPE,
"unexpected file type"))
ret = -1;
}
}
if (iter_status != ITER_DONE)
ret = error(_("failed to iterate over '%s'"), sb.buf);
out:
strbuf_release(&sb);
return ret;
}
static int files_fsck_refs(struct ref_store *ref_store,
struct fsck_options *o)
{
files_fsck_refs_fn fsck_refs_fn[]= {
files_fsck_refs_name,
NULL,
};
if (o->verbose)
fprintf_ln(stderr, _("Checking references consistency"));
return files_fsck_refs_dir(ref_store, o, "refs", fsck_refs_fn);
}
static int files_fsck(struct ref_store *ref_store,
struct fsck_options *o)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "fsck");
return files_fsck_refs(ref_store, o) |
refs->packed_ref_store->be->fsck(refs->packed_ref_store, o);
}
struct ref_storage_be refs_be_files = {
.name = "files",
.init = files_ref_store_init,
@ -3445,5 +3556,7 @@ struct ref_storage_be refs_be_files = {
.reflog_exists = files_reflog_exists,
.create_reflog = files_create_reflog,
.delete_reflog = files_delete_reflog,
.reflog_expire = files_reflog_expire
.reflog_expire = files_reflog_expire,
.fsck = files_fsck,
};

View File

@ -1733,6 +1733,12 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
return empty_ref_iterator_begin();
}
static int packed_fsck(struct ref_store *ref_store,
struct fsck_options *o)
{
return 0;
}
struct ref_storage_be refs_be_packed = {
.name = "packed",
.init = packed_ref_store_init,
@ -1760,4 +1766,6 @@ struct ref_storage_be refs_be_packed = {
.create_reflog = NULL,
.delete_reflog = NULL,
.reflog_expire = NULL,
.fsck = packed_fsck,
};

View File

@ -4,6 +4,7 @@
#include "refs.h"
#include "iterator.h"
struct fsck_options;
struct ref_transaction;
/*
@ -651,6 +652,9 @@ typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname,
typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refname,
struct strbuf *referent);
typedef int fsck_fn(struct ref_store *ref_store,
struct fsck_options *o);
struct ref_storage_be {
const char *name;
ref_store_init_fn *init;
@ -678,6 +682,8 @@ struct ref_storage_be {
create_reflog_fn *create_reflog;
delete_reflog_fn *delete_reflog;
reflog_expire_fn *reflog_expire;
fsck_fn *fsck;
};
extern struct ref_storage_be refs_be_files;

View File

@ -2309,6 +2309,12 @@ done:
return ret;
}
static int reftable_be_fsck(struct ref_store *ref_store,
struct fsck_options *o)
{
return 0;
}
struct ref_storage_be refs_be_reftable = {
.name = "reftable",
.init = reftable_be_init,
@ -2336,4 +2342,6 @@ struct ref_storage_be refs_be_reftable = {
.create_reflog = reftable_be_create_reflog,
.delete_reflog = reftable_be_delete_reflog,
.reflog_expire = reftable_be_reflog_expire,
.fsck = reftable_be_fsck,
};