ostree/fsck: Add --verify-back-refs option

This new option verifies that the refs listed in the ref-bindings for
each commit all point to that commit (i.e. there aren’t multiple commits
listing the same ref in their ref-bindings, and there aren’t any commits
with non-empty ref-bindings which aren’t pointed at by a ref).

This is useful when generating a new repository from scratch, but not
useful when adding new commits to an existing repository (since the old
commits will still, correctly, have ref-bindings from when the refs
pointed at them). That’s why it has to be enabled explicitly using
--verify-back-refs, rather than being on by default.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Closes: #1347
Approved by: cgwalters
This commit is contained in:
Philip Withnall 2017-11-20 13:16:35 +00:00 committed by Atomic Bot
parent 38152d71aa
commit fb7692bd44
3 changed files with 85 additions and 0 deletions

View File

@ -963,6 +963,7 @@ _ostree_fsck() {
--add-tombstones
--delete
--quiet -q
--verify-back-refs
"
local options_with_args="

View File

@ -85,6 +85,17 @@ Boston, MA 02111-1307, USA.
Add tombstone commit for referenced but missing commits.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--verify-back-refs</option></term>
<listitem><para>
Verify that all the refs listed in a commits ref-bindings
point to that commit. This cannot be used in repositories
where the target of refs is changed over time as new commits
are added, but can be used in repositories which are
regenerated from scratch for each commit.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -30,6 +30,7 @@
static gboolean opt_quiet;
static gboolean opt_delete;
static gboolean opt_add_tombstones;
static gboolean opt_verify_back_refs;
/* ATTENTION:
* Please remember to update the bash-completion script (bash/ostree) and
@ -40,6 +41,7 @@ static GOptionEntry options[] = {
{ "add-tombstones", 0, 0, G_OPTION_ARG_NONE, &opt_add_tombstones, "Add tombstones for missing commits", NULL },
{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &opt_quiet, "Only print error messages", NULL },
{ "delete", 0, 0, G_OPTION_ARG_NONE, &opt_delete, "Remove corrupted objects", NULL },
{ "verify-back-refs", 0, 0, G_OPTION_ARG_NONE, &opt_verify_back_refs, "Verify back-references", NULL },
{ NULL }
};
@ -253,6 +255,77 @@ ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation,
if (!ostree_repo_load_commit (repo, checksum, &commit, &commitstate, error))
return FALSE;
/* If requested, check that all the refs listed in the ref-bindings
* for this commit resolve back to this commit. */
if (opt_verify_back_refs)
{
g_autoptr(GVariant) metadata = g_variant_get_child_value (commit, 0);
const char *collection_id = NULL;
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
if (!g_variant_lookup (metadata,
OSTREE_COMMIT_META_KEY_COLLECTION_BINDING,
"&s",
&collection_id))
collection_id = NULL;
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
g_autofree const char **refs = NULL;
if (g_variant_lookup (metadata,
OSTREE_COMMIT_META_KEY_REF_BINDING,
"^a&s",
&refs))
{
for (const char **iter = refs; *iter != NULL; ++iter)
{
g_autofree char *checksum_for_ref = NULL;
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
if (collection_id != NULL)
{
const OstreeCollectionRef collection_ref = { (char *) collection_id, (char *) *iter };
if (!ostree_repo_resolve_collection_ref (repo, &collection_ref,
TRUE,
OSTREE_REPO_RESOLVE_REV_EXT_NONE,
&checksum_for_ref,
cancellable,
error))
return FALSE;
}
else
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
{
if (!ostree_repo_resolve_rev (repo, *iter, TRUE,
&checksum_for_ref, error))
return FALSE;
}
if (checksum_for_ref == NULL)
{
if (collection_id != NULL)
return glnx_throw (error,
"Collectionref (%s, %s) in bindings for commit %s does not exist",
collection_id, *iter, checksum);
else
return glnx_throw (error,
"Ref %s in bindings for commit %s does not exist",
*iter, checksum);
}
else if (g_strcmp0 (checksum_for_ref, checksum) != 0)
{
if (collection_id != NULL)
return glnx_throw (error,
"Collectionref (%s, %s) in bindings for commit %s does not resolve to that commit",
collection_id, *iter, checksum);
else
return glnx_throw (error,
"Ref %s in bindings for commit %s does not resolve to that commit",
*iter, checksum);
}
}
}
}
if (opt_add_tombstones)
{
GError *local_error = NULL;