summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhaedrus Leeds <matthew.leeds@endlessm.com>2020-08-07 18:49:32 -0700
committerPhaedrus Leeds <matthew.leeds@endlessm.com>2020-08-31 09:11:14 -0700
commite6228a6e4c1551e6345735d537858e2809b2b92e (patch)
tree2feba257d9b6b97cd0e37aaf671d3057a3f83ddf
parenta70b7a76282ca4e6a8ef797c20cb78a75f09ed24 (diff)
downloadflatpak-e6228a6e4c1551e6345735d537858e2809b2b92e.tar.gz
installation: Check user installation in list_unused_refs()
Currently flatpak_installation_list_unused_refs() only considers one installation when determining which runtimes are unused. But apps in a per-user installation can use a runtime in the system installation, so check for this scenario as well. Also check related scenarios such as a user-installed runtime with a system-installed extension, though that seems less likely to happen in practice. I realize now after writing this that it's also possible to have runtime dependencies across system installations (see flatpak_transaction_add_default_dependency_sources()). So we may need a more generalized mechanism here eventually. Note that this is limited in only being able to check the current user's installation. On multi-user systems there may be another user sharing the system installation. A unit test is included, though it doesn't cover every edge case.
-rw-r--r--common/flatpak-dir-private.h1
-rw-r--r--common/flatpak-dir.c10
-rw-r--r--common/flatpak-installation.c236
-rw-r--r--tests/testlibrary.c83
4 files changed, 267 insertions, 63 deletions
diff --git a/common/flatpak-dir-private.h b/common/flatpak-dir-private.h
index 7a6d7080..543e94a0 100644
--- a/common/flatpak-dir-private.h
+++ b/common/flatpak-dir-private.h
@@ -963,6 +963,7 @@ GPtrArray * flatpak_dir_find_local_related_for_metadata (FlatpakDir *self,
GError **error);
GPtrArray * flatpak_dir_find_local_related (FlatpakDir *self,
const char *ref,
+ FlatpakDir *ref_dir,
const char *remote_name,
gboolean deployed,
GCancellable *cancellable,
diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c
index 492f1bcb..5d56a87d 100644
--- a/common/flatpak-dir.c
+++ b/common/flatpak-dir.c
@@ -14124,6 +14124,7 @@ flatpak_dir_find_local_related_for_metadata (FlatpakDir *self,
GPtrArray *
flatpak_dir_find_local_related (FlatpakDir *self,
const char *ref,
+ FlatpakDir *ref_dir,
const char *remote_name,
gboolean deployed,
GCancellable *cancellable,
@@ -14140,9 +14141,14 @@ flatpak_dir_find_local_related (FlatpakDir *self,
if (!flatpak_dir_ensure_repo (self, cancellable, error))
return NULL;
+ /* If @ref_dir is non-NULL it will be used for getting the metadata for @ref
+ * but not for finding related refs */
+ if (ref_dir != NULL && !flatpak_dir_ensure_repo (ref_dir, cancellable, error))
+ return NULL;
+
if (deployed)
{
- deploy_dir = flatpak_dir_get_if_deployed (self, ref, NULL, cancellable);
+ deploy_dir = flatpak_dir_get_if_deployed (ref_dir ? ref_dir : self, ref, NULL, cancellable);
if (deploy_dir == NULL)
{
g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED,
@@ -14165,7 +14171,7 @@ flatpak_dir_find_local_related (FlatpakDir *self,
}
else
{
- g_autoptr(GVariant) commit_data = flatpak_dir_read_latest_commit (self, remote_name, ref, &checksum, NULL, NULL);
+ g_autoptr(GVariant) commit_data = flatpak_dir_read_latest_commit (ref_dir ? ref_dir : self, remote_name, ref, &checksum, NULL, NULL);
if (commit_data)
{
g_autoptr(GVariant) commit_metadata = g_variant_get_child_value (commit_data, 0);
diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c
index f18622fb..89437934 100644
--- a/common/flatpak-installation.c
+++ b/common/flatpak-installation.c
@@ -2737,7 +2737,7 @@ flatpak_installation_list_installed_related_refs_sync (FlatpakInstallation *self
if (dir == NULL)
return NULL;
- related = flatpak_dir_find_local_related (dir, ref, remote_name, TRUE,
+ related = flatpak_dir_find_local_related (dir, ref, NULL, remote_name, TRUE,
cancellable, error);
if (related == NULL)
return NULL;
@@ -2884,17 +2884,25 @@ flatpak_installation_run_triggers (FlatpakInstallation *self,
static void
-find_used_refs (FlatpakDir *dir,
- GHashTable *used_refs,
- const char *ref,
- const char *origin)
+find_used_related_refs (FlatpakDir *dir,
+ FlatpakDir *system_dir, /* nullable */
+ 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));
+ 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 (system_dir, ref, dir, origin, TRUE, NULL, NULL);
+ else
+ related = flatpak_dir_find_local_related (dir, ref, NULL, origin, TRUE, NULL, NULL);
- related = flatpak_dir_find_local_related (dir, ref, origin, TRUE, NULL, NULL);
if (related == NULL)
return;
@@ -2902,15 +2910,142 @@ find_used_refs (FlatpakDir *dir,
{
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_hash_table_add (used_refs, g_strdup (rel->ref));
- related_origin = flatpak_dir_get_origin (dir, rel->ref, NULL, NULL);
+ related_origin = flatpak_dir_get_origin (system_dir ? system_dir : dir, rel->ref, NULL, NULL);
if (related_origin != NULL)
- find_used_refs (dir, used_refs, rel->ref, related_origin);
+ find_used_related_refs (system_dir ? system_dir : dir, NULL, used_refs, rel->ref, related_origin);
+ }
+ }
+}
+
+static void
+find_used_refs_for_apps (FlatpakDir *dir,
+ FlatpakDir *system_dir, /* nullable */
+ char **app_refs,
+ const char *arch,
+ 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_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_related_refs (dir, system_dir, used_refs, ref, origin);
+
+ metakey = flatpak_deploy_get_metadata (deploy);
+ 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 *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_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_related_refs (dir, system_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_related_refs (dir, system_dir, used_refs, sdk_ref, sdk_origin);
+
+ if (system_dir != NULL && sdk_origin == NULL)
+ {
+ g_autofree char *system_sdk_origin = flatpak_dir_get_origin (system_dir, sdk_ref, NULL, NULL);
+ if (system_sdk_origin)
+ find_used_related_refs (system_dir, NULL, used_refs, sdk_ref, sdk_origin);
+ }
}
}
}
@@ -2962,67 +3097,46 @@ flatpak_installation_list_unused_refs (FlatpakInstallation *self,
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++)
+ /* For each app, note the runtime, sdk, and related refs */
+ find_used_refs_for_apps (dir, NULL, app_refs, arch, 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 (dir))
{
- 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);
+ g_autoptr(GFile) user_base_dir = flatpak_get_user_base_dir_location ();
- if (arch != NULL && strcmp (parts[2], arch) != 0)
- continue;
+ 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);
- deploy = flatpak_dir_load_deployed (dir, ref, NULL, NULL, NULL);
- if (deploy == NULL)
- continue;
+ if (!flatpak_dir_list_refs (user_dir, "app", &user_app_refs, cancellable, error))
+ return NULL;
- origin = flatpak_dir_get_origin (dir, ref, NULL, NULL);
- if (origin == NULL)
- continue;
+ find_used_refs_for_apps (user_dir, dir, user_app_refs, arch, used_runtimes, used_refs);
- find_used_refs (dir, used_refs, ref, origin);
+ if (!flatpak_dir_list_refs (user_dir, "runtime", &user_runtime_refs, cancellable, error))
+ return NULL;
- 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));
+ 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/"));
+ }
- sdk = g_key_file_get_string (metakey, "Application", "sdk", NULL);
- if (sdk)
- g_hash_table_add (used_runtimes, g_steal_pointer (&sdk));
+ find_used_refs_for_runtimes (user_dir, dir, user_runtimes, used_refs);
+ }
}
- 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);
- }
- }
+ find_used_refs_for_runtimes (dir, NULL, used_runtimes, used_refs);
for (i = 0; runtime_refs[i] != NULL; i++)
{
diff --git a/tests/testlibrary.c b/tests/testlibrary.c
index 1f760c6e..8b1b0c02 100644
--- a/tests/testlibrary.c
+++ b/tests/testlibrary.c
@@ -4331,6 +4331,88 @@ test_installation_unused_refs_excludes_pins (void)
g_assert_cmpint (refs->len, ==, 0);
}
+static void
+test_installation_unused_refs_across_installations (void)
+{
+ g_autoptr(FlatpakInstallation) system_inst = NULL;
+ g_autoptr(FlatpakInstallation) user_inst = NULL;
+ g_autoptr(FlatpakTransaction) transaction = NULL;
+ g_autoptr(GPtrArray) refs = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autofree char *runtime = NULL;
+ g_autofree char *app = NULL;
+ FlatpakInstalledRef *unused_ref;
+ gboolean res;
+
+ runtime = g_strdup_printf ("runtime/org.test.Platform/%s/master",
+ flatpak_get_default_arch ());
+ app = g_strdup_printf ("app/org.test.Hello/%s/master",
+ flatpak_get_default_arch ());
+
+ system_inst = flatpak_installation_new_system (NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (system_inst);
+
+ user_inst = flatpak_installation_new_user (NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (user_inst);
+
+ empty_installation (system_inst);
+ empty_installation (user_inst);
+
+ add_remote_system ("test-runtime-only", NULL);
+
+ transaction = flatpak_transaction_new_for_installation (system_inst, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (transaction);
+
+ res = flatpak_transaction_add_install (transaction, "test-runtime-only-repo", runtime, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+
+ res = flatpak_transaction_run (transaction, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+ g_clear_object (&transaction);
+
+ /* Undo the pinning that happened as a side effect of the install */
+ const char *argv[] = { "flatpak", "pin", "--system", "--remove", runtime, NULL };
+ run_test_subprocess ((char **) argv, RUN_TEST_SUBPROCESS_DEFAULT);
+ flatpak_installation_drop_caches (system_inst, NULL, &error);
+ g_assert_no_error (error);
+
+ /* The runtime should show as unused */
+ refs = flatpak_installation_list_unused_refs (system_inst, NULL, NULL, &error);
+ g_assert_nonnull (refs);
+ g_assert_no_error (error);
+ g_assert_cmpint (refs->len, ==, 1);
+ unused_ref = g_ptr_array_index (refs, 0);
+ g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (unused_ref)), ==, "org.test.Platform");
+ g_clear_pointer (&refs, g_ptr_array_unref);
+
+ /* Install an app in the user installation that uses the runtime in the
+ * system installation */
+ transaction = flatpak_transaction_new_for_installation (user_inst, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (transaction);
+
+ flatpak_transaction_add_dependency_source (transaction, system_inst);
+
+ res = flatpak_transaction_add_install (transaction, repo_name, app, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+
+ res = flatpak_transaction_run (transaction, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+
+ /* Now the runtime should be used */
+ refs = flatpak_installation_list_unused_refs (system_inst, NULL, NULL, &error);
+ g_assert_nonnull (refs);
+ g_assert_no_error (error);
+ g_assert_cmpint (refs->len, ==, 0);
+}
+
int
main (int argc, char *argv[])
{
@@ -4382,6 +4464,7 @@ main (int argc, char *argv[])
g_test_add_func ("/library/installation-no-interaction", test_installation_no_interaction);
g_test_add_func ("/library/installation-unused-refs", test_installation_unused_refs);
g_test_add_func ("/library/installation-unused-refs-excludes-pins", test_installation_unused_refs_excludes_pins);
+ g_test_add_func ("/library/installation-unused-refs-across-installations", test_installation_unused_refs_across_installations);
global_setup ();