Git fork

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 <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Patrick Steinhardt and committed by
Junio C Hamano
9f119599 3969d783

+79 -35
+68 -29
cache-tree.c
··· 2 2 3 3 #include "git-compat-util.h" 4 4 #include "environment.h" 5 + #include "gettext.h" 5 6 #include "hex.h" 6 7 #include "lockfile.h" 7 8 #include "tree.h" ··· 864 865 return 0; 865 866 } 866 867 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) 870 871 { 871 872 struct cache_entry *ce = istate->cache[pos]; 872 - 873 873 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; 876 877 } 877 878 878 879 /* ··· 881 882 * 1 - Restart verification - a call to ensure_full_index() freed the cache 882 883 * tree that is being verified and verification needs to be restarted from 883 884 * the new toplevel cache tree. 885 + * -1 - Verification failed. 884 886 */ 885 887 static int verify_one(struct repository *r, 886 888 struct index_state *istate, ··· 890 892 int i, pos, len = path->len; 891 893 struct strbuf tree_buf = STRBUF_INIT; 892 894 struct object_id new_oid; 895 + int ret; 893 896 894 897 for (i = 0; i < it->subtree_nr; i++) { 895 898 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 + 898 903 strbuf_setlen(path, len); 899 904 } 900 905 901 906 if (it->entry_count < 0 || 902 907 /* 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 + } 905 912 906 913 if (path->len) { 907 914 /* ··· 911 918 */ 912 919 int is_sparse = istate->sparse_index; 913 920 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 + } 916 925 917 926 if (pos >= 0) { 918 - verify_one_sparse(istate, path, pos); 919 - return 0; 927 + ret = verify_one_sparse(istate, path, pos); 928 + goto out; 920 929 } 921 930 922 931 pos = -pos - 1; ··· 934 943 unsigned mode; 935 944 int entlen; 936 945 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 + 940 952 name = ce->name + path->len; 941 953 slash = strchr(name, '/'); 942 954 if (slash) { 943 955 entlen = slash - name; 956 + 944 957 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 + 947 963 oid = &sub->cache_tree->oid; 948 964 mode = S_IFDIR; 949 965 i += sub->cache_tree->entry_count; ··· 956 972 strbuf_addf(&tree_buf, "%o %.*s%c", mode, entlen, name, '\0'); 957 973 strbuf_add(&tree_buf, oid->hash, r->hash_algo->rawsz); 958 974 } 975 + 959 976 hash_object_file(r->hash_algo, tree_buf.buf, tree_buf.len, OBJ_TREE, 960 977 &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: 965 988 strbuf_setlen(path, len); 966 989 strbuf_release(&tree_buf); 967 - return 0; 990 + return ret; 968 991 } 969 992 970 - void cache_tree_verify(struct repository *r, struct index_state *istate) 993 + int cache_tree_verify(struct repository *r, struct index_state *istate) 971 994 { 972 995 struct strbuf path = STRBUF_INIT; 996 + int ret; 973 997 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) { 977 1007 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) 979 1013 BUG("ensure_full_index() called twice while verifying cache tree"); 980 1014 } 1015 + 1016 + ret = 0; 1017 + 1018 + out: 981 1019 strbuf_release(&path); 1020 + return ret; 982 1021 }
+1 -1
cache-tree.h
··· 33 33 34 34 int cache_tree_fully_valid(struct cache_tree *); 35 35 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 *); 37 37 38 38 /* bitmasks to write_index_as_tree flags */ 39 39 #define WRITE_TREE_MISSING_OK 1
+3 -2
read-cache.c
··· 3331 3331 int new_shared_index, ret, test_split_index_env; 3332 3332 struct split_index *si = istate->split_index; 3333 3333 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; 3336 3337 3337 3338 if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) { 3338 3339 if (flags & COMMIT_LOCK)
+7 -3
unpack-trees.c
··· 2070 2070 if (o->dst_index) { 2071 2071 move_index_extensions(&o->internal.result, o->src_index); 2072 2072 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 + 2076 2080 if (!o->skip_cache_tree_update && 2077 2081 !cache_tree_fully_valid(o->internal.result.cache_tree)) 2078 2082 cache_tree_update(&o->internal.result,