Esempio n. 1
0
/**
 * ostree_repo_prune_from_reachable:
 * @self: Repo
 * @options: Options controlling prune process
 * @out_objects_total: (out): Number of objects found
 * @out_objects_pruned: (out): Number of objects deleted
 * @out_pruned_object_size_total: (out): Storage size in bytes of objects deleted
 * @cancellable: Cancellable
 * @error: Error
 *
 * Delete content from the repository.  This function is the "backend"
 * half of the higher level ostree_repo_prune().  To use this function,
 * you determine the root set yourself, and this function finds all other
 * unreferenced objects and deletes them.
 *
 * Use this API when you want to perform more selective pruning - for example,
 * retain all commits from a production branch, but just GC some history from
 * your dev branch.
 *
 * The %OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE flag may be specified to just determine
 * statistics on objects that would be deleted, without actually deleting them.
 */
gboolean
ostree_repo_prune_from_reachable (OstreeRepo        *self,
                                  OstreeRepoPruneOptions *options,
                                  gint              *out_objects_total,
                                  gint              *out_objects_pruned,
                                  guint64           *out_pruned_object_size_total,
                                  GCancellable      *cancellable,
                                  GError           **error)
{
  g_autoptr(GHashTable) objects = NULL;

  if (!ostree_repo_list_objects (self, OSTREE_REPO_LIST_OBJECTS_ALL | OSTREE_REPO_LIST_OBJECTS_NO_PARENTS,
                                 &objects, cancellable, error))
    return FALSE;

  return repo_prune_internal (self, objects, options, out_objects_total,
                              out_objects_pruned, out_pruned_object_size_total,
                              cancellable, error);
}
Esempio n. 2
0
gboolean
ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error)
{
  g_autoptr(OstreeRepo) repo = NULL;
  gboolean found_corruption = FALSE;

  g_autoptr(GOptionContext) context = g_option_context_new ("");
  if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error))
    return FALSE;

  if (!opt_quiet)
    g_print ("Validating refs...\n");

  /* Validate that the commit for each ref is available */
  g_autoptr(GHashTable) all_refs = NULL;
  if (!ostree_repo_list_refs (repo, NULL, &all_refs,
                              cancellable, error))
    return FALSE;

  GHashTableIter hash_iter;
  gpointer key, value;
  g_hash_table_iter_init (&hash_iter, all_refs);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      const char *refspec = key;
      const char *checksum = value;
      g_autofree char *ref_name = NULL;
      if (!ostree_parse_refspec (refspec, NULL, &ref_name, error))
        return FALSE;
      if (!fsck_commit_for_ref (repo, checksum, NULL, ref_name,
                                &found_corruption, cancellable, error))
        return FALSE;
    }

  if (!opt_quiet)
    g_print ("Validating refs in collections...\n");

  g_autoptr(GHashTable) all_collection_refs = NULL;  /* (element-type OstreeCollectionRef utf8) */
  if (!ostree_repo_list_collection_refs (repo, NULL, &all_collection_refs,
                                         OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES,
                                         cancellable, error))
    return FALSE;

  g_hash_table_iter_init (&hash_iter, all_collection_refs);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      const OstreeCollectionRef *ref = key;
      if (!fsck_commit_for_ref (repo, value, ref->collection_id, ref->ref_name,
                                &found_corruption, cancellable, error))
        return FALSE;
    }

  if (!opt_quiet)
    g_print ("Enumerating objects...\n");

  g_autoptr(GHashTable) objects = NULL;
  if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL,
                                 &objects, cancellable, error))
    return FALSE;

  g_autoptr(GHashTable) commits = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
                                                         (GDestroyNotify)g_variant_unref, NULL);


  g_autoptr(GPtrArray) tombstones = NULL;
  if (opt_add_tombstones)
    tombstones = g_ptr_array_new_with_free_func (g_free);

  if (opt_verify_back_refs)
    opt_verify_bindings = TRUE;

  guint n_partial = 0;
  g_hash_table_iter_init (&hash_iter, objects);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      GVariant *serialized_key = key;
      const char *checksum;
      OstreeObjectType objtype;
      OstreeRepoCommitState commitstate = 0;
      g_autoptr(GVariant) commit = NULL;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
        {
          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;
              if (!g_variant_lookup (metadata,
                                     OSTREE_COMMIT_META_KEY_COLLECTION_BINDING,
                                     "&s",
                                     &collection_id))
                collection_id = NULL;

              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;

                      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
                        {
                          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;
              g_autofree char *parent = ostree_commit_get_parent (commit);
              if (parent)
                {
                  g_autoptr(GVariant) parent_commit = NULL;
                  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, parent,
                                                 &parent_commit, &local_error))
                    {
                      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
                        {
                          g_ptr_array_add (tombstones, g_strdup (checksum));
                          g_clear_error (&local_error);
                        }
                      else
                        {
                          g_propagate_error (error, local_error);
                          return FALSE;
                        }
                    }
                }
            }

          if (commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL)
            n_partial++;
          else
            g_hash_table_add (commits, g_variant_ref (serialized_key));
        }
    }

  g_clear_pointer (&objects, (GDestroyNotify) g_hash_table_unref);

  if (!opt_quiet)
    g_print ("Verifying content integrity of %u commit objects...\n",
             (guint)g_hash_table_size (commits));

  if (!fsck_reachable_objects_from_commits (repo, commits, &found_corruption,
                                            cancellable, error))
    return FALSE;

  if (opt_add_tombstones)
    {
      guint i;
      if (tombstones->len)
        {
          if (!ot_enable_tombstone_commits (repo, error))
            return FALSE;
        }
      for (i = 0; i < tombstones->len; i++)
        {
          const char *checksum = tombstones->pdata[i];
          g_print ("Adding tombstone for commit %s\n", checksum);
          if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error))
            return FALSE;
        }
    }
  else if (n_partial > 0)
    {
      g_print ("%u partial commits not verified\n", n_partial);
    }

  if (found_corruption)
    return glnx_throw (error, "Repository corruption encountered");

  return TRUE;
}
Esempio n. 3
0
gboolean
ostree_builtin_fsck (int argc, char **argv, GFile *repo_path, GError **error)
{
  GOptionContext *context;
  OtFsckData data;
  gboolean ret = FALSE;
  GCancellable *cancellable = NULL;
  GHashTableIter hash_iter;
  gpointer key, value;
  ot_lobj OstreeRepo *repo = NULL;
  ot_lhash GHashTable *objects = NULL;
  ot_lhash GHashTable *commits = NULL;

  context = g_option_context_new ("- Check the repository for consistency");
  g_option_context_add_main_entries (context, options, NULL);

  if (!g_option_context_parse (context, &argc, &argv, error))
    goto out;

  repo = ostree_repo_new (repo_path);
  if (!ostree_repo_check (repo, error))
    goto out;

  memset (&data, 0, sizeof (data));
  data.repo = repo;

  g_print ("Enumerating objects...\n");

  if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL,
                                 &objects, cancellable, error))
    goto out;

  commits = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
                                   (GDestroyNotify)g_variant_unref, NULL);
  
  g_hash_table_iter_init (&hash_iter, objects);

  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      GVariant *serialized_key = key;
      const char *checksum;
      OstreeObjectType objtype;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
        g_hash_table_insert (commits, g_variant_ref (serialized_key), serialized_key);
    }

  g_clear_pointer (&objects, (GDestroyNotify) g_hash_table_unref);

  g_print ("Verifying content integrity of %u commit objects...\n",
           (guint)g_hash_table_size (commits));

  if (!fsck_reachable_objects_from_commits (&data, commits, cancellable, error))
    goto out;

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
Esempio n. 4
0
static gboolean
prune_commits_keep_younger_than_date (OstreeRepo *repo, const char *date, GCancellable *cancellable, GError **error)
{
  g_autoptr(GHashTable) refs = NULL;
  g_autoptr(GHashTable) ref_heads = g_hash_table_new (g_str_hash, g_str_equal);
  g_autoptr(GHashTable) objects = NULL;
  GHashTableIter hash_iter;
  gpointer key, value;
  struct timespec ts;
  gboolean ret = FALSE;

  if (!parse_datetime (&ts, date, NULL))
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Could not parse '%s'", date);
      goto out;
    }

  if (!ot_enable_tombstone_commits (repo, error))
    goto out;

  if (!ostree_repo_list_refs (repo, NULL, &refs, cancellable, error))
    goto out;

  /* We used to prune the HEAD of a given ref by default, but that's
   * broken for a few reasons.  One is that people may use branches as
   * tags.  Second is that if we do it, we should be deleting the ref
   * too, otherwise e.g. `summary -u` breaks trying to load it, etc.
   */
  g_hash_table_iter_init (&hash_iter, refs);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      /* Value is lifecycle bound to refs */
      g_hash_table_add (ref_heads, (char*)value);
    }

  if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, &objects,
                                 cancellable, error))
    goto out;

  g_hash_table_iter_init (&hash_iter, objects);

  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      GVariant *serialized_key = key;
      const char *checksum;
      OstreeObjectType objtype;
      guint64 commit_timestamp;
      g_autoptr(GVariant) commit = NULL;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      if (objtype != OSTREE_OBJECT_TYPE_COMMIT)
        continue;

      if (g_hash_table_contains (ref_heads, checksum))
        continue;

      if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum,
                                     &commit, error))
        goto out;

      commit_timestamp = ostree_commit_get_timestamp (commit);
      if (commit_timestamp < ts.tv_sec)
        {
          if (opt_static_deltas_only)
            {
              if(!ostree_repo_prune_static_deltas (repo, checksum, cancellable, error))
                goto out;
            }
          else
            {
              if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error))
                goto out;
            }
        }
    }

  ret = TRUE;

 out:
  return ret;
}
Esempio n. 5
0
gboolean
ostree_builtin_fsck (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  GOptionContext *context;
  GHashTableIter hash_iter;
  gpointer key, value;
  gboolean found_corruption = FALSE;
  gs_unref_hashtable GHashTable *objects = NULL;
  gs_unref_hashtable GHashTable *commits = NULL;

  context = g_option_context_new ("- Check the repository for consistency");
  g_option_context_add_main_entries (context, options, NULL);

  if (!g_option_context_parse (context, &argc, &argv, error))
    goto out;

  if (!opt_quiet)
    g_print ("Enumerating objects...\n");

  if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL,
                                 &objects, cancellable, error))
    goto out;

  commits = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
                                   (GDestroyNotify)g_variant_unref, NULL);
  
  g_hash_table_iter_init (&hash_iter, objects);

  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      GVariant *serialized_key = key;
      const char *checksum;
      OstreeObjectType objtype;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
        g_hash_table_insert (commits, g_variant_ref (serialized_key), serialized_key);
    }

  g_clear_pointer (&objects, (GDestroyNotify) g_hash_table_unref);

  if (!opt_quiet)
    g_print ("Verifying content integrity of %u commit objects...\n",
             (guint)g_hash_table_size (commits));

  if (!fsck_reachable_objects_from_commits (repo, commits, &found_corruption,
                                            cancellable, error))
    goto out;

  if (found_corruption)
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Repository corruption encountered");
      goto out;
    }

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
Esempio n. 6
0
/**
 * ostree_repo_prune:
 * @self: Repo
 * @flags: Options controlling prune process
 * @depth: Stop traversal after this many iterations (-1 for unlimited)
 * @out_objects_total: (out): Number of objects found
 * @out_objects_pruned: (out): Number of objects deleted
 * @out_pruned_object_size_total: (out): Storage size in bytes of objects deleted
 * @cancellable: Cancellable
 * @error: Error
 *
 * Delete content from the repository.  By default, this function will
 * only delete "orphaned" objects not referred to by any commit.  This
 * can happen during a local commit operation, when we have written
 * content objects but not saved the commit referencing them.
 *
 * However, if %OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY is provided, instead
 * of traversing all commits, only refs will be used.  Particularly
 * when combined with @depth, this is a convenient way to delete
 * history from the repository.
 * 
 * Use the %OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE to just determine
 * statistics on objects that would be deleted, without actually
 * deleting them.
 */
