Skip to content

Commit 8354fa3

Browse files
novalisgitster
authored andcommitted
fsck: handle bad trees like other errors
Instead of dying when fsck hits a malformed tree object, log the error like any other and continue. Now fsck can tell the user which tree is bad, too. Signed-off-by: David Turner <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2edffef commit 8354fa3

File tree

4 files changed

+106
-19
lines changed

4 files changed

+106
-19
lines changed

fsck.c

+12-6
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,9 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
347347
return -1;
348348

349349
name = get_object_name(options, &tree->object);
350-
init_tree_desc(&desc, tree->buffer, tree->size);
351-
while (tree_entry(&desc, &entry)) {
350+
if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
351+
return -1;
352+
while (tree_entry_gently(&desc, &entry)) {
352353
struct object *obj;
353354
int result;
354355

@@ -520,7 +521,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con
520521

521522
static int fsck_tree(struct tree *item, struct fsck_options *options)
522523
{
523-
int retval;
524+
int retval = 0;
524525
int has_null_sha1 = 0;
525526
int has_full_path = 0;
526527
int has_empty_name = 0;
@@ -535,7 +536,10 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
535536
unsigned o_mode;
536537
const char *o_name;
537538

538-
init_tree_desc(&desc, item->buffer, item->size);
539+
if (init_tree_desc_gently(&desc, item->buffer, item->size)) {
540+
retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
541+
return retval;
542+
}
539543

540544
o_mode = 0;
541545
o_name = NULL;
@@ -556,7 +560,10 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
556560
is_hfs_dotgit(name) ||
557561
is_ntfs_dotgit(name));
558562
has_zero_pad |= *(char *)desc.buffer == '0';
559-
update_tree_entry(&desc);
563+
if (update_tree_entry_gently(&desc)) {
564+
retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
565+
break;
566+
}
560567

561568
switch (mode) {
562569
/*
@@ -597,7 +604,6 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
597604
o_name = name;
598605
}
599606

600-
retval = 0;
601607
if (has_null_sha1)
602608
retval += report(options, &item->object, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
603609
if (has_full_path)

t/t1450-fsck.sh

+14-2
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,7 @@ test_expect_success 'commit with NUL in header' '
188188
grep "error in commit $new.*unterminated header: NUL at offset" out
189189
'
190190

191-
test_expect_success 'malformatted tree object' '
192-
test_when_finished "git update-ref -d refs/tags/wrong" &&
191+
test_expect_success 'tree object with duplicate entries' '
193192
test_when_finished "remove_object \$T" &&
194193
T=$(
195194
GIT_INDEX_FILE=test-index &&
@@ -208,6 +207,19 @@ test_expect_success 'malformatted tree object' '
208207
grep "error in tree .*contains duplicate file entries" out
209208
'
210209

210+
test_expect_success 'unparseable tree object' '
211+
test_when_finished "git update-ref -d refs/heads/wrong" &&
212+
test_when_finished "remove_object \$tree_sha1" &&
213+
test_when_finished "remove_object \$commit_sha1" &&
214+
tree_sha1=$(printf "100644 \0twenty-bytes-of-junk" | git hash-object -t tree --stdin -w --literally) &&
215+
commit_sha1=$(git commit-tree $tree_sha1) &&
216+
git update-ref refs/heads/wrong $commit_sha1 &&
217+
test_must_fail git fsck 2>out &&
218+
test_i18ngrep "error: empty filename in tree entry" out &&
219+
test_i18ngrep "$tree_sha1" out &&
220+
test_i18ngrep ! "fatal: empty filename in tree entry" out
221+
'
222+
211223
test_expect_success 'tag pointing to nonexistent' '
212224
cat >invalid-tag <<-\EOF &&
213225
object ffffffffffffffffffffffffffffffffffffffff

tree-walk.c

+72-11
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,60 @@ static const char *get_mode(const char *str, unsigned int *modep)
2222
return str;
2323
}
2424

25-
static void decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size)
25+
static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
2626
{
2727
const char *path;
2828
unsigned int mode, len;
2929

30-
if (size < 23 || buf[size - 21])
31-
die(_("too-short tree object"));
30+
if (size < 23 || buf[size - 21]) {
31+
strbuf_addstr(err, _("too-short tree object"));
32+
return -1;
33+
}
3234

3335
path = get_mode(buf, &mode);
34-
if (!path)
35-
die(_("malformed mode in tree entry for tree"));
36-
if (!*path)
37-
die(_("empty filename in tree entry for tree"));
36+
if (!path) {
37+
strbuf_addstr(err, _("malformed mode in tree entry"));
38+
return -1;
39+
}
40+
if (!*path) {
41+
strbuf_addstr(err, _("empty filename in tree entry"));
42+
return -1;
43+
}
3844
len = strlen(path) + 1;
3945

4046
/* Initialize the descriptor entry */
4147
desc->entry.path = path;
4248
desc->entry.mode = canon_mode(mode);
4349
desc->entry.oid = (const struct object_id *)(path + len);
50+
51+
return 0;
4452
}
4553

46-
void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
54+
static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer, unsigned long size, struct strbuf *err)
4755
{
4856
desc->buffer = buffer;
4957
desc->size = size;
5058
if (size)
51-
decode_tree_entry(desc, buffer, size);
59+
return decode_tree_entry(desc, buffer, size, err);
60+
return 0;
61+
}
62+
63+
void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
64+
{
65+
struct strbuf err = STRBUF_INIT;
66+
if (init_tree_desc_internal(desc, buffer, size, &err))
67+
die("%s", err.buf);
68+
strbuf_release(&err);
69+
}
70+
71+
int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size)
72+
{
73+
struct strbuf err = STRBUF_INIT;
74+
int result = init_tree_desc_internal(desc, buffer, size, &err);
75+
if (result)
76+
error("%s", err.buf);
77+
strbuf_release(&err);
78+
return result;
5279
}
5380

