Add internal API for list_unused_refs() and add _with_options()

In a few places we are using
flatpak_installation_list_unused_refs() and then only
using the ref strings not the FlatpakInstalledRef objects, so the
resources used to construct those objects are wasted. Add a flatpak_dir_
function to be used internally instead. One day we will figure out how
to make flatpak-dir.c less of a wilderness.

This also adds the flatpak_installation_list_unused_refs_with_options()
verion that has extended features.
This commit is contained in:
Phaedrus Leeds
2020-09-22 17:59:27 -07:00
committed by Alexander Larsson
parent b6bd4472c5
commit 44aa0d6830
4 changed files with 412 additions and 130 deletions

View File

@@ -1019,4 +1019,12 @@ gboolean flatpak_dir_delete_mirror_refs (FlatpakDir *self,
GCancellable *cancellable,
GError **error);
char ** flatpak_dir_list_unused_refs_with_options (FlatpakDir *self,
const char *arch,
GHashTable *metadata_injection,
const char * const *refs_to_exclude,
gboolean filter_by_eol,
GCancellable *cancellable,
GError **error);
#endif /* __FLATPAK_DIR_H__ */

View File

@@ -14628,3 +14628,345 @@ flatpak_dir_delete_mirror_refs (FlatpakDir *self,
return TRUE;
}
static gboolean
maybe_get_metakey_and_origin (FlatpakDir *dir,
const char *ref,
GHashTable *metadata_injection,
GKeyFile **out_metakey,
char **out_origin)
{
g_autoptr(GKeyFile) metakey = NULL;
g_autofree char *origin = NULL;
origin = flatpak_dir_get_origin (dir, ref, NULL, NULL);
if (origin == NULL)
return FALSE;
if (metadata_injection != NULL)
metakey = g_hash_table_lookup (metadata_injection, ref);
if (metakey != NULL)
g_key_file_ref (metakey);
else
{
g_autoptr(FlatpakDeploy) deploy = flatpak_dir_load_deployed (dir, ref, NULL, NULL, NULL);
if (deploy == NULL)
return FALSE;
metakey = flatpak_deploy_get_metadata (deploy);
}
if (out_metakey)
*out_metakey = g_steal_pointer (&metakey);
if (out_origin)
*out_origin = g_steal_pointer (&origin);
return TRUE;
}
static void
find_used_related_refs (FlatpakDir *dir,
FlatpakDir *system_dir, /* nullable */
GHashTable *used_refs,
GHashTable *metadata_injection,
const char *ref,
GKeyFile *metakey,
const char *origin)
{
g_autoptr(GPtrArray) related = NULL;
int i;
if (system_dir == NULL)
g_hash_table_add (used_refs, g_strdup (ref));
/* If @system_dir is non-NULL, that means @ref exists in @dir but we should
* look in @system_dir for related things */
if (system_dir != NULL)
related = flatpak_dir_find_local_related_for_metadata (system_dir, ref, origin, metakey, NULL, NULL);
else
related = flatpak_dir_find_local_related_for_metadata (dir, ref, origin, metakey, NULL, NULL);
if (related == NULL)
return;
for (i = 0; i < related->len; i++)
{
FlatpakRelated *rel = g_ptr_array_index (related, i);
/* Check if this related ref is present in @dir, which implies the one
* in @system_dir is NOT the one being used. */
if (system_dir != NULL)
{
g_autoptr(FlatpakDeploy) user_related_deploy = flatpak_dir_load_deployed (dir, rel->ref, NULL, NULL, NULL);
if (user_related_deploy != NULL)
continue;
}
if (!rel->auto_prune && !g_hash_table_contains (used_refs, rel->ref))
{
g_autofree char *related_origin = NULL;
g_autoptr(GKeyFile) related_metakey = NULL;
g_hash_table_add (used_refs, g_strdup (rel->ref));
if (system_dir != NULL &&
maybe_get_metakey_and_origin (system_dir, rel->ref, NULL, &related_metakey, &related_origin))
find_used_related_refs (system_dir, NULL, used_refs, NULL,
rel->ref, related_metakey, related_origin);
else if (maybe_get_metakey_and_origin (dir, rel->ref, metadata_injection, &related_metakey, &related_origin))
find_used_related_refs (dir, NULL, used_refs, metadata_injection,
rel->ref, related_metakey, related_origin);
}
}
}
static void
find_used_refs_for_apps (FlatpakDir *dir,
FlatpakDir *system_dir, /* nullable */
char **app_refs,
const char *arch,
GHashTable *metadata_injection,
GHashTable *used_runtimes,
GHashTable *used_refs)
{
/* Check for related refs and runtimes and sdks for each app in @app_refs.
* The apps exist in @dir but if @system_dir is set that's where we check for
* the related/runtime/sdk, and if @system_dir is set we check that said
* runtime does not exist in @dir, so the one in @system_dir is probably the
* one being used. */
int i;
for (i = 0; app_refs[i] != NULL; i++)
{
const char *ref = app_refs[i];
g_autofree char *origin = NULL;
g_autofree char *runtime = NULL;
g_autofree char *sdk = NULL;
g_autoptr(GKeyFile) metakey = NULL;
g_auto(GStrv) parts = g_strsplit (ref, "/", -1);
if (arch != NULL && strcmp (parts[2], arch) != 0)
continue;
if (!maybe_get_metakey_and_origin (dir, ref, metadata_injection, &metakey, &origin))
continue;
find_used_related_refs (dir, system_dir, used_refs, metadata_injection, ref, metakey, origin);
runtime = g_key_file_get_string (metakey, "Application", "runtime", NULL);
if (runtime)
{
g_autoptr(FlatpakDeploy) runtime_deploy = NULL;
g_autoptr(FlatpakDeploy) user_runtime_deploy = NULL;
if (system_dir != NULL)
{
g_autofree char *runtime_ref = g_strconcat ("runtime/", runtime, NULL);
runtime_deploy = flatpak_dir_load_deployed (system_dir, runtime_ref, NULL, NULL, NULL);
user_runtime_deploy = flatpak_dir_load_deployed (dir, runtime_ref, NULL, NULL, NULL);
}
if (system_dir == NULL || (runtime_deploy != NULL && user_runtime_deploy == NULL))
g_hash_table_add (used_runtimes, g_steal_pointer (&runtime));
}
sdk = g_key_file_get_string (metakey, "Application", "sdk", NULL);
if (sdk)
{
g_autoptr(FlatpakDeploy) sdk_deploy = NULL;
g_autoptr(FlatpakDeploy) user_sdk_deploy = NULL;
if (system_dir != NULL)
{
g_autofree char *sdk_ref = g_strconcat ("runtime/", sdk, NULL);
sdk_deploy = flatpak_dir_load_deployed (system_dir, sdk_ref, NULL, NULL, NULL);
user_sdk_deploy = flatpak_dir_load_deployed (dir, sdk_ref, NULL, NULL, NULL);
}
if (system_dir == NULL || (sdk_deploy != NULL && user_sdk_deploy == NULL))
g_hash_table_add (used_runtimes, g_steal_pointer (&sdk));
}
}
}
static void
find_used_refs_for_runtimes (FlatpakDir *dir,
FlatpakDir *system_dir,
GHashTable *metadata_injection,
GHashTable *runtimes,
GHashTable *used_refs)
{
/* For each runtime in @runtimes, if it's in @dir, add the related refs and
* sdk to @used_refs. If @system_dir is set that's where we look for
* related refs and sdk related refs; the sdk could be in either dir. */
GLNX_HASH_TABLE_FOREACH (runtimes, const char *, runtime)
{
g_autofree char *runtime_ref = g_strconcat ("runtime/", runtime, NULL);
g_autofree char *origin = NULL;
g_autofree char *sdk = NULL;
g_autoptr(GKeyFile) metakey = NULL;
if (!maybe_get_metakey_and_origin (dir, runtime_ref, metadata_injection,
&metakey, &origin))
continue;
find_used_related_refs (dir, system_dir, used_refs, metadata_injection,
runtime_ref, metakey, origin);
sdk = g_key_file_get_string (metakey, "Runtime", "sdk", NULL);
if (sdk)
{
g_autofree char *sdk_ref = g_strconcat ("runtime/", sdk, NULL);
g_autofree char *sdk_origin = NULL;
g_autoptr(GKeyFile) sdk_metakey = NULL;
if (maybe_get_metakey_and_origin (dir, sdk_ref, metadata_injection,
&sdk_metakey, &sdk_origin))
find_used_related_refs (dir, system_dir, used_refs, metadata_injection,
sdk_ref, sdk_metakey, sdk_origin);
if (system_dir != NULL && sdk_origin == NULL)
{
g_autofree char *system_sdk_origin = NULL;;
g_autoptr(GKeyFile) system_sdk_metakey = NULL;
if (maybe_get_metakey_and_origin (system_dir, sdk_ref, NULL,
&system_sdk_metakey, &system_sdk_origin))
find_used_related_refs (system_dir, NULL, used_refs, NULL,
sdk_ref, system_sdk_metakey, system_sdk_origin);
}
}
}
}
static void
prune_excluded_refs (char **full_refs,
const char * const *refs_to_exclude)
{
guint len = g_strv_length (full_refs);
for (guint i = 0; i < len; i++)
{
if (g_strv_contains (refs_to_exclude, full_refs[i]))
{
g_free (full_refs[i]);
full_refs[i] = full_refs[len - 1];
full_refs[len - 1] = NULL;
len--;
i--;
}
}
}
/* See the documentation for
* flatpak_installation_list_unused_refs_with_options().
* The returned pointer array is transfer full. */
char **
flatpak_dir_list_unused_refs_with_options (FlatpakDir *self,
const char *arch,
GHashTable *metadata_injection,
const char * const *refs_to_exclude,
gboolean filter_by_eol,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GHashTable) refs_hash = NULL;
g_autoptr(GPtrArray) refs = NULL;
g_auto(GStrv) app_refs = NULL;
g_auto(GStrv) runtime_refs = NULL;
g_autoptr(GHashTable) used_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_autoptr(GHashTable) used_runtimes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
int i;
if (!flatpak_dir_list_refs (self, "app", &app_refs, cancellable, error))
return NULL;
if (!flatpak_dir_list_refs (self, "runtime", &runtime_refs, cancellable, error))
return NULL;
if (refs_to_exclude != NULL)
{
prune_excluded_refs (app_refs, refs_to_exclude);
prune_excluded_refs (runtime_refs, refs_to_exclude);
}
refs_hash = g_hash_table_new (g_str_hash, g_str_equal);
refs = g_ptr_array_new_with_free_func (g_free);
/* For each app, note the runtime, sdk, and related refs */
find_used_refs_for_apps (self, NULL, app_refs, arch, metadata_injection, used_runtimes, used_refs);
/* If @self is a system installation, also check the per-user installation
* for any apps there using runtimes in the system installation or runtimes
* there with sdks or extensions in the system installation. Only do so if
* the per-user installation exists; it wouldn't make sense to create it here
* if not.
*/
if (!flatpak_dir_is_user (self))
{
g_autoptr(GFile) user_base_dir = flatpak_get_user_base_dir_location ();
if (g_file_query_exists (user_base_dir, cancellable))
{
g_autoptr(FlatpakDir) user_dir = flatpak_dir_get_user ();
g_auto(GStrv) user_app_refs = NULL;
g_auto(GStrv) user_runtime_refs = NULL;
g_autoptr(GHashTable) user_runtimes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
if (!flatpak_dir_list_refs (user_dir, "app", &user_app_refs, cancellable, error))
return NULL;
/* metadata_injection is NULL because it's not for this installation */
find_used_refs_for_apps (user_dir, self, user_app_refs, arch, NULL, used_runtimes, used_refs);
if (!flatpak_dir_list_refs (user_dir, "runtime", &user_runtime_refs, cancellable, error))
return NULL;
for (i = 0; user_runtime_refs[i] != NULL; i++)
{
const char *ref = user_runtime_refs[i];
g_assert (g_str_has_prefix (ref, "runtime/"));
g_hash_table_add (user_runtimes, (char *)ref + strlen ("runtime/"));
}
find_used_refs_for_runtimes (user_dir, self, NULL, user_runtimes, used_refs);
}
}
find_used_refs_for_runtimes (self, NULL, metadata_injection, used_runtimes, used_refs);
for (i = 0; runtime_refs[i] != NULL; i++)
{
const char *ref = runtime_refs[i];
g_auto(GStrv) parts = g_strsplit (ref, "/", -1);
if (arch != NULL && strcmp (parts[2], arch) != 0)
continue;
if (flatpak_dir_ref_is_pinned (self, ref))
{
g_debug ("Ref %s is pinned, considering as used", ref);
continue;
}
if (!g_hash_table_contains (used_refs, ref) &&
g_hash_table_add (refs_hash, (gpointer) ref))
{
if (!filter_by_eol)
g_ptr_array_add (refs, g_strdup (ref));
else
{
g_autoptr(GBytes) deploy_data = NULL;
deploy_data = flatpak_dir_get_deploy_data (self, ref, FLATPAK_DEPLOY_VERSION_ANY,
cancellable, error);
if (deploy_data == NULL)
return NULL;
if (flatpak_deploy_data_get_eol (deploy_data) == NULL &&
flatpak_deploy_data_get_eol_rebase (deploy_data) == NULL)
{
g_debug ("Ref %s is not EOL, considering as used", ref);
continue;
}
else
g_ptr_array_add (refs, g_strdup (ref));
}
}
}
g_ptr_array_add (refs, NULL);
return (char **)g_ptr_array_free (g_steal_pointer (&refs), FALSE);
}

