Skip to content

Commit 9f11959

Browse files
pks-tgitster
authored andcommitted
cache-tree: refactor verification to return error codes
The function `cache_tree_verify()` will `BUG()` whenever it finds that the cache-tree extension of the index is corrupt. The function is thus inherently untestable because the resulting call to `abort()` will be detected by our testing framework and labelled an error. And rightfully so: it shouldn't ever be possible to hit bugs, as they should indicate a programming error rather than corruption of on-disk state. Refactor the function to instead return error codes. This also ensures that the function can be used e.g. by git-fsck(1) without the whole process dying. Furthermore, this refactoring plugs some memory leaks when returning early by creating a common exit path. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3969d78 commit 9f11959

File tree

4 files changed

+79
-35
lines changed

4 files changed

+79
-35
lines changed

cache-tree.c

+68-29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "git-compat-util.h"
44
#include "environment.h"
5+
#include "gettext.h"
56
#include "hex.h"
67
#include "lockfile.h"
78
#include "tree.h"
@@ -864,15 +865,15 @@ int cache_tree_matches_traversal(struct cache_tree *root,
864865
return 0;
865866
}
866867

867-
static void verify_one_sparse(struct index_state *istate,
868-
struct strbuf *path,
869-
int pos)
868+
static int verify_one_sparse(struct index_state *istate,
869+
struct strbuf *path,
870+
int pos)
870871
{
871872
struct cache_entry *ce = istate->cache[pos];
872-
873873
if (!S_ISSPARSEDIR(ce->ce_mode))
874-
BUG("directory '%s' is present in index, but not sparse",
875-
path->buf);
874+
return error(_("directory '%s' is present in index, but not sparse"),
875+
path->buf);
876+
return 0;
876877
}
877878

