diff --git a/src/ostree/ot-builtin-fsck.c b/src/ostree/ot-builtin-fsck.c index 3f9da783..8a44b619 100644 --- a/src/ostree/ot-builtin-fsck.c +++ b/src/ostree/ot-builtin-fsck.c @@ -53,6 +53,8 @@ static gboolean fsck_one_object (OstreeRepo *repo, const char *checksum, OstreeObjectType objtype, + GHashTable *object_parents, + GVariant *key, gboolean *out_found_corruption, GCancellable *cancellable, GError **error) @@ -60,12 +62,14 @@ fsck_one_object (OstreeRepo *repo, g_autoptr(GError) temp_error = NULL; if (!ostree_repo_fsck_object (repo, objtype, checksum, cancellable, &temp_error)) { + gboolean object_missing = FALSE; + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_clear_error (&temp_error); g_printerr ("Object missing: %s.%s\n", checksum, ostree_object_type_to_string (objtype)); - *out_found_corruption = TRUE; + object_missing = TRUE; } else { @@ -73,7 +77,7 @@ fsck_one_object (OstreeRepo *repo, { g_printerr ("%s\n", temp_error->message); (void) ostree_repo_delete_object (repo, objtype, checksum, cancellable, NULL); - *out_found_corruption = TRUE; + object_missing = TRUE; } else { @@ -81,6 +85,26 @@ fsck_one_object (OstreeRepo *repo, return FALSE; } } + + if (object_missing) + { + *out_found_corruption = TRUE; + + if (object_parents != NULL && objtype != OSTREE_OBJECT_TYPE_COMMIT) + { + g_auto(GStrv) parent_commits = ostree_repo_traverse_parents_get_commits (object_parents, key); + int i; + + /* The commit was missing or deleted, mark the commit partial */ + for (i = 0; parent_commits[i] != NULL; i++) + { + const char *parent_commit = parent_commits[i]; + g_printerr ("Marking commit %s as partial\n", parent_commit); + if (!ostree_repo_mark_commit_partial (repo, parent_commit, TRUE, error)) + return FALSE; + } + } + } } return TRUE; @@ -94,6 +118,7 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo, GError **error) { g_autoptr(GHashTable) reachable_objects = ostree_repo_traverse_new_reachable (); + g_autoptr(GHashTable) object_parents = ostree_repo_traverse_new_parents (); GHashTableIter hash_iter; gpointer key, value; @@ -108,8 +133,8 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo, g_assert (objtype == OSTREE_OBJECT_TYPE_COMMIT); - if (!ostree_repo_traverse_commit_union (repo, checksum, 0, reachable_objects, - cancellable, error)) + if (!ostree_repo_traverse_commit_union_with_parents (repo, checksum, 0, reachable_objects, object_parents, + cancellable, error)) return FALSE; } @@ -127,8 +152,9 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo, ostree_object_name_deserialize (serialized_key, &checksum, &objtype); - if (!fsck_one_object (repo, checksum, objtype, out_found_corruption, - cancellable, error)) + if (!fsck_one_object (repo, checksum, objtype, + object_parents, serialized_key, + out_found_corruption, cancellable, error)) return FALSE; i++; @@ -150,7 +176,7 @@ fsck_commit_for_ref (OstreeRepo *repo, GError **error) { if (!fsck_one_object (repo, checksum, OSTREE_OBJECT_TYPE_COMMIT, - found_corruption, + NULL, NULL, found_corruption, cancellable, error)) return FALSE; diff --git a/tests/installed/nondestructive/itest-pull.sh b/tests/installed/nondestructive/itest-pull.sh index a7cd922d..b3a52a27 100755 --- a/tests/installed/nondestructive/itest-pull.sh +++ b/tests/installed/nondestructive/itest-pull.sh @@ -30,6 +30,20 @@ ostree --repo=bare-repo init --mode=bare-user ostree --repo=bare-repo remote add origin --set=gpg-verify=false $(cat ${test_tmpdir}/httpd-address) log_timestamps ostree --repo=bare-repo pull --disable-static-deltas origin ${host_nonremoteref} +echo "ok pull" + +# fsck marks commits partial +# https://github.com/ostreedev/ostree/pull/1533 +for d in $(find bare-repo/objects/ -maxdepth 1 -type d); do + (find ${d} -name '*.file' || true) | head -20 | xargs rm -vf +done +if ostree --repo=bare-repo fsck; then + fatal "fsck unexpectedly succeeded" +fi +ostree --repo=bare-repo pull origin ${host_nonremoteref} +# Don't need a full fsck here +ostree --repo=bare-repo ls origin:${host_nonremoteref} >/dev/null + rm bare-repo repo -rf # Try copying the host's repo across a mountpoint for direct @@ -44,6 +58,7 @@ log_timestamps ostree --repo=repo fsck cd .. umount mnt +# Cleanup kill -TERM $(cat ${test_tmpdir}/httpd-pid) echo "ok" date diff --git a/tests/test-corruption.sh b/tests/test-corruption.sh index 997d39c7..3b4a649e 100755 --- a/tests/test-corruption.sh +++ b/tests/test-corruption.sh @@ -21,7 +21,7 @@ set -euo pipefail -echo "1..6" +echo "1..8" . $(dirname $0)/libtest.sh @@ -89,3 +89,43 @@ if ${CMD_PREFIX} ostree --repo=ostree-path-traverse/repo checkout pathtraverse-t fi assert_file_has_content_literal err.txt 'Invalid / in filename ../afile' echo "ok path traverse checkout" + +cd ${test_tmpdir} +rm repo files -rf +setup_test_repository "bare" + +rev=$($OSTREE rev-parse test2) + +filechecksum=$(ostree_file_path_to_checksum repo test2 /firstfile) +rm repo/$(ostree_checksum_to_relative_object_path repo $filechecksum) + +assert_not_has_file repo/state/${rev}.commitpartial + +if $OSTREE fsck -q 2>err.txt; then + assert_not_reached "fsck unexpectedly succeeded" +fi +assert_file_has_content_literal err.txt "Object missing:" +assert_file_has_content_literal err.txt "Marking commit $rev as partial" +assert_has_file repo/state/${rev}.commitpartial + +echo "ok missing file" + +cd ${test_tmpdir} +rm repo files -rf +setup_test_repository "bare" + +rev=$($OSTREE rev-parse test2) + +filechecksum=$(ostree_file_path_to_checksum repo test2 /firstfile) +echo corrupted >> repo/$(ostree_checksum_to_relative_object_path repo $filechecksum) + +assert_not_has_file repo/state/${rev}.commitpartial + +if $OSTREE fsck -q --delete 2>err.txt; then + assert_not_reached "fsck unexpectedly succeeded" +fi +assert_file_has_content_literal err.txt "Corrupted file object;" +assert_file_has_content_literal err.txt "Marking commit $rev as partial" +assert_has_file repo/state/${rev}.commitpartial + +echo "ok corrupt file"