Skip to content

Commit 31deb28

Browse files
avargitster
authored andcommitted
fsck: don't hard die on invalid object types
Change the error fsck emits on invalid object types, such as: $ git hash-object --stdin -w -t garbage --literally </dev/null <OID> From the very ungraceful error of: $ git fsck fatal: invalid object type $ To: $ git fsck error: <OID>: object is of unknown type 'garbage': <OID_PATH> [ other fsck output ] We'll still exit with non-zero, but now we'll finish the rest of the traversal. The tests that's being added here asserts that we'll still complain about other fsck issues (e.g. an unrelated dangling blob). To do this we need to pass down the "OBJECT_INFO_ALLOW_UNKNOWN_TYPE" flag from read_loose_object() through to parse_loose_header(). Since the read_loose_object() function is only used in builtin/fsck.c we can simply change it to accept a "struct object_info" (which contains the OBJECT_INFO_ALLOW_UNKNOWN_TYPE in its flags). See f6371f9 (sha1_file: add read_loose_object() function, 2017-01-13) for the introduction of read_loose_object(). Since we'll need a "struct strbuf" to hold the "type_name" let's pass it to the for_each_loose_file_in_objdir() callback to avoid allocating a new one for each loose object in the iteration. It also makes the memory management simpler than sticking it in fsck_loose() itself, as we'll only need to strbuf_reset() it, with no need to do a strbuf_release() before each "return". Before this commit we'd never check the "type" if read_loose_object() failed, but now we do. We therefore need to initialize it to OBJ_NONE to be able to tell the difference between e.g. its unpack_loose_header() having failed, and us getting past that and into parse_loose_header(). Signed-off-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent dccb32b commit 31deb28

File tree

4 files changed

+49
-30
lines changed

4 files changed

+49
-30
lines changed

builtin/fsck.c

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -592,18 +592,36 @@ static void get_default_heads(void)
592592
}
593593
}
594594

595+
struct for_each_loose_cb
596+
{
597+
struct progress *progress;
598+
struct strbuf obj_type;
599+
};
600+
595601
static int fsck_loose(const struct object_id *oid, const char *path, void *data)
596602
{
603+
struct for_each_loose_cb *cb_data = data;
597604
struct object *obj;
598-
enum object_type type;
605+
enum object_type type = OBJ_NONE;
599606
unsigned long size;
600607
void *contents;
601608
int eaten;
609+
struct object_info oi = OBJECT_INFO_INIT;
610+
int err = 0;
602611

603-
if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
612+
strbuf_reset(&cb_data->obj_type);
613+
oi.type_name = &cb_data->obj_type;
614+
oi.sizep = &size;
615+
oi.typep = &type;
616+
617+
if (read_loose_object(path, oid, &contents, &oi) < 0)
618+
err = error(_("%s: object corrupt or missing: %s"),
619+
oid_to_hex(oid), path);
620+
if (type != OBJ_NONE && type < 0)
621+
err = error(_("%s: object is of unknown type '%s': %s"),
622+
oid_to_hex(oid), cb_data->obj_type.buf, path);
623+
if (err < 0) {
604624
errors_found |= ERROR_OBJECT;
605-
error(_("%s: object corrupt or missing: %s"),
606-
oid_to_hex(oid), path);
607625
return 0; /* keep checking other objects */
608626
}
609627

@@ -639,15 +657,21 @@ static int fsck_cruft(const char *basename, const char *path, void *data)
639657
return 0;
640658
}
641659

642-
static int fsck_subdir(unsigned int nr, const char *path, void *progress)
660+
static int fsck_subdir(unsigned int nr, const char *path, void *data)
643661
{
662+
struct for_each_loose_cb *cb_data = data;
663+
struct progress *progress = cb_data->progress;
644664
display_progress(progress, nr + 1);
645665
return 0;
646666
}
647667

648668
static void fsck_object_dir(const char *path)
649669
{
650670
struct progress *progress = NULL;
671+
struct for_each_loose_cb cb_data = {
672+
.obj_type = STRBUF_INIT,
673+
.progress = progress,
674+
};
651675

652676
if (verbose)
653677
fprintf_ln(stderr, _("Checking object directory"));
@@ -656,9 +680,10 @@ static void fsck_object_dir(const char *path)
656680
progress = start_progress(_("Checking object directories"), 256);
657681

658682
for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
659-
progress);
683+
&cb_data);
660684
display_progress(progress, 256);
661685
stop_progress(&progress);
686+
strbuf_release(&cb_data.obj_type);
662687
}
663688

