From 474556b95547d75fe1cc4c2131a878489c7a3f82 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 6 Apr 2018 15:39:43 +0200 Subject: [PATCH] fsck: Mark commits with missing or deleted object partial This means we can later use various operations to heal the repository because ostree does not assume all objects are there. This the begining of a fix for https://github.com/ostreedev/ostree/pull/345 Closes: #1533 Approved by: cgwalters --- src/ostree/ot-builtin-fsck.c | 40 +++++++++++++++---- tests/installed/nondestructive/itest-pull.sh | 15 +++++++ tests/test-corruption.sh | 42 +++++++++++++++++++- 3 files changed, 89 insertions(+), 8 deletions(-) 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"