예제 #1
0
static gboolean
ot_static_delta_builtin_list (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  g_autoptr(GPtrArray) delta_names = NULL;
  guint i;
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;

  context = g_option_context_new ("LIST - list static delta files");

  if (!ostree_option_context_parse (context, list_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
    goto out;

  if (!ostree_repo_list_static_delta_names (repo, &delta_names, cancellable, error))
    goto out;
      
  if (delta_names->len == 0)
    {
      g_print ("(No static deltas)\n");
    }
  else
    {
      for (i = 0; i < delta_names->len; i++)
        {
          g_print ("%s\n", (char*)delta_names->pdata[i]);
        }
    }

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
예제 #2
0
/**
 * ostree_repo_prune_static_deltas:
 * @self: Repo
 * @commit: (allow-none): ASCII SHA256 checksum for commit, or %NULL for each
 * non existing commit
 * @cancellable: Cancellable
 * @error: Error
 *
 * Prune static deltas, if COMMIT is specified then delete static delta files only
 * targeting that commit; otherwise any static delta of non existing commits are
 * deleted.
 *
 * Locking: exclusive
 */
gboolean
ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit,
                                 GCancellable      *cancellable,
                                 GError           **error)
{
  g_autoptr(OstreeRepoAutoLock) lock =
    _ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
  if (!lock)
    return FALSE;

  g_autoptr(GPtrArray) deltas = NULL;
  if (!ostree_repo_list_static_delta_names (self, &deltas,
                                            cancellable, error))
    return FALSE;

  for (guint i = 0; i < deltas->len; i++)
    {
      const char *deltaname = deltas->pdata[i];
      const char *dash = strchr (deltaname, '-');
      const char *to = NULL;
      g_autofree char *from = NULL;

      if (!dash)
        {
          to = deltaname;
        }
      else
        {
          from = g_strndup (deltaname, dash - deltaname);
          to = dash + 1;
        }

      if (commit)
        {
          if (g_strcmp0 (to, commit))
            continue;
        }
      else
        {
          gboolean have_commit;
          if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT,
                                       to, &have_commit,
                                       cancellable, error))
            return FALSE;

          if (have_commit)
            continue;
        }

      g_debug ("Trying to prune static delta %s", deltaname);
      g_autofree char *deltadir = _ostree_get_relative_static_delta_path (from, to, NULL);
      if (!glnx_shutil_rm_rf_at (self->repo_dir_fd, deltadir,
                                 cancellable, error))
        return FALSE;
    }

  return TRUE;
}
static gboolean
generate_all_deltas (OstreeRepo   *repo,
                     GPtrArray   **unwanted_deltas,
                     GCancellable *cancellable,
                     GError      **error)
{
  g_autoptr(GHashTable) all_refs = NULL;
  g_autoptr(GHashTable) all_deltas_hash = NULL;
  g_autoptr(GHashTable) wanted_deltas_hash = NULL;
  g_autoptr(GPtrArray) all_deltas = NULL;
  int i;
  GHashTableIter iter;
  gpointer key, value;
  g_autoptr(GVariantBuilder) parambuilder = NULL;
  g_autoptr(GVariant) params = NULL;
  int n_spawned_delta_generate = 0;
  g_autoptr(GMainContextPopDefault) context = NULL;
  g_autoptr(GPtrArray) ignore_patterns = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pattern_spec_free);

  g_print ("Generating static deltas\n");

  parambuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
  /* Fall back for 1 meg files */
  g_variant_builder_add (parambuilder, "{sv}",
                         "min-fallback-size", g_variant_new_uint32 (1));
  params = g_variant_ref_sink (g_variant_builder_end (parambuilder));

  if (!ostree_repo_list_static_delta_names (repo, &all_deltas,
                                            cancellable, error))
    return FALSE;

  wanted_deltas_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);

  all_deltas_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
  for (i = 0; i < all_deltas->len; i++)
    g_hash_table_insert (all_deltas_hash,
                         g_strdup (g_ptr_array_index (all_deltas, i)),
                         NULL);

  if (!ostree_repo_list_refs (repo, NULL, &all_refs,
                              cancellable, error))
    return FALSE;

  context = flatpak_main_context_new_default ();

  if (opt_static_delta_ignore_refs != NULL)
    {
      for (i = 0; opt_static_delta_ignore_refs[i] != NULL; i++)
        g_ptr_array_add (ignore_patterns,
                         g_pattern_spec_new (opt_static_delta_ignore_refs[i]));
    }

  g_hash_table_iter_init (&iter, all_refs);
  while (g_hash_table_iter_next (&iter, &key, &value))
    {
      const char *ref = key;
      const char *commit = value;
      g_autoptr(GVariant) variant = NULL;
      g_autoptr(GVariant) parent_variant = NULL;
      g_autofree char *parent_commit = NULL;
      g_autofree char *grandparent_commit = NULL;
      gboolean ignore_ref = FALSE;

      if (g_str_has_prefix (ref, "app/") || g_str_has_prefix (ref, "runtime/"))
        {
          g_auto(GStrv) parts = g_strsplit (ref, "/", 4);

          for (i = 0; i < ignore_patterns->len; i++)
            {
              GPatternSpec *pattern = g_ptr_array_index(ignore_patterns, i);
              if (g_pattern_match_string (pattern, parts[1]))
                {
                  ignore_ref = TRUE;
                  break;
                }
            }

        }
      else if (g_str_has_prefix (ref, "appstream/"))
        {
          /* Old appstream branch deltas poorly, and most users handle the new format */
          ignore_ref = TRUE;
        }
      else if (g_str_has_prefix (ref, "appstream2/"))
        {
          /* Always delta this */
          ignore_ref = FALSE;
        }
      else
        {
          /* Ignore unknown ref types */
          ignore_ref = FALSE;
        }

      if (ignore_ref)
        {
          g_debug ("Ignoring deltas for ref %s", ref);
          continue;
        }

      if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit,
                                     &variant, NULL))
        {
          g_warning ("Couldn't load commit %s", commit);
          continue;
        }

      /* From empty */
      if (!g_hash_table_contains (all_deltas_hash, commit))
        {
          if (!spawn_delta_generation (context, &n_spawned_delta_generate, repo, params,
                                       ref, NULL, commit,
                                       error))
            return FALSE;
        }

      /* Mark this one as wanted */
      g_hash_table_insert (wanted_deltas_hash, g_strdup (commit), GINT_TO_POINTER (1));

      parent_commit = ostree_commit_get_parent (variant);

      if (parent_commit != NULL &&
          !ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, parent_commit,
                                     &parent_variant, NULL))
        {
          g_warning ("Couldn't load parent commit %s", parent_commit);
          continue;
        }

      /* From parent */
      if (parent_variant != NULL)
        {
          g_autofree char *from_parent = g_strdup_printf ("%s-%s", parent_commit, commit);

          if (!g_hash_table_contains (all_deltas_hash, from_parent))
            {
              if (!spawn_delta_generation (context, &n_spawned_delta_generate, repo, params,
                                           ref, parent_commit, commit,
                                           error))
                return FALSE;
            }

          /* Mark parent-to-current as wanted */
          g_hash_table_insert (wanted_deltas_hash, g_strdup (from_parent), GINT_TO_POINTER (1));

          /* We also want to keep around the parent and the grandparent-to-parent deltas
           * because otherwise these will be deleted immediately which may cause a race if
           * someone is currently downloading them.
           * However, there is no need to generate these if they don't exist.
           */

          g_hash_table_insert (wanted_deltas_hash, g_strdup (parent_commit), GINT_TO_POINTER (1));
          grandparent_commit = ostree_commit_get_parent (parent_variant);
          if (grandparent_commit != NULL)
            g_hash_table_insert (wanted_deltas_hash,
                                 g_strdup_printf ("%s-%s", grandparent_commit, parent_commit),
                                 GINT_TO_POINTER (1));
        }
    }

  while (n_spawned_delta_generate > 0)
    g_main_context_iteration (context, TRUE);

  *unwanted_deltas = g_ptr_array_new_with_free_func (g_free);
  for (i = 0; i < all_deltas->len; i++)
    {
      const char *delta = g_ptr_array_index (all_deltas, i);
      if (!g_hash_table_contains (wanted_deltas_hash, delta))
        g_ptr_array_add (*unwanted_deltas, g_strdup (delta));
    }

  return TRUE;
}