From 3f91b4d883264edb72bcb9e13dcfc85a77dff168 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Tue, 26 Sep 2017 09:45:57 +0800 Subject: [PATCH] system-helper: Add RemoveLocalRef helper function This depends on the modify-repo privilege Closes: #1034 Approved by: alexlarsson --- common/flatpak-dir.c | 177 ++++++++++++------ data/org.freedesktop.Flatpak.xml | 6 + system-helper/flatpak-system-helper.c | 54 ++++++ .../org.freedesktop.Flatpak.policy.in | 15 ++ .../org.freedesktop.Flatpak.rules.in | 3 +- 5 files changed, 200 insertions(+), 55 deletions(-) diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index ce064eea..8b64ac09 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -6974,10 +6974,36 @@ flatpak_dir_remove_ref (FlatpakDir *self, GCancellable *cancellable, GError **error) { - if (!ostree_repo_set_ref_immediate (self->repo, remote_name, ref, NULL, cancellable, error)) + if (flatpak_dir_use_system_helper (self, NULL)) + { + const char *installation = flatpak_dir_get_id (self); + FlatpakSystemHelper *system_helper = flatpak_dir_get_system_helper (self); + + /* If we don't have the system helper, we'll have to try and just remove + * the ref as an unprivileged user, which might fail later */ + if (system_helper) + { + if (!flatpak_system_helper_call_remove_local_ref_sync (system_helper, + remote_name, + ref, + installation ? installation : "", + cancellable, + error)) + return FALSE; + } + + return TRUE; + } + + if (!ostree_repo_set_ref_immediate (self->repo, + remote_name, + ref, + NULL, + cancellable, + error)) return FALSE; - return TRUE; + return flatpak_dir_prune (self, cancellable, error); } gboolean @@ -7033,58 +7059,6 @@ out: return ret; } -static GHashTable * -filter_out_deployed_refs (FlatpakDir *self, - GHashTable *local_refs) -{ - GHashTable *undeployed_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - g_autoptr(GVariant) deploy_data = NULL; - GHashTableIter hash_iter; - gpointer key; - - g_hash_table_iter_init (&hash_iter, local_refs); - while (g_hash_table_iter_next (&hash_iter, &key, NULL)) - { - g_autoptr(GVariant) deploy_data = flatpak_dir_get_deploy_data (self, key, NULL, NULL); - - if (!deploy_data) - g_hash_table_add (undeployed_refs, g_strdup (key)); - } - - return undeployed_refs; -} - -gboolean -flatpak_dir_cleanup_undeployed_refs (FlatpakDir *self, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GHashTable) local_refs = NULL; - g_autoptr(GHashTable) undeployed_refs = NULL; - GHashTableIter hash_iter; - gpointer key; - - if (!ostree_repo_list_refs (self->repo, NULL, &local_refs, cancellable, error)) - return FALSE; - - undeployed_refs = filter_out_deployed_refs (self, local_refs); - g_hash_table_iter_init (&hash_iter, undeployed_refs); - - while (g_hash_table_iter_next (&hash_iter, &key, NULL)) - { - g_autofree gchar *remote = NULL; - g_autofree gchar *ref = NULL; - - if (!ostree_parse_refspec (key, &remote, &ref, error)) - return FALSE; - - if (!flatpak_dir_remove_ref (self, remote, ref, cancellable, error)) - return FALSE; - } - - return TRUE; -} - gboolean flatpak_dir_prune (FlatpakDir *self, GCancellable *cancellable, @@ -8055,6 +8029,101 @@ flatpak_dir_find_installed_ref (FlatpakDir *self, return NULL; } +/* Given a list of refs in local_refspecs, remove any refs that have already + * been deployed and return a new GPtrArray containing only the undeployed + * refs. This is used by flatpak_dir_cleanup_undeployed_refs to determine + * which undeployed refs need to be removed from the local repository. + * + * Returns: (transfer-full): A #GPtrArray + */ +static GPtrArray * +filter_out_deployed_refs (FlatpakDir *self, + GPtrArray *local_refspecs, + GError **error) +{ + g_autoptr(GPtrArray) undeployed_refs = g_ptr_array_new_full (local_refspecs->len, g_free); + gsize i; + + for (i = 0; i < local_refspecs->len; ++i) + { + const gchar *refspec = g_ptr_array_index (local_refspecs, i); + g_autofree gchar *ref = NULL; + g_autoptr(GVariant) deploy_data = NULL; + + if (!ostree_parse_refspec (refspec, NULL, &ref, error)) + return FALSE; + + deploy_data = flatpak_dir_get_deploy_data (self, ref, NULL, NULL); + + if (!deploy_data) + g_ptr_array_add (undeployed_refs, g_strdup (refspec)); + } + + return g_steal_pointer (&undeployed_refs); +} + +/** + * flatpak_dir_cleanup_undeployed_refs: + * + * Find all flatpak refs in the local repository which have not been deloyed + * in the dir and remove them from the repository. You might want to call this + * function if you pulled refs into the dir but then decided that you did + * not want to deploy them for some reason. Note that this does not prune + * objects bound to the cleaned up refs from the underlying OSTree repository, + * you should consider using flatpak_dir_prune to do that. + * + * @self: a #FlatpakDir + * @cancellable: (allow-none): a #GCancellable + * @error: (allow-none): a #GError + * + * Since: 0.10.0 + * Returns: (transfer-full): %TRUE if cleaning up the refs suceeded, %FALSE + * otherwise + */ +gboolean +flatpak_dir_cleanup_undeployed_refs (FlatpakDir *self, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GHashTable) local_refspecs = NULL; + g_autoptr(GPtrArray) local_flatpak_refspecs = NULL; + g_autoptr(GPtrArray) undeployed_refs = NULL; + gsize i = 0; + + if (!ostree_repo_list_refs (self->repo, NULL, &local_refspecs, cancellable, error)) + return FALSE; + + local_flatpak_refspecs = find_matching_refs (local_refspecs, + NULL, NULL, NULL, + FLATPAK_KINDS_APP | + FLATPAK_KINDS_RUNTIME, + FIND_MATCHING_REFS_FLAGS_KEEP_REMOTE, + error); + + if (!local_flatpak_refspecs) + return FALSE; + + undeployed_refs = filter_out_deployed_refs (self, local_flatpak_refspecs, error); + + if (!undeployed_refs) + return FALSE; + + for (; i < undeployed_refs->len; ++i) + { + const char *refspec = g_ptr_array_index (undeployed_refs, i); + g_autofree gchar *remote = NULL; + g_autofree gchar *ref = NULL; + + if (!ostree_parse_refspec (refspec, &remote, &ref, error)) + return FALSE; + + if (!flatpak_dir_remove_ref (self, remote, ref, cancellable, error)) + return FALSE; + } + + return TRUE; +} + static FlatpakDir * flatpak_dir_new_full (GFile *path, gboolean user, DirExtraData *extra_data) { diff --git a/data/org.freedesktop.Flatpak.xml b/data/org.freedesktop.Flatpak.xml index 7136024c..267b02c9 100644 --- a/data/org.freedesktop.Flatpak.xml +++ b/data/org.freedesktop.Flatpak.xml @@ -111,6 +111,12 @@ + + + + + + diff --git a/system-helper/flatpak-system-helper.c b/system-helper/flatpak-system-helper.c index 1f0cad9a..40576fbf 100644 --- a/system-helper/flatpak-system-helper.c +++ b/system-helper/flatpak-system-helper.c @@ -790,6 +790,49 @@ handle_update_remote (FlatpakSystemHelper *object, return TRUE; } +static gboolean +handle_remove_local_ref (FlatpakSystemHelper *object, + GDBusMethodInvocation *invocation, + const gchar *arg_remote, + const gchar *arg_ref, + const gchar *arg_installation) +{ + g_autoptr(FlatpakDir) system = NULL; + g_autoptr(GError) error = NULL; + + g_debug ("RemoveLocalRef %s %s %s", arg_remote, arg_ref, arg_installation); + + system = dir_get_system (arg_installation, &error); + if (system == NULL) + { + g_dbus_method_invocation_return_gerror (invocation, error); + return TRUE; + } + + if (*arg_remote == 0 || strchr (arg_remote, '/') != NULL) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Invalid remote name: %s", arg_remote); + return TRUE; + } + + if (!flatpak_dir_ensure_repo (system, NULL, &error)) + { + g_dbus_method_invocation_return_gerror (invocation, error); + return TRUE; + } + + if (!flatpak_dir_remove_ref (system, arg_remote, arg_ref, NULL, &error)) + { + g_dbus_method_invocation_return_gerror (invocation, error); + return TRUE; + } + + flatpak_system_helper_complete_remove_local_ref (object, invocation); + + return TRUE; +} + static gboolean flatpak_authorize_method_handler (GDBusInterfaceSkeleton *interface, GDBusMethodInvocation *invocation, @@ -901,6 +944,16 @@ flatpak_authorize_method_handler (GDBusInterfaceSkeleton *interface, action = "org.freedesktop.Flatpak.update-remote"; + polkit_details_insert (details, "remote", remote); + } + else if (g_strcmp0 (method_name, "RemoveLocalRef") == 0) + { + const char *remote; + + g_variant_get_child (parameters, 0, "&s", &remote); + + action = "org.freedesktop.Flatpak.modify-repo"; + polkit_details_insert (details, "remote", remote); } @@ -958,6 +1011,7 @@ on_bus_acquired (GDBusConnection *connection, g_signal_connect (helper, "handle-install-bundle", G_CALLBACK (handle_install_bundle), NULL); g_signal_connect (helper, "handle-configure-remote", G_CALLBACK (handle_configure_remote), NULL); g_signal_connect (helper, "handle-update-remote", G_CALLBACK (handle_update_remote), NULL); + g_signal_connect (helper, "handle-remove-local-ref", G_CALLBACK (handle_remove_local_ref), NULL); g_signal_connect (helper, "g-authorize-method", G_CALLBACK (flatpak_authorize_method_handler), diff --git a/system-helper/org.freedesktop.Flatpak.policy.in b/system-helper/org.freedesktop.Flatpak.policy.in index 8c629c9f..daf8bdab 100644 --- a/system-helper/org.freedesktop.Flatpak.policy.in +++ b/system-helper/org.freedesktop.Flatpak.policy.in @@ -98,6 +98,21 @@ + + + Update system repository + Authentication is required to update the system repository + package-x-generic + + auth_admin + auth_admin + yes + + + Install bundle Authentication is required to install software diff --git a/system-helper/org.freedesktop.Flatpak.rules.in b/system-helper/org.freedesktop.Flatpak.rules.in index d2185238..c2ec6a96 100644 --- a/system-helper/org.freedesktop.Flatpak.rules.in +++ b/system-helper/org.freedesktop.Flatpak.rules.in @@ -2,7 +2,8 @@ polkit.addRule(function(action, subject) { if ((action.id == "org.freedesktop.Flatpak.app-install" || action.id == "org.freedesktop.Flatpak.runtime-install"|| action.id == "org.freedesktop.Flatpak.app-uninstall" || - action.id == "org.freedesktop.Flatpak.runtime-uninstall") && + action.id == "org.freedesktop.Flatpak.runtime-uninstall" || + action.id == "org.freedesktop.Flatpak.modify-repo") && subject.active == true && subject.local == true && subject.isInGroup("@privileged_group@")) { return polkit.Result.YES;