5481
void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
@@ -75,7 +102,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
75102
*a = t->entry;
76103
}
77104

78-
void update_tree_entry(struct tree_desc *desc)
105+
static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
79106
{
80107
const void *buf = desc->buffer;
81108
const unsigned char *end = desc->entry.oid->hash + 20;
@@ -89,7 +116,30 @@ void update_tree_entry(struct tree_desc *desc)
89116
desc->buffer = buf;
90117
desc->size = size;
91118
if (size)
92-
decode_tree_entry(desc, buf, size);
119+
return decode_tree_entry(desc, buf, size, err);
120+
return 0;
121+
}
122+
123+
void update_tree_entry(struct tree_desc *desc)
124+
{
125+
struct strbuf err = STRBUF_INIT;
126+
if (update_tree_entry_internal(desc, &err))
127+
die("%s", err.buf);
128+
strbuf_release(&err);
129+
}
130+
131+
int update_tree_entry_gently(struct tree_desc *desc)
132+
{
133+
struct strbuf err = STRBUF_INIT;
134+
if (update_tree_entry_internal(desc, &err)) {
135+
error("%s", err.buf);
136+
strbuf_release(&err);
137+
/* Stop processing this tree after error */
138+
desc->size = 0;
139+
return -1;
140+
}
141+
strbuf_release(&err);
142+
return 0;
93143
}
94144

95145
int tree_entry(struct tree_desc *desc, struct name_entry *entry)
@@ -102,6 +152,17 @@ int tree_entry(struct tree_desc *desc, struct name_entry *entry)
102152
return 1;
103153
}
104154

155+
int tree_entry_gently(struct tree_desc *desc, struct name_entry *entry)
156+
{
157+
if (!desc->size)
158+
return 0;
159+
160+
*entry = desc->entry;
161+
if (update_tree_entry_gently(desc))
162+
return 0;
163+
return 1;
164+
}
165+
105166
void setup_traverse_info(struct traverse_info *info, const char *base)
106167
{
107168
int pathlen = strlen(base);

tree-walk.h

+8
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,22 @@ static inline int tree_entry_len(const struct name_entry *ne)
2525
return (const char *)ne->oid - ne->path - 1;
2626
}
2727

28+
/*
29+
* The _gently versions of these functions warn and return false on a
30+
* corrupt tree entry rather than dying,
31+
*/
32+
2833
void update_tree_entry(struct tree_desc *);
34+
int update_tree_entry_gently(struct tree_desc *);
2935
void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
36+
int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size);
3037

3138
/*
3239
* Helper function that does both tree_entry_extract() and update_tree_entry()
3340
* and returns true for success
3441
*/
3542
int tree_entry(struct tree_desc *, struct name_entry *);
43+
int tree_entry_gently(struct tree_desc *, struct name_entry *);
3644

3745
void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
3846

0 commit comments

Comments
 (0)