diff options
author | Phaedrus Leeds <matthew.leeds@endlessm.com> | 2020-08-07 18:49:32 -0700 |
---|---|---|
committer | Phaedrus Leeds <matthew.leeds@endlessm.com> | 2020-08-31 09:11:14 -0700 |
commit | e6228a6e4c1551e6345735d537858e2809b2b92e (patch) | |
tree | 2feba257d9b6b97cd0e37aaf671d3057a3f83ddf | |
parent | a70b7a76282ca4e6a8ef797c20cb78a75f09ed24 (diff) | |
download | flatpak-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.h | 1 | ||||
-rw-r--r-- | common/flatpak-dir.c | 10 | ||||
-rw-r--r-- | common/flatpak-installation.c | 236 | ||||
-rw-r--r-- | tests/testlibrary.c | 83 |
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 (); |