Skip to content

Commit cd34442

Browse files
shejialuogitster
authored andcommitted
packed-backend: add "packed-refs" header consistency check
In "packed-backend.c::create_snapshot", if there is a header (the line which starts with '#'), we will check whether the line starts with "# pack-refs with:". Before we port this check into "packed_fsck", let's fix "create_snapshot" to check the prefix "# packed-ref with: " instead of "# packed-ref with:" due to that we will always write a single trailing space after the colon. However, we need to consider other situations and discuss whether we need to add checks. 1. If the header does not exist, we should not report an error to the user. This is because in older Git version, we never write header in the "packed-refs" file. Also, we do allow no header in "packed-refs" in runtime. 2. If the header content does not start with "# packed-ref with: ", we should report an error just like what "create_snapshot" does. So, create a new fsck message "badPackedRefHeader(ERROR)" for this. 3. If the header content is not the same as the constant string "PACKED_REFS_HEADER". This is expected because we make it extensible intentionally. So, there is no need to report. As we have analyzed, we only need to check the case 2 in the above. In order to do this, read the "packed-refs" file via "strbuf_read". Like what "create_snapshot" and other functions do, we could split the line by finding the next newline in the buffer. When we cannot find a newline, we could report an error. So, create a function "packed_fsck_ref_next_line" to find the next newline and if there is no such newline, use "packedRefEntryNotTerminated(ERROR)" to report an error to the user. Then, parse the first line to apply the checks. Update the test to exercise the code. Mentored-by: Patrick Steinhardt <[email protected]> Mentored-by: Karthik Nayak <[email protected]> Signed-off-by: shejialuo <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b0f09d5 commit cd34442

File tree

4 files changed

+136
-1
lines changed

4 files changed

+136
-1
lines changed

Documentation/fsck-msgids.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
`badObjectSha1`::
1717
(ERROR) An object has a bad sha1.
1818

19+
`badPackedRefHeader`::
20+
(ERROR) The "packed-refs" file contains an invalid
21+
header.
22+
1923
`badParentSha1`::
2024
(ERROR) A commit object has a bad parent sha1.
2125

@@ -176,6 +180,10 @@
176180
`nullSha1`::
177181
(WARN) Tree contains entries pointing to a null sha1.
178182

183+
`packedRefEntryNotTerminated`::
184+
(ERROR) The "packed-refs" file contains an entry that is
185+
not terminated by a newline.
186+
179187
`refMissingNewline`::
180188
(INFO) A loose ref that does not end with newline(LF). As
181189
valid implementations of Git never created such a loose ref

