From ac9391093f54370ff3f06470e24d78b536a12327 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 14 Jul 2008 21:46:48 -0400 Subject: [PATCH] restore legacy behavior for read_sha1_file() Since commit 8eca0b47ff1598a6d163df9358c0e0c9bd92d4c8, it is possible for read_sha1_file() to return NULL even with existing objects when they are corrupted. Previously a corrupted object would have terminated the program immediately, effectively making read_sha1_file() return NULL only when specified object is not found. Let's restore this behavior for all users of read_sha1_file() and provide a separate function with the ability to not terminate when bad objects are encountered. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- cache.h | 3 ++ sha1_file.c | 30 +++++++++++++-- t/t6011-rev-list-with-bad-commit.sh | 60 +++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) create mode 100755 t/t6011-rev-list-with-bad-commit.sh diff --git a/cache.h b/cache.h index 0d8eddac77..a8254e2f9e 100644 --- a/cache.h +++ b/cache.h @@ -538,6 +538,9 @@ extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsig extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *); extern int force_object_loose(const unsigned char *sha1, time_t mtime); +/* just like read_sha1_file(), but non fatal in presence of bad objects */ +extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size); + extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type); extern int move_temp_to_file(const char *tmpfile, const char *filename); diff --git a/sha1_file.c b/sha1_file.c index 2df78b5afd..e281c14f01 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1006,6 +1006,18 @@ static void mark_bad_packed_object(struct packed_git *p, p->num_bad_objects++; } +static int has_packed_and_bad(const unsigned char *sha1) +{ + struct packed_git *p; + unsigned i; + + for (p = packed_git; p; p = p->next) + for (i = 0; i < p->num_bad_objects; i++) + if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i)) + return 1; + return 0; +} + int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type) { unsigned char real_sha1[20]; @@ -1647,7 +1659,7 @@ static void *unpack_delta_entry(struct packed_git *p, sha1_to_hex(base_sha1), (uintmax_t)base_offset, p->pack_name); mark_bad_packed_object(p, base_sha1); - base = read_sha1_file(base_sha1, type, &base_size); + base = read_object(base_sha1, type, &base_size); if (!base) return NULL; } @@ -1945,7 +1957,7 @@ static void *read_packed_sha1(const unsigned char *sha1, error("failed to read object %s at offset %"PRIuMAX" from %s", sha1_to_hex(sha1), (uintmax_t)e.offset, e.p->pack_name); mark_bad_packed_object(e.p, sha1); - data = read_sha1_file(sha1, type, size); + data = read_object(sha1, type, size); } return data; } @@ -2010,8 +2022,8 @@ int pretend_sha1_file(void *buf, unsigned long len, enum object_type type, return 0; } -void *read_sha1_file(const unsigned char *sha1, enum object_type *type, - unsigned long *size) +void *read_object(const unsigned char *sha1, enum object_type *type, + unsigned long *size) { unsigned long mapsize; void *map, *buf; @@ -2037,6 +2049,16 @@ void *read_sha1_file(const unsigned char *sha1, enum object_type *type, return read_packed_sha1(sha1, type, size); } +void *read_sha1_file(const unsigned char *sha1, enum object_type *type, + unsigned long *size) +{ + void *data = read_object(sha1, type, size); + /* legacy behavior is to die on corrupted objects */ + if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1))) + die("object %s is corrupted", sha1_to_hex(sha1)); + return data; +} + void *read_object_with_reference(const unsigned char *sha1, const char *required_type_name, unsigned long *size, diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh new file mode 100755 index 0000000000..e51eb41f4b --- /dev/null +++ b/t/t6011-rev-list-with-bad-commit.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +test_description='git rev-list should notice bad commits' + +. ./test-lib.sh + +# Note: +# - compression level is set to zero to make "corruptions" easier to perform +# - reflog is disabled to avoid extra references which would twart the test + +test_expect_success 'setup' \ + ' + git init && + git config core.compression 0 && + git config core.logallrefupdates false && + echo "foo" > foo && + git add foo && + git commit -m "first commit" && + echo "bar" > bar && + git add bar && + git commit -m "second commit" && + echo "baz" > baz && + git add baz && + git commit -m "third commit" && + echo "foo again" >> foo && + git add foo && + git commit -m "fourth commit" && + git repack -a -f -d + ' + +test_expect_success 'verify number of revisions' \ + ' + revs=$(git rev-list --all | wc -l) && + test $revs -eq 4 && + first_commit=$(git rev-parse HEAD~3) + ' + +test_expect_success 'corrupt second commit object' \ + ' + perl -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack && + test_must_fail git fsck --full + ' + +test_expect_success 'rev-list should fail' \ + ' + test_must_fail git rev-list --all > /dev/null + ' + +test_expect_success 'git repack _MUST_ fail' \ + ' + test_must_fail git repack -a -f -d + ' + +test_expect_success 'first commit is still available' \ + ' + git log $first_commit + ' + +test_done + -- 2.32.0.93.g670b81a890