View File

@@ -990,13 +990,13 @@ transaction_ready (FlatpakTransaction *transaction,
GPtrArray *op_related_to_ops = flatpak_transaction_operation_get_related_to_ops (op); /* (element-type FlatpakTransactionOperation) */
FlatpakTransactionOperationType type = flatpak_transaction_operation_get_operation_type (op);
/* There is currently no way for a set of updates to lead to an
* uninstall, but check anyway.
/* There may be an uninstall op if a runtime will now be considered
* unused after the updates
*/
if (type == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
{
const char *ref = flatpak_transaction_operation_get_ref (op);
g_warning ("Update transaction unexpectedly wants to uninstall %s", ref);
g_debug ("Update transaction wants to uninstall %s", ref);
continue;
}
@@ -2882,39 +2882,6 @@ flatpak_installation_run_triggers (FlatpakInstallation *self,
return flatpak_dir_run_triggers (dir, cancellable, error);
}
static void
find_used_refs (FlatpakDir *dir,
GHashTable *used_refs,
const char *ref,
const char *origin)
{
g_autoptr(GPtrArray) related = NULL;
int i;
g_hash_table_add (used_refs, g_strdup (ref));
related = flatpak_dir_find_local_related (dir, ref, origin, TRUE, NULL, NULL);
if (related == NULL)
return;
for (i = 0; i < related->len; i++)
{
FlatpakRelated *rel = g_ptr_array_index (related, i);
if (!rel->auto_prune && !g_hash_table_contains (used_refs, rel->ref))
{
g_autofree char *related_origin = NULL;
g_hash_table_add (used_refs, g_strdup (rel->ref));
related_origin = flatpak_dir_get_origin (dir, rel->ref, NULL, NULL);
if (related_origin != NULL)
find_used_refs (dir, used_refs, rel->ref, related_origin);
}
}
}
/**
* flatpak_installation_list_unused_refs:
* @self: a #FlatpakInstallation
@@ -2939,111 +2906,70 @@ flatpak_installation_list_unused_refs (FlatpakInstallation *self,
const char *arch,
GCancellable *cancellable,
GError **error)
{
return flatpak_installation_list_unused_refs_with_options (self, arch, NULL, NULL, cancellable, error);
}
/**
* flatpak_installation_list_unused_refs_with_options:
* @self: a #FlatpakInstallation
* @arch: (nullable): if non-%NULL, the architecture of refs to collect
* @metadata_injection: (nullable): if non-%NULL, a #GHashTable mapping refs to
* #GKeyFile objects, which when available will
* be used instead of installed metadata
* @options: (nullable): if non-%NULL, a GVariant a{sv} with an extensible set
* of options
* @cancellable: (nullable): a #GCancellable
* @error: return location for a #GError
*
* Like flatpak_installation_list_unused_refs() but supports an extensible set
* of options as well as an @metadata_injection parameter. The following are
* currently defined:
*
* * exclude-refs (as): Act as if these refs are not installed even if they
* are when determining the set of unused refs
* * filter-by-eol (b): Only return refs as unused if they are End-Of-Life.
* Note that if this option is combined with other filters (of which there
* are none currently) non-EOL refs may also be returned.
*
* Returns: (transfer container) (element-type FlatpakInstalledRef): a GPtrArray of
* #FlatpakInstalledRef instances
*
* Since: 1.9.1
*/
GPtrArray *
flatpak_installation_list_unused_refs_with_options (FlatpakInstallation *self,
const char *arch,
GHashTable *metadata_injection,
GVariant *options,
GCancellable *cancellable,
GError **error)
{
g_autoptr(FlatpakDir) dir = NULL;
g_autoptr(GHashTable) refs_hash = NULL;
g_autoptr(GPtrArray) refs = NULL;
g_auto(GStrv) app_refs = NULL;
g_auto(GStrv) runtime_refs = NULL;
g_autoptr(GHashTable) used_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_autoptr(GHashTable) used_runtimes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
int i;
g_auto(GStrv) refs_strv = NULL;
g_autofree char **refs_to_exclude = NULL;
gboolean filter_by_eol = FALSE;
if (options)
{
(void) g_variant_lookup (options, "exclude-refs", "^a&s", &refs_to_exclude);
(void) g_variant_lookup (options, "filter-by-eol", "b", &filter_by_eol);
}
dir = flatpak_installation_get_dir (self, error);
if (dir == NULL)
return NULL;
if (!flatpak_dir_list_refs (dir, "app", &app_refs, cancellable, error))
refs_strv = flatpak_dir_list_unused_refs_with_options (dir, arch, metadata_injection,
(const char * const *)refs_to_exclude, filter_by_eol,
cancellable, error);
if (refs_strv == NULL)
return NULL;
if (!flatpak_dir_list_refs (dir, "runtime", &runtime_refs, cancellable, error))
return NULL;
refs_hash = g_hash_table_new (g_str_hash, g_str_equal);
refs = g_ptr_array_new_with_free_func (g_object_unref);
for (i = 0; app_refs[i] != NULL; i++)
{
const char *ref = app_refs[i];
g_autoptr(FlatpakDeploy) deploy = NULL;
g_autofree char *origin = NULL;
g_autofree char *runtime = NULL;
g_autofree char *sdk = NULL;
g_autoptr(GKeyFile) metakey = NULL;
g_auto(GStrv) parts = g_strsplit (ref, "/", -1);
if (arch != NULL && strcmp (parts[2], arch) != 0)
continue;
deploy = flatpak_dir_load_deployed (dir, ref, NULL, NULL, NULL);
if (deploy == NULL)
continue;
origin = flatpak_dir_get_origin (dir, ref, NULL, NULL);
if (origin == NULL)
continue;
find_used_refs (dir, used_refs, ref, origin);
metakey = flatpak_deploy_get_metadata (deploy);
runtime = g_key_file_get_string (metakey, "Application", "runtime", NULL);
if (runtime)
g_hash_table_add (used_runtimes, g_steal_pointer (&runtime));
sdk = g_key_file_get_string (metakey, "Application", "sdk", NULL);
if (sdk)
g_hash_table_add (used_runtimes, g_steal_pointer (&sdk));
}
GLNX_HASH_TABLE_FOREACH (used_runtimes, const char *, runtime)
{
g_autofree char *runtime_ref = g_strconcat ("runtime/", runtime, NULL);
g_autoptr(FlatpakDeploy) deploy = NULL;
g_autofree char *origin = NULL;
g_autofree char *sdk = NULL;
g_autoptr(GKeyFile) metakey = NULL;
deploy = flatpak_dir_load_deployed (dir, runtime_ref, NULL, NULL, NULL);
if (deploy == NULL)
continue;
origin = flatpak_dir_get_origin (dir, runtime_ref, NULL, NULL);
if (origin == NULL)
continue;
find_used_refs (dir, used_refs, runtime_ref, origin);
metakey = flatpak_deploy_get_metadata (deploy);
sdk = g_key_file_get_string (metakey, "Runtime", "sdk", NULL);
if (sdk)
{
g_autofree char *sdk_ref = g_strconcat ("runtime/", sdk, NULL);
g_autofree char *sdk_origin = flatpak_dir_get_origin (dir, sdk_ref, NULL, NULL);
if (sdk_origin)
find_used_refs (dir, used_refs, sdk_ref, sdk_origin);
}
}
for (i = 0; runtime_refs[i] != NULL; i++)
{
const char *ref = runtime_refs[i];
g_auto(GStrv) parts = g_strsplit (ref, "/", -1);
if (arch != NULL && strcmp (parts[2], arch) != 0)
continue;
if (flatpak_dir_ref_is_pinned (dir, ref))
{
g_debug ("Ref %s is pinned, considering as used", ref);
continue;
}
if (!g_hash_table_contains (used_refs, ref))
{
if (g_hash_table_add (refs_hash, (gpointer) ref))
g_ptr_array_add (refs, get_ref (dir, ref, NULL, NULL));
}
}
for (char **iter = refs_strv; iter && *iter; iter++)
g_ptr_array_add (refs, get_ref (dir, *iter, NULL, NULL));
return g_steal_pointer (&refs);
}

View File

@@ -245,6 +245,12 @@ FLATPAK_EXTERN GPtrArray *flatpak_installation_list_unused_refs (Flatp
const char *arch,
GCancellable *cancellable,
GError **error);
FLATPAK_EXTERN GPtrArray *flatpak_installation_list_unused_refs_with_options (FlatpakInstallation *self,
const char *arch,
GHashTable *metadata_injection,
GVariant *options,
GCancellable *cancellable,
GError **error);
FLATPAK_EXTERN GPtrArray *flatpak_installation_list_pinned_refs (FlatpakInstallation *self,
const char *arch,
GCancellable *cancellable,