Merge branch 'jk/fsck-tree-mode-bits-fix' into maint

"git fsck" reads mode from tree objects but canonicalizes the mode
before passing it to the logic to check object sanity, which has
hid broken tree objects from the checking logic.  This has been
corrected, but to help exiting projects with broken tree objects
that they cannot fix retroactively, the severity of anomalies this
code detects has been demoted to "info" for now.
source: <YvQcNpizy9uOZiAz@coredump.intra.peff.net>

* jk/fsck-tree-mode-bits-fix:
  fsck: downgrade tree badFilemode to "info"
  fsck: actually detect bad file modes in trees
  tree-walk: add a mechanism for getting non-canonicalized modes
This commit is contained in:
Junio C Hamano 2022-08-26 11:13:12 -07:00
Родитель 222f953777 4dd3b045f5
Коммит e5cb51d3aa
7 изменённых файлов: 51 добавлений и 10 удалений

4
fsck.c
Просмотреть файл

@ -308,7 +308,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
return -1;
name = fsck_get_object_name(options, &tree->object.oid);
if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
return -1;
while (tree_entry_gently(&desc, &entry)) {
struct object *obj;
@ -578,7 +578,7 @@ static int fsck_tree(const struct object_id *tree_oid,
const char *o_name;
struct name_stack df_dup_candidates = { NULL };
if (init_tree_desc_gently(&desc, buffer, size)) {
if (init_tree_desc_gently(&desc, buffer, size, TREE_DESC_RAW_MODES)) {
retval += report(options, tree_oid, OBJ_TREE,
FSCK_MSG_BAD_TREE,
"cannot be parsed as a tree");

2
fsck.h
Просмотреть файл

@ -56,7 +56,6 @@ enum fsck_msg_type {
FUNC(GITMODULES_PATH, ERROR) \
FUNC(GITMODULES_UPDATE, ERROR) \
/* warnings */ \
FUNC(BAD_FILEMODE, WARN) \
FUNC(EMPTY_NAME, WARN) \
FUNC(FULL_PATHNAME, WARN) \
FUNC(HAS_DOT, WARN) \
@ -66,6 +65,7 @@ enum fsck_msg_type {
FUNC(ZERO_PADDED_FILEMODE, WARN) \
FUNC(NUL_IN_COMMIT, WARN) \
/* infos (reported as warnings, but ignored by default) */ \
FUNC(BAD_FILEMODE, INFO) \
FUNC(GITMODULES_PARSE, INFO) \
FUNC(GITIGNORE_SYMLINK, INFO) \
FUNC(GITATTRIBUTES_SYMLINK, INFO) \

Просмотреть файл

@ -2243,7 +2243,7 @@ static int add_promisor_object(const struct object_id *oid,
struct tree *tree = (struct tree *)obj;
struct tree_desc desc;
struct name_entry entry;
if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
/*
* Error messages are given when packs are
* verified, so do not print any here.

Просмотреть файл

@ -364,6 +364,20 @@ test_expect_success 'tree entry with type mismatch' '
test_i18ngrep ! "dangling blob" out
'
test_expect_success 'tree entry with bogus mode' '
test_when_finished "remove_object \$blob" &&
test_when_finished "remove_object \$tree" &&
blob=$(echo blob | git hash-object -w --stdin) &&
blob_oct=$(echo $blob | hex2oct) &&
tree=$(printf "100000 foo\0${blob_oct}" |
git hash-object -t tree --stdin -w --literally) &&
git fsck 2>err &&
cat >expect <<-EOF &&
warning in tree $tree: badFilemode: contains bad file modes
EOF
test_cmp expect err
'
test_expect_success 'tag pointing to nonexistent' '
badoid=$(test_oid deadbeef) &&
cat >invalid-tag <<-EOF &&

Просмотреть файл

@ -352,4 +352,21 @@ test_expect_success \
grep "Cannot demote unterminatedheader" act
'
test_expect_success 'badFilemode is not a strict error' '
git init --bare badmode.git &&
tree=$(
cd badmode.git &&
blob=$(echo blob | git hash-object -w --stdin | hex2oct) &&
printf "123456 foo\0${blob}" |
git hash-object -t tree --stdin -w --literally
) &&
rm -rf dst.git &&
git init --bare dst.git &&
git -C dst.git config transfer.fsckObjects true &&
git -C badmode.git push ../dst.git $tree:refs/tags/tree 2>err &&
grep "$tree: badFilemode" err
'
test_done

Просмотреть файл

@ -47,17 +47,20 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
/* Initialize the descriptor entry */
desc->entry.path = path;
desc->entry.mode = canon_mode(mode);
desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
desc->entry.pathlen = len - 1;
oidread(&desc->entry.oid, (const unsigned char *)path + len);
return 0;
}
static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer, unsigned long size, struct strbuf *err)
static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
unsigned long size, struct strbuf *err,
enum tree_desc_flags flags)
{
desc->buffer = buffer;
desc->size = size;
desc->flags = flags;
if (size)
return decode_tree_entry(desc, buffer, size, err);
return 0;
@ -66,15 +69,16 @@ static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer, u
void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
{
struct strbuf err = STRBUF_INIT;
if (init_tree_desc_internal(desc, buffer, size, &err))
if (init_tree_desc_internal(desc, buffer, size, &err, 0))
die("%s", err.buf);
strbuf_release(&err);
}
int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size)
int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
enum tree_desc_flags flags)
{
struct strbuf err = STRBUF_INIT;
int result = init_tree_desc_internal(desc, buffer, size, &err);
int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
if (result)
error("%s", err.buf);
strbuf_release(&err);

Просмотреть файл

@ -34,6 +34,11 @@ struct tree_desc {
/* counts the number of bytes left in the `buffer`. */
unsigned int size;
/* option flags passed via init_tree_desc_gently() */
enum tree_desc_flags {
TREE_DESC_RAW_MODES = (1 << 0),
} flags;
};
/**
@ -79,7 +84,8 @@ int update_tree_entry_gently(struct tree_desc *);
*/
void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size);
int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size,
enum tree_desc_flags flags);
/*
* Visit the next entry in a tree. Returns 1 when there are more entries