diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 6361b27015..fe74af73af 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -2767,19 +2767,89 @@ static int reftable_be_fsck(struct ref_store *ref_store, struct fsck_options *o, { struct reftable_ref_store *refs = reftable_be_downcast(ref_store, REF_STORE_READ, "fsck"); + struct reftable_ref_iterator *iter = NULL; + struct reftable_ref_record ref = { 0 }; + struct fsck_ref_report report = { 0 }; + struct strbuf refname = STRBUF_INIT; struct reftable_backend *backend; + int ret, errors = 0; if (is_main_worktree(wt)) { backend = &refs->main_backend; } else { - int ret = backend_for_worktree(&backend, refs, wt->id); - if (ret < 0) - return error(_("reftable stack for worktree '%s' is broken"), - wt->id); + ret = backend_for_worktree(&backend, refs, wt->id); + if (ret < 0) { + ret = error(_("reftable stack for worktree '%s' is broken"), + wt->id); + goto out; + } } - return reftable_fsck_check(backend->stack, reftable_fsck_error_handler, - reftable_fsck_verbose_handler, o); + errors |= reftable_fsck_check(backend->stack, reftable_fsck_error_handler, + reftable_fsck_verbose_handler, o); + + iter = ref_iterator_for_stack(refs, backend->stack, "", NULL, 0); + if (!iter) { + ret = error(_("could not create iterator for worktree '%s'"), wt->id); + goto out; + } + + while (1) { + ret = reftable_iterator_next_ref(&iter->iter, &ref); + if (ret > 0) + break; + if (ret < 0) { + ret = error(_("could not read record for worktree '%s'"), wt->id); + goto out; + } + + strbuf_reset(&refname); + if (!is_main_worktree(wt)) + strbuf_addf(&refname, "worktrees/%s/", wt->id); + strbuf_addstr(&refname, ref.refname); + report.path = refname.buf; + + switch (ref.value_type) { + case REFTABLE_REF_VAL1: + case REFTABLE_REF_VAL2: { + struct object_id oid; + unsigned hash_id; + + switch (reftable_stack_hash_id(backend->stack)) { + case REFTABLE_HASH_SHA1: + hash_id = GIT_HASH_SHA1; + break; + case REFTABLE_HASH_SHA256: + hash_id = GIT_HASH_SHA256; + break; + default: + BUG("unhandled hash ID %d", + reftable_stack_hash_id(backend->stack)); + } + + oidread(&oid, reftable_ref_record_val1(&ref), + &hash_algos[hash_id]); + + errors |= refs_fsck_ref(ref_store, o, &report, ref.refname, &oid); + break; + } + case REFTABLE_REF_SYMREF: + errors |= refs_fsck_symref(ref_store, o, &report, ref.refname, + ref.value.symref); + break; + default: + BUG("unhandled reference value type %d", ref.value_type); + } + } + + ret = errors ? -1 : 0; + +out: + if (iter) + ref_iterator_free(&iter->base); + reftable_ref_record_release(&ref); + strbuf_release(&refname); + return ret; } struct ref_storage_be refs_be_reftable = { diff --git a/t/t0614-reftable-fsck.sh b/t/t0614-reftable-fsck.sh index 4757eb5931..d24b87f961 100755 --- a/t/t0614-reftable-fsck.sh +++ b/t/t0614-reftable-fsck.sh @@ -87,4 +87,16 @@ test_expect_success 'worktree stacks can be verified' ' done ' +test_expect_success 'invalid symref gets reported' ' + test_when_finished "rm -rf repo" && + git init repo && + test_commit -C repo initial && + git -C repo symbolic-ref refs/heads/symref garbage && + test_must_fail git -C repo refs verify 2>err && + cat >expect <<-EOF && + error: refs/heads/symref: badReferentName: points to invalid refname ${SQ}garbage${SQ} + EOF + test_cmp expect err +' + test_done