Skip to content

Commit c68fa89

Browse files
pks-tdscho
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 6c1e873 commit c68fa89

File tree

4 files changed

+79
-35
lines changed

4 files changed

+79
-35
lines changed

cache-tree.c

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#define USE_THE_REPOSITORY_VARIABLE
22

33
#include "git-compat-util.h"
4+
#include "gettext.h"
45
#include "hex.h"
56
#include "lockfile.h"
67
#include "tree.h"
@@ -865,15 +866,15 @@ int cache_tree_matches_traversal(struct cache_tree *root,
865866
return 0;
866867
}
867868

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

879880
/*
@@ -882,6 +883,7 @@ static void verify_one_sparse(struct index_state *istate,
882883
* 1 - Restart verification - a call to ensure_full_index() freed the cache
883884
* tree that is being verified and verification needs to be restarted from
884885
* the new toplevel cache tree.
886+
* -1 - Verification failed.
885887
*/
886888
static int verify_one(struct repository *r,
887889
struct index_state *istate,
@@ -891,18 +893,23 @@ static int verify_one(struct repository *r,
891893
int i, pos, len = path->len;
892894
struct strbuf tree_buf = STRBUF_INIT;
893895
struct object_id new_oid;
896+
int ret;
894897

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

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

907914
if (path->len) {
908915
/*
@@ -912,12 +919,14 @@ static int verify_one(struct repository *r,
912919
*/
913920
int is_sparse = istate->sparse_index;
914921
pos = index_name_pos(istate, path->buf, path->len);
915-
if (is_sparse && !istate->sparse_index)
916-
return 1;
922+
if (is_sparse && !istate->sparse_index) {
923+
ret = 1;
924+
goto out;
925+
}
917926

918927
if (pos >= 0) {
919-
verify_one_sparse(istate, path, pos);
920-
return 0;
928+
ret = verify_one_sparse(istate, path, pos);
929+
goto out;
921930
}
922931

923932
pos = -pos - 1;
@@ -940,16 +949,23 @@ static int verify_one(struct repository *r,
940949
unsigned mode;
941950
int entlen;
942951

943-
if (ce->ce_flags & (CE_STAGEMASK | CE_INTENT_TO_ADD | CE_REMOVE))
944-
BUG("%s with flags 0x%x should not be in cache-tree",
945-
ce->name, ce->ce_flags);
952+
if (ce->ce_flags & (CE_STAGEMASK | CE_INTENT_TO_ADD | CE_REMOVE)) {
953+
ret = error(_("%s with flags 0x%x should not be in cache-tree"),
954+
ce->name, ce->ce_flags);
955+
goto out;
956+
}
957+
946958
name = ce->name + path->len;
947959
slash = strchr(name, '/');
948960
if (slash) {
949961
entlen = slash - name;
962+
950963
sub = find_subtree(it, ce->name + path->len, entlen, 0);
951-
if (!sub || sub->cache_tree->entry_count < 0)
952-
BUG("bad subtree '%.*s'", entlen, name);
964+
if (!sub || sub->cache_tree->entry_count < 0) {
965+
ret = error(_("bad subtree '%.*s'"), entlen, name);
966+
goto out;
967+
}
968+
953969
oid = &sub->cache_tree->oid;
954970
mode = S_IFDIR;
955971
i += sub->cache_tree->entry_count;
@@ -962,27 +978,50 @@ static int verify_one(struct repository *r,
962978
strbuf_addf(&tree_buf, "%o %.*s%c", mode, entlen, name, '\0');
963979
strbuf_add(&tree_buf, oid->hash, r->hash_algo->rawsz);
964980
}
981+
965982
hash_object_file(r->hash_algo, tree_buf.buf, tree_buf.len, OBJ_TREE,
966983
&new_oid);
967-
if (!oideq(&new_oid, &it->oid))
968-
BUG("cache-tree for path %.*s does not match. "
969-
"Expected %s got %s", len, path->buf,
970-
oid_to_hex(&new_oid), oid_to_hex(&it->oid));
984+
985+
if (!oideq(&new_oid, &it->oid)) {
986+
ret = error(_("cache-tree for path %.*s does not match. "
987+
"Expected %s got %s"), len, path->buf,
988+
oid_to_hex(&new_oid), oid_to_hex(&it->oid));
989+
goto out;
990+
}
991+
992+
ret = 0;
993+
out:
971994
strbuf_setlen(path, len);
972995
strbuf_release(&tree_buf);
973-
return 0;
996+
return ret;
974997
}
975998

976-
void cache_tree_verify(struct repository *r, struct index_state *istate)
999+
int cache_tree_verify(struct repository *r, struct index_state *istate)
9771000
{
9781001
struct strbuf path = STRBUF_INIT;
1002+
int ret;
9791003

980-
if (!istate->cache_tree)
981-
return;
982-
if (verify_one(r, istate, istate->cache_tree, &path)) {
1004+
if (!istate->cache_tree) {
1005+
ret = 0;
1006+
goto out;
1007+
}
1008+
1009+
ret = verify_one(r, istate, istate->cache_tree, &path);
1010+
if (ret < 0)
1011+
goto out;
1012+
if (ret > 0) {
9831013
strbuf_reset(&path);
984-
if (verify_one(r, istate, istate->cache_tree, &path))
1014+
1015+
ret = verify_one(r, istate, istate->cache_tree, &path);
1016+
if (ret < 0)
1017+
goto out;
1018+
if (ret > 0)
9851019
BUG("ensure_full_index() called twice while verifying cache tree");
9861020
}
1021+
1022+
ret = 0;
1023+
1024+
out:
9871025
strbuf_release(&path);
1026+
return ret;
9881027
}

cache-tree.h

Lines changed: 1 addition & 1 deletion
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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3347,8 +3347,9 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
33473347
int new_shared_index, ret, test_split_index_env;
33483348
struct split_index *si = istate->split_index;
33493349

3350-
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
3351-
cache_tree_verify(the_repository, istate);
3350+
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0) &&
3351+
cache_tree_verify(the_repository, istate) < 0)
3352+
return -1;
33523353

33533354
if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) {
33543355
if (flags & COMMIT_LOCK)

unpack-trees.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,9 +2074,13 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
20742074
if (o->dst_index) {
20752075
move_index_extensions(&o->internal.result, o->src_index);
20762076
if (!ret) {
2077-
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
2078-
cache_tree_verify(the_repository,
2079-
&o->internal.result);
2077+
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0) &&
2078+
cache_tree_verify(the_repository,
2079+
&o->internal.result) < 0) {
2080+
ret = -1;
2081+
goto done;
2082+
}
2083+
20802084
if (!o->skip_cache_tree_update &&
20812085
!cache_tree_fully_valid(o->internal.result.cache_tree))
20822086
cache_tree_update(&o->internal.result,

0 commit comments

Comments
 (0)