gboolean
ostree_repo_prune (OstreeRepo        *self,
                   OstreeRepoPruneFlags   flags,
                   gint               depth,
                   gint              *out_objects_total,
                   gint              *out_objects_pruned,
                   guint64           *out_pruned_object_size_total,
                   GCancellable      *cancellable,
                   GError           **error)
{
  GHashTableIter hash_iter;
  gpointer key, value;
  g_autoptr(GHashTable) objects = NULL;
  g_autoptr(GHashTable) all_refs = NULL;
  g_autoptr(GHashTable) reachable = NULL;
  gboolean refs_only = flags & OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY;

  reachable = ostree_repo_traverse_new_reachable ();

  /* This original prune API has fixed logic for traversing refs or all commits
   * combined with actually deleting content. The newer backend API just does
   * the deletion.
   */

  if (refs_only)
    {
      if (!ostree_repo_list_refs (self, NULL, &all_refs,
                                  cancellable, error))
        return FALSE;

      g_hash_table_iter_init (&hash_iter, all_refs);

      while (g_hash_table_iter_next (&hash_iter, &key, &value))
        {
          const char *checksum = value;

          g_debug ("Finding objects to keep for commit %s", checksum);
          if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable,
                                                  cancellable, error))
            return FALSE;
        }
    }

  if (!ostree_repo_list_objects (self, OSTREE_REPO_LIST_OBJECTS_ALL | OSTREE_REPO_LIST_OBJECTS_NO_PARENTS,
                                 &objects, cancellable, error))
    return FALSE;

  if (!refs_only)
    {
      g_hash_table_iter_init (&hash_iter, objects);
      while (g_hash_table_iter_next (&hash_iter, &key, &value))
        {
          GVariant *serialized_key = key;
          const char *checksum;
          OstreeObjectType objtype;

          ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

          if (objtype != OSTREE_OBJECT_TYPE_COMMIT)
            continue;

          g_debug ("Finding objects to keep for commit %s", checksum);
          if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable,
                                                  cancellable, error))
            return FALSE;
        }
    }

  { OstreeRepoPruneOptions opts = { flags, reachable };
    return repo_prune_internal (self, objects, &opts,
                                out_objects_total, out_objects_pruned,
                                out_pruned_object_size_total, cancellable, error);
  }
}
Esempio n. 7
0
static gboolean
prune_commits_keep_younger_than_date (OstreeRepo *repo, const char *date, GCancellable *cancellable, GError **error)
{
  g_autoptr(GHashTable) objects = NULL;
  GHashTableIter hash_iter;
  gpointer key, value;
  struct timespec ts;
  gboolean ret = FALSE;

  if (!parse_datetime (&ts, date, NULL))
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Could not parse '%s'", date);
      goto out;
    }

  if (!ot_enable_tombstone_commits (repo, error))
    goto out;

  if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, &objects,
                                 cancellable, error))
    goto out;

  g_hash_table_iter_init (&hash_iter, objects);

  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      GVariant *serialized_key = key;
      const char *checksum;
      OstreeObjectType objtype;
      guint64 commit_timestamp;
      g_autoptr(GVariant) commit = NULL;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      if (objtype != OSTREE_OBJECT_TYPE_COMMIT)
        continue;

      if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum,
                                     &commit, error))
        goto out;

      commit_timestamp = ostree_commit_get_timestamp (commit);
      if (commit_timestamp < ts.tv_sec)
        {
          if (opt_static_deltas_only)
            {
              if(!ostree_repo_prune_static_deltas (repo, checksum, cancellable, error))
                goto out;
            }
          else
            {
              if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error))
                goto out;
            }
        }
    }

  ret = TRUE;

 out:
  return ret;
}