664689
static int fsck_head_link(const char *head_ref_name,

object-file.c

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2520,18 +2520,15 @@ static int check_stream_oid(git_zstream *stream,
25202520

25212521
int read_loose_object(const char *path,
25222522
const struct object_id *expected_oid,
2523-
enum object_type *type,
2524-
unsigned long *size,
2525-
void **contents)
2523+
void **contents,
2524+
struct object_info *oi)
25262525
{
25272526
int ret = -1;
25282527
void *map = NULL;
25292528
unsigned long mapsize;
25302529
git_zstream stream;
25312530
char hdr[MAX_HEADER_LEN];
2532-
struct object_info oi = OBJECT_INFO_INIT;
2533-
oi.typep = type;
2534-
oi.sizep = size;
2531+
unsigned long *size = oi->sizep;
25352532

25362533
*contents = NULL;
25372534

@@ -2547,15 +2544,13 @@ int read_loose_object(const char *path,
25472544
goto out;
25482545
}
25492546

2550-
if (parse_loose_header(hdr, &oi) < 0) {
2547+
if (parse_loose_header(hdr, oi) < 0) {
25512548
error(_("unable to parse header of %s"), path);
25522549
git_inflate_end(&stream);
25532550
goto out;
25542551
}
2555-
if (*type < 0)
2556-
die(_("invalid object type"));
25572552

2558-
if (*type == OBJ_BLOB && *size > big_file_threshold) {
2553+
if (*oi->typep == OBJ_BLOB && *size > big_file_threshold) {
25592554
if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0)
25602555
goto out;
25612556
} else {
@@ -2566,8 +2561,7 @@ int read_loose_object(const char *path,
25662561
goto out;
25672562
}
25682563
if (check_object_signature(the_repository, expected_oid,
2569-
*contents, *size,
2570-
type_name(*type))) {
2564+
*contents, *size, oi->type_name->buf)) {
25712565
error(_("hash mismatch for %s (expected %s)"), path,
25722566
oid_to_hex(expected_oid));
25732567
free(*contents);

object-store.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,16 +236,16 @@ int force_object_loose(const struct object_id *oid, time_t mtime);
236236

237237
/*
238238
* Open the loose object at path, check its hash, and return the contents,
239+
* use the "oi" argument to assert things about the object, or e.g. populate its
239240
* type, and size. If the object is a blob, then "contents" may return NULL,
240241
* to allow streaming of large blobs.
241242
*
242243
* Returns 0 on success, negative on error (details may be written to stderr).
243244
*/
244245
int read_loose_object(const char *path,
245246
const struct object_id *expected_oid,
246-
enum object_type *type,
247-
unsigned long *size,
248-
void **contents);
247+
void **contents,
248+
struct object_info *oi);
249249

250250
/* Retry packed storage after checking packed and loose storage */
251251
#define HAS_OBJECT_RECHECK_PACKED 1

t/t1450-fsck.sh

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,10 @@ test_expect_success 'object with hash and type mismatch' '
8585
cmt=$(echo bogus | git commit-tree $tree) &&
8686
git update-ref refs/heads/bogus $cmt &&
8787
88-
cat >expect <<-\EOF &&
89-
fatal: invalid object type
90-
EOF
91-
test_must_fail git fsck 2>actual &&
92-
test_cmp expect actual
88+
89+
test_must_fail git fsck 2>out &&
90+
grep "^error: hash mismatch for " out &&
91+
grep "^error: $oid: object is of unknown type '"'"'garbage'"'"'" out
9392
)
9493
'
9594

@@ -910,19 +909,20 @@ test_expect_success 'detect corrupt index file in fsck' '
910909
test_i18ngrep "bad index file" errors
911910
'
912911

913-
test_expect_success 'fsck hard errors on an invalid object type' '
912+
test_expect_success 'fsck error and recovery on invalid object type' '
914913
git init --bare garbage-type &&
915914
(
916915
cd garbage-type &&
917916
918-
git hash-object --stdin -w -t garbage --literally </dev/null &&
917+
garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
919918
920919
cat >err.expect <<-\EOF &&
921920
fatal: invalid object type
922921
EOF
923922
test_must_fail git fsck >out 2>err &&
924-
test_cmp err.expect err &&
925-
test_must_be_empty out
923+
grep -e "^error" -e "^fatal" err >errors &&
924+
test_line_count = 1 errors &&
925+
grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err
926926
)
927927
'
928928

0 commit comments

Comments
 (0)