fsck.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ enum fsck_msg_type {
3030
FUNC(BAD_EMAIL, ERROR) \
3131
FUNC(BAD_NAME, ERROR) \
3232
FUNC(BAD_OBJECT_SHA1, ERROR) \
33+
FUNC(BAD_PACKED_REF_HEADER, ERROR) \
3334
FUNC(BAD_PARENT_SHA1, ERROR) \
3435
FUNC(BAD_REF_CONTENT, ERROR) \
3536
FUNC(BAD_REF_FILETYPE, ERROR) \
@@ -53,6 +54,7 @@ enum fsck_msg_type {
5354
FUNC(MISSING_TYPE, ERROR) \
5455
FUNC(MISSING_TYPE_ENTRY, ERROR) \
5556
FUNC(MULTIPLE_AUTHORS, ERROR) \
57+
FUNC(PACKED_REF_ENTRY_NOT_TERMINATED, ERROR) \
5658
FUNC(TREE_NOT_SORTED, ERROR) \
5759
FUNC(UNKNOWN_TYPE, ERROR) \
5860
FUNC(ZERO_PADDED_DATE, ERROR) \

refs/packed-backend.c

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
694694

695695
tmp = xmemdupz(snapshot->buf, eol - snapshot->buf);
696696

697-
if (!skip_prefix(tmp, "# pack-refs with:", (const char **)&p))
697+
if (!skip_prefix(tmp, "# pack-refs with: ", (const char **)&p))
698698
die_invalid_line(refs->path,
699699
snapshot->buf,
700700
snapshot->eof - snapshot->buf);
@@ -1749,12 +1749,76 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
17491749
return empty_ref_iterator_begin();
17501750
}
17511751

1752+
static int packed_fsck_ref_next_line(struct fsck_options *o,
1753+
unsigned long line_number, const char *start,
1754+
const char *eof, const char **eol)
1755+
{
1756+
int ret = 0;
1757+
1758+
*eol = memchr(start, '\n', eof - start);
1759+
if (!*eol) {
1760+
struct strbuf packed_entry = STRBUF_INIT;
1761+
struct fsck_ref_report report = { 0 };
1762+
1763+
strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
1764+
report.path = packed_entry.buf;
1765+
ret = fsck_report_ref(o, &report,
1766+
FSCK_MSG_PACKED_REF_ENTRY_NOT_TERMINATED,
1767+
"'%.*s' is not terminated with a newline",
1768+
(int)(eof - start), start);
1769+
1770+
/*
1771+
* There is no newline but we still want to parse it to the end of
1772+
* the buffer.
1773+
*/
1774+
*eol = eof;
1775+
strbuf_release(&packed_entry);
1776+
}
1777+
1778+
return ret;
1779+
}
1780+
1781+
static int packed_fsck_ref_header(struct fsck_options *o,
1782+
const char *start, const char *eol)
1783+
{
1784+
if (!starts_with(start, "# pack-refs with: ")) {
1785+
struct fsck_ref_report report = { 0 };
1786+
report.path = "packed-refs.header";
1787+
1788+
return fsck_report_ref(o, &report,
1789+
FSCK_MSG_BAD_PACKED_REF_HEADER,
1790+
"'%.*s' does not start with '# pack-refs with: '",
1791+
(int)(eol - start), start);
1792+
}
1793+
1794+
return 0;
1795+
}
1796+
1797+
static int packed_fsck_ref_content(struct fsck_options *o,
1798+
const char *start, const char *eof)
1799+
{
1800+
unsigned long line_number = 1;
1801+
const char *eol;
1802+
int ret = 0;
1803+
1804+
ret |= packed_fsck_ref_next_line(o, line_number, start, eof, &eol);
1805+
if (*start == '#') {
1806+
ret |= packed_fsck_ref_header(o, start, eol);
1807+
1808+
start = eol + 1;
1809+
line_number++;
1810+
}
1811+
1812+
return ret;
1813+
}
1814+
17521815
static int packed_fsck(struct ref_store *ref_store,
17531816
struct fsck_options *o,
17541817
struct worktree *wt)
17551818
{
17561819
struct packed_ref_store *refs = packed_downcast(ref_store,
17571820
REF_STORE_READ, "fsck");
1821+
struct strbuf packed_ref_content = STRBUF_INIT;
17581822
int ret = 0;
17591823
int fd;
17601824

@@ -1786,7 +1850,16 @@ static int packed_fsck(struct ref_store *ref_store,
17861850
goto cleanup;
17871851
}
17881852

1853+
if (strbuf_read(&packed_ref_content, fd, 0) < 0) {
1854+
ret = error_errno(_("unable to read %s"), refs->path);
1855+
goto cleanup;
1856+
}
1857+
1858+
ret = packed_fsck_ref_content(o, packed_ref_content.buf,
1859+
packed_ref_content.buf + packed_ref_content.len);
1860+
17891861
cleanup:
1862+
strbuf_release(&packed_ref_content);
17901863
return ret;
17911864
}
17921865

t/t0602-reffiles-fsck.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,4 +639,56 @@ test_expect_success SYMLINKS 'the filetype of packed-refs should be checked' '
639639
)
640640
'
641641

642+
test_expect_success 'packed-refs header should be checked' '
643+
test_when_finished "rm -rf repo" &&
644+
git init repo &&
645+
(
646+
cd repo &&
647+
test_commit default &&
648+
649+
git refs verify 2>err &&
650+
test_must_be_empty err &&
651+
652+
for bad_header in "# pack-refs wit: peeled fully-peeled sorted " \
653+
"# pack-refs with traits: peeled fully-peeled sorted " \
654+
"# pack-refs with a: peeled fully-peeled" \
655+
"# pack-refs with:peeled fully-peeled sorted"
656+
do
657+
printf "%s\n" "$bad_header" >.git/packed-refs &&
658+
test_must_fail git refs verify 2>err &&
659+
cat >expect <<-EOF &&
660+
error: packed-refs.header: badPackedRefHeader: '\''$bad_header'\'' does not start with '\''# pack-refs with: '\''
661+
EOF
662+
rm .git/packed-refs &&
663+
test_cmp expect err || return 1
664+
done
665+
)
666+
'
667+
668+
test_expect_success 'packed-refs missing header should not be reported' '
669+
test_when_finished "rm -rf repo" &&
670+
git init repo &&
671+
(
672+
cd repo &&
673+
test_commit default &&
674+
675+
printf "$(git rev-parse HEAD) refs/heads/main\n" >.git/packed-refs &&
676+
git refs verify 2>err &&
677+
test_must_be_empty err
678+
)
679+
'
680+
681+
test_expect_success 'packed-refs unknown traits should not be reported' '
682+
test_when_finished "rm -rf repo" &&
683+
git init repo &&
684+
(
685+
cd repo &&
686+
test_commit default &&
687+
688+
printf "# pack-refs with: peeled fully-peeled sorted foo\n" >.git/packed-refs &&
689+
git refs verify 2>err &&
690+
test_must_be_empty err
691+
)
692+
'
693+
642694
test_done

0 commit comments

Comments
 (0)