@@ -19,6 +19,9 @@
`badParentSha1`::
(ERROR) A commit object has a bad parent sha1.
+`badRefContent`::
+ (ERROR) A ref has bad content.
+
`badRefFiletype`::
(ERROR) A ref has a bad file type.
@@ -31,6 +31,7 @@ enum fsck_msg_type {
FUNC(BAD_NAME, ERROR) \
FUNC(BAD_OBJECT_SHA1, ERROR) \
FUNC(BAD_PARENT_SHA1, ERROR) \
+ FUNC(BAD_REF_CONTENT, ERROR) \
FUNC(BAD_REF_FILETYPE, ERROR) \
FUNC(BAD_REF_NAME, ERROR) \
FUNC(BAD_TIMEZONE, ERROR) \
@@ -3504,6 +3504,50 @@ typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
const char *refs_check_dir,
struct dir_iterator *iter);
+static int files_fsck_refs_content(struct ref_store *ref_store,
+ struct fsck_options *o,
+ const char *refs_check_dir,
+ struct dir_iterator *iter)
+{
+ struct strbuf ref_content = STRBUF_INIT;
+ struct strbuf referent = STRBUF_INIT;
+ struct strbuf refname = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+ unsigned int type = 0;
+ int failure_errno = 0;
+ struct object_id oid;
+ int ret = 0;
+
+ strbuf_addf(&refname, "%s/%s", refs_check_dir, iter->relative_path);
+ report.path = refname.buf;
+
+ if (S_ISLNK(iter->st.st_mode))
+ goto cleanup;
+
+ if (strbuf_read_file(&ref_content, iter->path.buf, 0) < 0) {
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_CONTENT,
+ "cannot read ref file");
+ goto cleanup;
+ }
+
+ if (parse_loose_ref_contents(ref_store->repo->hash_algo,
+ ref_content.buf, &oid, &referent,
+ &type, &failure_errno)) {
+ strbuf_rtrim(&ref_content);
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_CONTENT,
+ "%s", ref_content.buf);
+ goto cleanup;
+ }
+
+cleanup:
+ strbuf_release(&refname);
+ strbuf_release(&ref_content);
+ strbuf_release(&referent);
+ return ret;
+}
+
static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
struct fsck_options *o,
const char *refs_check_dir,
@@ -3586,6 +3630,7 @@ static int files_fsck_refs(struct ref_store *ref_store,
{
files_fsck_refs_fn fsck_refs_fn[]= {
files_fsck_refs_name,
+ files_fsck_refs_content,
NULL,
};
@@ -148,4 +148,70 @@ test_expect_success 'ref name check should work for multiple worktrees' '
)
'
+test_expect_success 'regular ref content should be checked (individual)' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ branch_dir_prefix=.git/refs/heads &&
+ tag_dir_prefix=.git/refs/tags &&
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ git refs verify 2>err &&
+ test_must_be_empty err &&
+
+ bad_content=$(git rev-parse main)x &&
+ printf "%s" $bad_content >$tag_dir_prefix/tag-bad-1 &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/tags/tag-bad-1: badRefContent: $bad_content
+ EOF
+ rm $tag_dir_prefix/tag-bad-1 &&
+ test_cmp expect err &&
+
+ bad_content=xfsazqfxcadas &&
+ printf "%s" $bad_content >$tag_dir_prefix/tag-bad-2 &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/tags/tag-bad-2: badRefContent: $bad_content
+ EOF
+ rm $tag_dir_prefix/tag-bad-2 &&
+ test_cmp expect err &&
+
+ bad_content=Xfsazqfxcadas &&
+ printf "%s" $bad_content >$branch_dir_prefix/a/b/branch-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/a/b/branch-bad: badRefContent: $bad_content
+ EOF
+ rm $branch_dir_prefix/a/b/branch-bad &&
+ test_cmp expect err
+'
+
+test_expect_success 'regular ref content should be checked (aggregate)' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ branch_dir_prefix=.git/refs/heads &&
+ tag_dir_prefix=.git/refs/tags &&
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ bad_content_1=$(git rev-parse main)x &&
+ bad_content_2=xfsazqfxcadas &&
+ bad_content_3=Xfsazqfxcadas &&
+ printf "%s" $bad_content_1 >$tag_dir_prefix/tag-bad-1 &&
+ printf "%s" $bad_content_2 >$tag_dir_prefix/tag-bad-2 &&
+ printf "%s" $bad_content_3 >$branch_dir_prefix/a/b/branch-bad &&
+
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/a/b/branch-bad: badRefContent: $bad_content_3
+ error: refs/tags/tag-bad-1: badRefContent: $bad_content_1
+ error: refs/tags/tag-bad-2: badRefContent: $bad_content_2
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err
+'
+
test_done
"git-fsck(1)" has some consistency checks for regular refs. As we want to align the checks "git refs verify" performs with them (and eventually call the unified code that checks refs from both), port the logic "git-fsck" has to "git refs verify". "git-fsck(1)" will report an error when the ref content is invalid. Following this, add a similar check to "git refs verify". Then add a new fsck error message "badRefContent(ERROR)" to represent that a ref has an invalid content. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: shejialuo <shejialuo@gmail.com> --- Documentation/fsck-msgids.txt | 3 ++ fsck.h | 1 + refs/files-backend.c | 45 ++++++++++++++++++++++++ t/t0602-reffiles-fsck.sh | 66 +++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+)