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; }
/** * 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; }