ostree/fsck: Add --verify-back-refs option
authorPhilip Withnall <withnall@endlessm.com>
Mon, 20 Nov 2017 13:16:35 +0000 (13:16 +0000)
committerAtomic Bot <atomic-devel@projectatomic.io>
Thu, 14 Dec 2017 14:18:44 +0000 (14:18 +0000)
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

bash/ostree
man/ostree-fsck.xml
src/ostree/ot-builtin-fsck.c

index c132a43fd7d92794b09a06d917255d8c61ddef5e..2a67d1aa0bf254f60abe2af225777c0d2d0b6120 100644 (file)
@@ -963,6 +963,7 @@ _ostree_fsck() {
         --add-tombstones
         --delete
         --quiet -q
+        --verify-back-refs
     "
 
     local options_with_args="
index 2cae92cfb236fb3d5e1017b216678c15576e8e04..f634933babe70e6681cca217bf36e770a5757d06 100644 (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 commit’s 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>
 
index 6cd9b45742954186a61042fb40fa25999f8522b3..3505f7b304f9d58bad06076fe75a5392a0239514 100644 (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,
+                                               "Collection–ref (%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,
+                                               "Collection–ref (%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;