878879
/*
@@ -881,6 +882,7 @@ static void verify_one_sparse(struct index_state *istate,
881882
* 1 - Restart verification - a call to ensure_full_index() freed the cache
882883
* tree that is being verified and verification needs to be restarted from
883884
* the new toplevel cache tree.
885+
* -1 - Verification failed.
884886
*/
885887
static int verify_one(struct repository *r,
886888
struct index_state *istate,
@@ -890,18 +892,23 @@ static int verify_one(struct repository *r,
890892
int i, pos, len = path->len;
891893
struct strbuf tree_buf = STRBUF_INIT;
892894
struct object_id new_oid;
895+
int ret;
893896

894897
for (i = 0; i < it->subtree_nr; i++) {
895898
strbuf_addf(path, "%s/", it->down[i]->name);
896-
if (verify_one(r, istate, it->down[i]->cache_tree, path))
897-
return 1;
899+
ret = verify_one(r, istate, it->down[i]->cache_tree, path);
900+
if (ret)
901+
goto out;
902+
898903
strbuf_setlen(path, len);
899904
}
900905

901906
if (it->entry_count < 0 ||
902907
/* no verification on tests (t7003) that replace trees */
903-
lookup_replace_object(r, &it->oid) != &it->oid)
904-
return 0;
908+
lookup_replace_object(r, &it->oid) != &it->oid) {
909+
ret = 0;
910+
goto out;
911+
}
905912

906913
if (path->len) {
907914
/*
@@ -911,12 +918,14 @@ static int verify_one(struct repository *r,
911918
*/
912919
int is_sparse = istate->sparse_index;
913920
pos = index_name_pos(istate, path->buf, path->len);
914-
if (is_sparse && !istate->sparse_index)
915-
return 1;
921+
if (is_sparse && !istate->sparse_index) {
922+
ret = 1;
923+
goto out;
924+
}
916925

917926
if (pos >= 0) {
918-
verify_one_sparse(istate, path, pos);
919-
return 0;
927+
ret = verify_one_sparse(istate, path, pos);
928+
goto out;
920929
}
921930

922931
pos = -pos - 1;
@@ -934,16 +943,23 @@ static int verify_one(struct repository *r,
934943
unsigned mode;
935944
int entlen;
936945

937-
if (ce->ce_flags & (CE_STAGEMASK | CE_INTENT_TO_ADD | CE_REMOVE))
938-
BUG("%s with flags 0x%x should not be in cache-tree",
939-
ce->name, ce->ce_flags);
946+
if (ce->ce_flags & (CE_STAGEMASK | CE_INTENT_TO_ADD | CE_REMOVE)) {
947+
ret = error(_("%s with flags 0x%x should not be in cache-tree"),
948+
ce->name, ce->ce_flags);
949+
goto out;
950+
}
951+
940952
name = ce->name + path->len;
941953
slash = strchr(name, '/');
942954
if (slash) {
943955
entlen = slash - name;
956+
944957
sub = find_subtree(it, ce->name + path->len, entlen, 0);
945-
if (!sub || sub->cache_tree->entry_count < 0)
946-
BUG("bad subtree '%.*s'", entlen, name);
958+
if (!sub || sub->cache_tree->entry_count < 0) {
959+
ret = error(_("bad subtree '%.*s'"), entlen, name);
960+
goto out;
961+
}
962+
947963
oid = &sub->cache_tree->oid;
948964
mode = S_IFDIR;
949965
i += sub->cache_tree->entry_count;
@@ -956,27 +972,50 @@ static int verify_one(struct repository *r,
956972
strbuf_addf(&tree_buf, "%o %.*s%c", mode, entlen, name, '\0');
957973
strbuf_add(&tree_buf, oid->hash, r->hash_algo->rawsz);
958974
}
975+
959976
hash_object_file(r->hash_algo, tree_buf.buf, tree_buf.len, OBJ_TREE,
960977
&new_oid);
961-
if (!oideq(&new_oid, &it->oid))
962-
BUG("cache-tree for path %.*s does not match. "
963-
"Expected %s got %s", len, path->buf,
964-
oid_to_hex(&new_oid), oid_to_hex(&it->oid));
978+
979+
if (!oideq(&new_oid, &it->oid)) {
980+
ret = error(_("cache-tree for path %.*s does not match. "
981+
"Expected %s got %s"), len, path->buf,
982+
oid_to_hex(&new_oid), oid_to_hex(&it->oid));
983+
goto out;
984+
}
985+
986+
ret = 0;
987+
out:
965988
strbuf_setlen(path, len);
966989
strbuf_release(&tree_buf);
967-
return 0;
990+
return ret;
968991
}
969992

970-
void cache_tree_verify(struct repository *r, struct index_state *istate)
993+
int cache_tree_verify(struct repository *r, struct index_state *istate)
971994
{
972995
struct strbuf path = STRBUF_INIT;
996+
int ret;
973997

974-
if (!istate->cache_tree)
975-
return;
976-
if (verify_one(r, istate, istate->cache_tree, &path)) {
998+
if (!istate->cache_tree) {
999+
ret = 0;
1000+
goto out;
1001+
}
1002+
1003+
ret = verify_one(r, istate, istate->cache_tree, &path);
1004+
if (ret < 0)
1005+
goto out;
1006+
if (ret > 0) {
9771007
strbuf_reset(&path);
978-
if (verify_one(r, istate, istate->cache_tree, &path))
1008+
1009+
ret = verify_one(r, istate, istate->cache_tree, &path);
1010+
if (ret < 0)
1011+
goto out;
1012+
if (ret > 0)
9791013
BUG("ensure_full_index() called twice while verifying cache tree");
9801014
}
1015+
1016+
ret = 0;
1017+
1018+
out:
9811019
strbuf_release(&path);
1020+
return ret;
9821021
}

cache-tree.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
3333

3434
int cache_tree_fully_valid(struct cache_tree *);
3535
int cache_tree_update(struct index_state *, int);
36-
void cache_tree_verify(struct repository *, struct index_state *);
36+
int cache_tree_verify(struct repository *, struct index_state *);
3737

3838
/* bitmasks to write_index_as_tree flags */
3939
#define WRITE_TREE_MISSING_OK 1

read-cache.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -3331,8 +3331,9 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
33313331
int new_shared_index, ret, test_split_index_env;
33323332
struct split_index *si = istate->split_index;
33333333

3334-
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
3335-
cache_tree_verify(the_repository, istate);
3334+
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0) &&
3335+
cache_tree_verify(the_repository, istate) < 0)
3336+
return -1;
33363337

33373338
if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) {
33383339
if (flags & COMMIT_LOCK)

unpack-trees.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -2070,9 +2070,13 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
20702070
if (o->dst_index) {
20712071
move_index_extensions(&o->internal.result, o->src_index);
20722072
if (!ret) {
2073-
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
2074-
cache_tree_verify(the_repository,
2075-
&o->internal.result);
2073+
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0) &&
2074+
cache_tree_verify(the_repository,
2075+
&o->internal.result) < 0) {
2076+
ret = -1;
2077+
goto done;
2078+
}
2079+
20762080
if (!o->skip_cache_tree_update &&
20772081
!cache_tree_fully_valid(o->internal.result.cache_tree))
20782082
cache_tree_update(&o->internal.result,

0 commit comments

Comments
 (0)